Source code for qermit.coherent_pauli_checks.monochromatic_convex_subdag
fromitertoolsimportcombinationsfromtypingimportAnyimportnetworkxasnx# type: ignoredef_subdag_nodes(subdag:int,node_subdag:dict[Any,int])->list[Any]:"""Get all nodes in a given sub-DAG. :param subdag: The sub-DAG whose nodes should be retrieved. :param node_subdag: Map from node to the sub-DAG to which the node belongs. :return: List of nodes belonging to given sub-DAG. """return[nodefornode,sinnode_subdag.items()ifs==subdag]def_subdag_predecessors(dag:nx.DiGraph,subdag:int,node_subdag:dict[Any,int])->list[Any]:"""Retrieve all nodes not in given sub-DAG with successors in sub-DAG. :param dag: Directed Acyclic Graph. :param subdag: Sub-DAG to retrieve predecessors of. :param node_subdag: Map from node to the sub-DAG to which it belongs. :return: Nodes with successors in given sub-DAG. """subdag_nodes=_subdag_nodes(subdag=subdag,node_subdag=node_subdag)returnsum([[predecessorforpredecessorindag.predecessors(node)# Exclude nodes in subdag.ifpredecessornotinsubdag_nodes]fornodeinsubdag_nodes],start=[],)def_subdag_successors(dag:nx.DiGraph,subdag:int,node_subdag:dict[Any,int])->list[Any]:"""Retrieve all nodes not in given sub-DAG with predecessors in sub-DAG. :param dag: Directed Acyclic Graph. :param subdag: Sub-DAG to retrieve successors of. :param node_subdag: Map from node to the sub-DAG to which it belongs. :return: Nodes with predecessors in given sub-DAG. """subdag_nodes=_subdag_nodes(subdag=subdag,node_subdag=node_subdag)returnsum([[successorforsuccessorindag.successors(node)# Exclude nodes in subdag.ifsuccessornotinsubdag_nodes]fornodeinsubdag_nodes],start=[],)
[docs]defget_monochromatic_convex_subdag(dag:nx.DiGraph,coloured_nodes:list[Any])->dict[Any,int]:"""Retrieve assignment of coloured nodes to sub-DAGs. The assignment aims to minimise the number of sub-DAGs. :param dag: Directed Acyclic Graph. :param coloured_nodes: The nodes which are coloured. :return: Map from node to the sub-DAG to which it belongs. """node_descendants={node:nx.descendants(dag,node)fornodeindag.nodes}fornodeinnode_descendants.keys():node_descendants[node].add(node)def_can_merge(dag:nx.DiGraph,subdag_one:int,subdag_two:int,node_subdag:dict[Any,int],)->bool:"""Determine if two sub-DAGs can be merged. This will be the case if there are no paths between predecessors of one sub-DAG to successors of the other. :param dag: Directed Acyclic Graph. :param subdag_one: First sub-DAG. :param subdag_two: Second sub-DAG. :param node_subdag: Map from node to the sub-DAG to which it belongs. :return: Boolean value indicating if the two sub-DAGs can be merged. """subdag_two_pred=_subdag_predecessors(dag=dag,subdag=subdag_two,node_subdag=node_subdag)forsubdag_one_succin_subdag_successors(dag=dag,subdag=subdag_one,node_subdag=node_subdag):ifany(descendantinsubdag_two_predfordescendantinnode_descendants[subdag_one_succ]):returnFalsesubgraph_one_pred=_subdag_predecessors(dag=dag,subdag=subdag_one,node_subdag=node_subdag)forsubdag_two_succin_subdag_successors(dag=dag,subdag=subdag_two,node_subdag=node_subdag):ifany(descendantinsubgraph_one_predfordescendantinnode_descendants[subdag_two_succ]):returnFalsereturnTruenode_subdag={node:ifori,nodeinenumerate(coloured_nodes)}subgraph_merged=Truewhilesubgraph_merged:subgraph_merged=False# Try to merge all pairs of sub-DAGsforsubdag_one,subdag_twoincombinations(set(node_subdag.values()),2):if_can_merge(dag=dag,subdag_one=subdag_one,subdag_two=subdag_two,node_subdag=node_subdag,):fornodein_subdag_nodes(subdag=subdag_two,node_subdag=node_subdag):node_subdag[node]=subdag_onesubgraph_merged=Truebreakreturnnode_subdag