Repository: Qiskit/qiskit-terra (python)

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 
```

Pull request:
OptimizeSwapBeforeMeasure in one run
<!--
⚠️ If you do not respect this template, your pull request will be closed.
⚠️ Your pull request title should be short detailed and understandable for all.
⚠️ Also, please add a release note file using reno if the change needs to be
  documented in the release notes.
⚠️ If your pull request fixes an open issue, please link to the issue.

- [ ] I have added the tests to cover my changes.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the CONTRIBUTING document.
-->
### Summary
It's the generalization of this pass for multiple Swap gates, fixes #5825  

### Details and Comments
Not sure if it's the right implementation but all tests passed




Patch:
diff --git a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py
index a06dc78ba..40a480a3a 100644
--- a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py
+++ b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py
@@ -36,7 +36,7 @@ class OptimizeSwapBeforeMeasure(TransformationPass):
             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
index 6adc0f018..6f15e9ef9 100644
--- a/test/python/transpiler/test_optimize_swap_before_measure.py
+++ b/test/python/transpiler/test_optimize_swap_before_measure.py
@@ -73,6 +73,72 @@ class TestOptimizeSwapBeforeMeasure(QiskitTestCase):
 
         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--