Repository: Qiskit/qiskit

Issue:
OptimizeSwapBeforeMeasure needs multiple invocations
The `OptimizeSwapBeforeMeasure` pass is supposed to commute quantum SWAP gates that immediately precede measurements, past the measurements, and instead convert them to classical swap (i.e.just retarget the measurement clbit).

However this should be generalized to work with any permutation that happens before the measurements, not just one swap layer. I made this work by invoking it many times for the example below, but really one invocation should be smart enough to remove all the swaps (that whole swap network should be modeled as a permutation).

```py
from qiskit import QuantumCircuit
from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure, Depth, FixedPoint
from qiskit.transpiler import PassManager

circ = QuantumCircuit(8, 8)
circ.swap(3, 4)
circ.swap(6, 7)
circ.swap(0, 1)
circ.swap(6, 7)
circ.swap(4, 5)
circ.swap(0, 1)
circ.swap(5, 6)
circ.swap(3, 4)
circ.swap(1, 2)
circ.swap(6, 7)
circ.swap(4, 5)
circ.swap(2, 3)
circ.swap(0, 1)
circ.swap(5, 6)
circ.swap(1, 2)
circ.swap(6, 7)
circ.swap(4, 5)
circ.swap(2, 3)
circ.swap(3, 4)
circ.swap(3, 4)
circ.swap(5, 6)
circ.swap(1, 2)
circ.swap(4, 5)
circ.swap(2, 3)
circ.swap(5, 6)
circ.measure(range(8), range(8))
print(circ)

_depth_check = [Depth(), FixedPoint('depth')]
_opt = [OptimizeSwapBeforeMeasure()]
def _opt_control(property_set):
    return not property_set['depth_fixed_point']

pm = PassManager()
pm.append(_depth_check + _opt, do_while=_opt_control)  # should not need this do_while
print(pm.run(circ))
```

```
                    ┌─┐                                 
q_0: ─X──X─────X────┤M├─────────────────────────────────
      │  │     │    └╥┘         ┌─┐                     
q_1: ─X──X──X──X──X──╫─────X────┤M├─────────────────────
            │     │  ║     │    └╥┘      ┌─┐            
q_2: ───────X──X──X──╫──X──X─────╫──X────┤M├────────────
               │     ║  │        ║  │    └╥┘┌─┐         
q_3: ─X─────X──X─────╫──X──X──X──╫──X─────╫─┤M├─────────
      │     │        ║     │  │  ║        ║ └╥┘┌─┐      
q_4: ─X──X──X──X─────╫──X──X──X──╫──X─────╫──╫─┤M├──────
         │     │     ║  │        ║  │     ║  ║ └╥┘┌─┐   
q_5: ────X──X──X──X──╫──X──X─────╫──X──X──╫──╫──╫─┤M├───
            │     │  ║     │     ║     │  ║  ║  ║ └╥┘┌─┐
q_6: ─X──X──X──X──X──╫──X──X─────╫─────X──╫──╫──╫──╫─┤M├
      │  │     │     ║  │ ┌─┐    ║        ║  ║  ║  ║ └╥┘
q_7: ─X──X─────X─────╫──X─┤M├────╫────────╫──╫──╫──╫──╫─
                     ║    └╥┘    ║        ║  ║  ║  ║  ║ 
c: 8/════════════════╩═════╩═════╩════════╩══╩══╩══╩══╩═
                     0     7     1        2  3  4  5  6 
           ┌─┐               
q_0: ──────┤M├───────────────
        ┌─┐└╥┘               
q_1: ───┤M├─╫────────────────
     ┌─┐└╥┘ ║                
q_2: ┤M├─╫──╫────────────────
     └╥┘ ║  ║    ┌─┐         
q_3: ─╫──╫──╫────┤M├─────────
      ║  ║  ║    └╥┘      ┌─┐
q_4: ─╫──╫──╫─────╫───────┤M├
      ║  ║  ║ ┌─┐ ║       └╥┘
q_5: ─╫──╫──╫─┤M├─╫────────╫─
      ║  ║  ║ └╥┘ ║ ┌─┐    ║ 
q_6: ─╫──╫──╫──╫──╫─┤M├────╫─
      ║  ║  ║  ║  ║ └╥┘┌─┐ ║ 
q_7: ─╫──╫──╫──╫──╫──╫─┤M├─╫─
      ║  ║  ║  ║  ║  ║ └╥┘ ║ 
c: 8/═╩══╩══╩══╩══╩══╩══╩══╩═
      0  1  2  3  4  5  6  7 
```


Discussion:
Hi, i can take this one, i think putting a loop on top of this pass is enough.
> i think putting a loop on top of this pass is enough.

I think the "permutation model" that @ajavadia is suggesting would allow to run this in a single pass over the circuit. You need to bookkeep the permutations that each swap is inserted and, at the end, move the measurement. 
@1ucian0 i meant, i want to add a loop with a number of iterations equal to dag.depth() directly in the OptimizeSwapBeforeMeasure code but reversing the dag would also work.


Resolving patch:
diff --git a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py
--- a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py
+++ b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py
@@ -36,7 +36,7 @@ def run(self, dag):
             DAGCircuit: the optimized DAG.
         """
         swaps = dag.op_nodes(SwapGate)
-        for swap in swaps:
+        for swap in swaps[::-1]:
             final_successor = []
             for successor in dag.successors(swap):
                 final_successor.append(successor.type == 'out' or (successor.type == 'op' and


Test patch:
diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py
--- a/test/python/transpiler/test_optimize_swap_before_measure.py
+++ b/test/python/transpiler/test_optimize_swap_before_measure.py
@@ -73,6 +73,72 @@ def test_optimize_1swap_2measure(self):
 
         self.assertEqual(circuit_to_dag(expected), after)
 
+    def test_optimize_nswap_nmeasure(self):
+        """ Remove severals swap affecting multiple measurements
+                            ┌─┐                                                   ┌─┐
+        q_0: ─X──X─────X────┤M├─────────────────────────────────       q_0: ──────┤M├───────────────
+              │  │     │    └╥┘         ┌─┐                                    ┌─┐└╥┘
+        q_1: ─X──X──X──X──X──╫─────X────┤M├─────────────────────       q_1: ───┤M├─╫────────────────
+                    │     │  ║     │    └╥┘      ┌─┐                        ┌─┐└╥┘ ║
+        q_2: ───────X──X──X──╫──X──X─────╫──X────┤M├────────────       q_2: ┤M├─╫──╫────────────────
+                       │     ║  │        ║  │    └╥┘┌─┐                     └╥┘ ║  ║    ┌─┐
+        q_3: ─X─────X──X─────╫──X──X──X──╫──X─────╫─┤M├─────────       q_3: ─╫──╫──╫────┤M├─────────
+              │     │        ║     │  │  ║        ║ └╥┘┌─┐                   ║  ║  ║    └╥┘      ┌─┐
+        q_4: ─X──X──X──X─────╫──X──X──X──╫──X─────╫──╫─┤M├──────  ==>  q_4: ─╫──╫──╫─────╫───────┤M├
+                 │     │     ║  │        ║  │     ║  ║ └╥┘┌─┐                ║  ║  ║ ┌─┐ ║       └╥┘
+        q_5: ────X──X──X──X──╫──X──X─────╫──X──X──╫──╫──╫─┤M├───       q_5: ─╫──╫──╫─┤M├─╫────────╫─
+                    │     │  ║     │     ║     │  ║  ║  ║ └╥┘┌─┐             ║  ║  ║ └╥┘ ║ ┌─┐    ║
+        q_6: ─X──X──X──X──X──╫──X──X─────╫─────X──╫──╫──╫──╫─┤M├       q_6: ─╫──╫──╫──╫──╫─┤M├────╫─
+              │  │     │     ║  │ ┌─┐    ║        ║  ║  ║  ║ └╥┘             ║  ║  ║  ║  ║ └╥┘┌─┐ ║
+        q_7: ─X──X─────X─────╫──X─┤M├────╫────────╫──╫──╫──╫──╫─       q_7: ─╫──╫──╫──╫──╫──╫─┤M├─╫─
+                             ║    └╥┘    ║        ║  ║  ║  ║  ║              ║  ║  ║  ║  ║  ║ └╥┘ ║
+        c: 8/════════════════╩═════╩═════╩════════╩══╩══╩══╩══╩═       c: 8/═╩══╩══╩══╩══╩══╩══╩══╩═
+                             0     7     1        2  3  4  5  6              0  1  2  3  4  5  6  7
+        """
+        circuit = QuantumCircuit(8, 8)
+        circuit.swap(3, 4)
+        circuit.swap(6, 7)
+        circuit.swap(0, 1)
+        circuit.swap(6, 7)
+        circuit.swap(4, 5)
+        circuit.swap(0, 1)
+        circuit.swap(5, 6)
+        circuit.swap(3, 4)
+        circuit.swap(1, 2)
+        circuit.swap(6, 7)
+        circuit.swap(4, 5)
+        circuit.swap(2, 3)
+        circuit.swap(0, 1)
+        circuit.swap(5, 6)
+        circuit.swap(1, 2)
+        circuit.swap(6, 7)
+        circuit.swap(4, 5)
+        circuit.swap(2, 3)
+        circuit.swap(3, 4)
+        circuit.swap(3, 4)
+        circuit.swap(5, 6)
+        circuit.swap(1, 2)
+        circuit.swap(4, 5)
+        circuit.swap(2, 3)
+        circuit.swap(5, 6)
+        circuit.measure(range(8), range(8))
+        dag = circuit_to_dag(circuit)
+
+        expected = QuantumCircuit(8, 8)
+        expected.measure(0, 2)
+        expected.measure(1, 1)
+        expected.measure(2, 0)
+        expected.measure(3, 4)
+        expected.measure(4, 7)
+        expected.measure(5, 3)
+        expected.measure(6, 5)
+        expected.measure(7, 6)
+
+        pass_ = OptimizeSwapBeforeMeasure()
+        after = pass_.run(dag)
+
+        self.assertEqual(circuit_to_dag(expected), after)
+
     def test_cannot_optimize(self):
         """ Cannot optimize when swap is not at the end in all of the successors
             qr0:--X-----m--