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):