From d40a3997180e7309b13aa82a8ba2995f5a58923c Mon Sep 17 00:00:00 2001 From: Matthew Treinish <mtreinish@kortar.org> Date: Fri, 17 Sep 2021 15:40:26 -0400 Subject: [PATCH 1/5] Improve efficiency of RemoveFinalMeasurement transpiler pass The RemoveFinalMeasurement was determining which operations were at the end of a circuit quite inefficiently. It was searching over the entire DAG to find barrier and measurement instructions and then checking any that were found if they're children were output nodes. While instead it'd be much more efficient to chack the parent of each output node for whether it's a barrier or a measurement. Additionally, the output dag was being needless rebuilt by modifying the input and then looping over all nodes in the dag again and copy the node to a new dag. While instead we can just return the modified input graph. This commit makes those changes to improve the efficiency of the pass. Fixes #7038 --- .../passes/utils/remove_final_measurements.py | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/qiskit/transpiler/passes/utils/remove_final_measurements.py b/qiskit/transpiler/passes/utils/remove_final_measurements.py index 5bf156970203..35ad1eb6972f 100644 --- a/qiskit/transpiler/passes/utils/remove_final_measurements.py +++ b/qiskit/transpiler/passes/utils/remove_final_measurements.py @@ -34,25 +34,16 @@ def run(self, dag): Returns: DAGCircuit: the optimized DAG. """ - final_op_types = ["measure", "barrier"] + final_op_types = {"measure", "barrier"} final_ops = [] cregs_to_remove = dict() clbits_with_final_measures = set() clbit_registers = {clbit: creg for creg in dag.cregs.values() for clbit in creg} - for candidate_node in dag.named_nodes(*final_op_types): - is_final_op = True - - for _, child_successors in dag.bfs_successors(candidate_node): - if any( - isinstance(suc, DAGOpNode) and suc.name not in final_op_types - for suc in child_successors - ): - is_final_op = False - break - - if is_final_op: - final_ops.append(candidate_node) + for qubit in dag.qubits: + op_node = next(dag.predecessors(dag.output_map[qubit])) + if isinstance(op_node, DAGOpNode) and op_node.op.name in final_op_types: + final_ops.append(op_node) if not final_ops: return dag @@ -77,10 +68,4 @@ def run(self, dag): if val in cregs_to_remove and cregs_to_remove[val] == val.size: del dag.cregs[key] - new_dag = dag._copy_circuit_metadata() - - for node in dag.topological_op_nodes(): - # copy the condition over too - new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs) - - return new_dag + return dag From 0d7cad70eb9af866e1780e3d68b08cd5672ad43d Mon Sep 17 00:00:00 2001 From: Matthew Treinish <mtreinish@kortar.org> Date: Fri, 17 Sep 2021 16:03:15 -0400 Subject: [PATCH 2/5] Improve performance of idle_wires --- qiskit/dagcircuit/dagcircuit.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index c5a97c09113f..2717367eaeb3 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -790,13 +790,16 @@ def idle_wires(self, ignore=None): if ignore is None: ignore = [] for wire in self._wires: - nodes = [ - node - for node in self.nodes_on_wire(wire, only_ops=True) - if node.op.name not in ignore - ] - if len(nodes) == 0: - yield wire + if not ignore: + if isinstance(self.successors(self.input_map[wire]), DAGOutNode): + yield wire + else: + count = 0 + for node in self.nodes_on_wire(wire, only_ops=True): + if node.op.name not in ignore: + count += 1 + if count == 0: + yield wire def size(self): """Return the number of operations.""" From 9fe38da84581212d841ca4852b2b1ae83f127808 Mon Sep 17 00:00:00 2001 From: Matthew Treinish <mtreinish@kortar.org> Date: Fri, 17 Sep 2021 16:06:34 -0400 Subject: [PATCH 3/5] Update qiskit/dagcircuit/dagcircuit.py --- qiskit/dagcircuit/dagcircuit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 2717367eaeb3..96e27edaff87 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -798,6 +798,8 @@ def idle_wires(self, ignore=None): for node in self.nodes_on_wire(wire, only_ops=True): if node.op.name not in ignore: count += 1 + # If we found an op node outside of ignore we can stop iterating over the wire + break if count == 0: yield wire From 5f971c5a88744cf204f28fc78aeeceaf94ed6d0f Mon Sep 17 00:00:00 2001 From: Matthew Treinish <mtreinish@kortar.org> Date: Fri, 17 Sep 2021 16:16:44 -0400 Subject: [PATCH 4/5] Improve logic in idle_wires --- qiskit/dagcircuit/dagcircuit.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 96e27edaff87..7bc40f73af73 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -788,19 +788,18 @@ def idle_wires(self, ignore=None): Bit: Bit in idle wire. """ if ignore is None: - ignore = [] + ignore = set() + ignore_set = set(ignore) for wire in self._wires: if not ignore: if isinstance(self.successors(self.input_map[wire]), DAGOutNode): yield wire else: - count = 0 for node in self.nodes_on_wire(wire, only_ops=True): - if node.op.name not in ignore: - count += 1 + if node.op.name not in ignore_set: # If we found an op node outside of ignore we can stop iterating over the wire break - if count == 0: + else: yield wire def size(self): From 89b1acfb830c58c9d26d4d65b7c36dfe8ac9f51f Mon Sep 17 00:00:00 2001 From: Matthew Treinish <mtreinish@kortar.org> Date: Fri, 17 Sep 2021 16:36:33 -0400 Subject: [PATCH 5/5] Fix typo in idle_wires --- qiskit/dagcircuit/dagcircuit.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 7bc40f73af73..01c99a13906c 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -786,13 +786,22 @@ def idle_wires(self, ignore=None): Yields: Bit: Bit in idle wire. + + Raises: + DAGCircuitError: If the DAG is invalid """ if ignore is None: ignore = set() ignore_set = set(ignore) for wire in self._wires: if not ignore: - if isinstance(self.successors(self.input_map[wire]), DAGOutNode): + try: + child = next(self.successors(self.input_map[wire])) + except StopIteration as e: + raise DAGCircuitError( + "Invalid dagcircuit input node %s has no output" % self.input_map[wire] + ) from e + if isinstance(child, DAGOutNode): yield wire else: for node in self.nodes_on_wire(wire, only_ops=True):