Repository: Qiskit/qiskit

Issue:
BarrierBeforeFinalMeasurements prevents RemoveDiagonalGatesBeforeMeasure
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

### What is the current behavior?

The transpiler pass `BarrierBeforeFinalMeasurements` is currently run before `RemoveDiagonalGatesBeforeMeasure` in the `level3` preset pass manager. `RemoveDiagonalGatesBeforeMeasure` won't optimize across a `barrier` (even if it was inserted by another pass, and not by the user) and so final diagonal gates are not removed.

### Steps to reproduce the problem

```
>>> qc = qk.QuantumCircuit(5,5)
>>> qc.h(0)
>>> qc.cx(range(3), range(1,4))
>>> qc.z(range(4))
>>> qc.measure(range(4), range(4))
>>> print(qc)
        ┌───┐     ┌───┐     ┌─┐
q_0: |0>┤ H ├──■──┤ Z ├─────┤M├──────────────
        └───┘┌─┴─┐└───┘┌───┐└╥┘     ┌─┐
q_1: |0>─────┤ X ├──■──┤ Z ├─╫──────┤M├──────
             └───┘┌─┴─┐└───┘ ║ ┌───┐└╥┘┌─┐
q_2: |0>──────────┤ X ├──■───╫─┤ Z ├─╫─┤M├───
                  └───┘┌─┴─┐ ║ ├───┤ ║ └╥┘┌─┐
q_3: |0>───────────────┤ X ├─╫─┤ Z ├─╫──╫─┤M├
                       └───┘ ║ └───┘ ║  ║ └╥┘
q_4: |0>─────────────────────╫───────╫──╫──╫─
                             ║       ║  ║  ║
 c_0: 0 ═════════════════════╩═══════╬══╬══╬═
                                     ║  ║  ║
 c_1: 0 ═════════════════════════════╩══╬══╬═
                                        ║  ║
 c_2: 0 ════════════════════════════════╩══╬═
                                           ║
 c_3: 0 ═══════════════════════════════════╩═

 c_4: 0 ═════════════════════════════════════

>>> print(qk.transpile(qc, FakeOurense(), optimization_level=3))
                                ┌───┐     ┌────────┐                          ░ ┌─┐
(q2) q0|0>───────────────────■──┤ X ├──■──┤ U1(pi) ├──────────────────────────░─┤M├─────────
          ┌──────────┐     ┌─┴─┐└─┬─┘┌─┴─┐└─┬───┬──┘               ┌────────┐ ░ └╥┘   ┌─┐
(q0) q1|0>┤ U2(0,pi) ├──■──┤ X ├──■──┤ X ├──┤ X ├───────────────■──┤ U1(pi) ├─░──╫────┤M├───
          └──────────┘┌─┴─┐└───┘     └───┘  └─┬─┘   ┌────────┐  │  └────────┘ ░  ║ ┌─┐└╥┘
(q1) q2|0>────────────┤ X ├───────────────────■─────┤ U1(pi) ├──┼─────────────░──╫─┤M├─╫────
                      └───┘                         └────────┘┌─┴─┐┌────────┐ ░  ║ └╥┘ ║ ┌─┐
(q3) q3|0>────────────────────────────────────────────────────┤ X ├┤ U1(pi) ├─░──╫──╫──╫─┤M├
                                                              └───┘└────────┘ ░  ║  ║  ║ └╥┘
(q4) q4|0>───────────────────────────────────────────────────────────────────────╫──╫──╫──╫─
                                                                                 ║  ║  ║  ║
   c_0: 0 ═══════════════════════════════════════════════════════════════════════╩══╬══╬══╬═
                                                                                    ║  ║  ║
   c_1: 0 ══════════════════════════════════════════════════════════════════════════╩══╬══╬═
                                                                                       ║  ║
   c_2: 0 ═════════════════════════════════════════════════════════════════════════════╩══╬═
                                                                                          ║
   c_3: 0 ════════════════════════════════════════════════════════════════════════════════╩═

   c_4: 0 ══════════════════════════════════════════════════════════════════════════════════
```

### What is the expected behavior?



### Suggested solutions

Update the `level3` pass manager to run the optimization passes `RemoveResetInZeroState` and `RemoveDiagonalGatesBeforeMeasure` prior to the embedding stage.

This may also be a good time to:
- move `barrier_before_final_measurements.py` out of the `mapping/` directory (as it's not directly related to solving the mapping problem, it can be moved up a level to the `passes/` directory.)
- run `BarrierBeforeFinalMeasurements` independent of `property_set['is_swap_mapped']` in all the default pass managers.

Though each of these could just as well be individual issues/PRs.


Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py
--- a/qiskit/transpiler/preset_passmanagers/level3.py
+++ b/qiskit/transpiler/preset_passmanagers/level3.py
@@ -175,12 +175,12 @@ def _opt_control(property_set):
     # Build pass manager
     pm3 = PassManager()
     pm3.append(_unroll3q)
+    pm3.append(_reset + _meas)
     if coupling_map:
         pm3.append(_given_layout)
         pm3.append(_choose_layout_1, condition=_choose_layout_condition)
         pm3.append(_choose_layout_2, condition=_choose_layout_condition)
         pm3.append(_embed)
-        pm3.append(_reset + _meas)
         pm3.append(_swap_check)
         pm3.append(_swap, condition=_swap_condition)
     pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control)


Test patch:
diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py
--- a/test/python/transpiler/test_preset_passmanagers.py
+++ b/test/python/transpiler/test_preset_passmanagers.py
@@ -51,6 +51,22 @@ def test_no_coupling_map(self, level):
         result = transpile(circuit, basis_gates=['u1', 'u2', 'u3', 'cx'], optimization_level=level)
         self.assertIsInstance(result, QuantumCircuit)
 
+    def test_layout_3239(self, level=3):
+        """Test final layout after preset level3 passmanager does not include diagonal gates
+        See: https://github.com/Qiskit/qiskit-terra/issues/3239
+        """
+        qc = QuantumCircuit(5, 5)
+        qc.h(0)
+        qc.cx(range(3), range(1, 4))
+        qc.z(range(4))
+        qc.measure(range(4), range(4))
+        result = transpile(qc, basis_gates=['u1', 'u2', 'u3', 'cx'],
+                           layout_method='trivial', optimization_level=level)
+
+        dag = circuit_to_dag(result)
+        op_nodes = [node.name for node in dag.topological_op_nodes()]
+        self.assertNotIn('u1', op_nodes)  # Check if the diagonal Z-Gates (u1) were removed
+
     @combine(level=[0, 1, 2, 3], name='level{level}')
     def test_no_basis_gates(self, level):
         """Test that basis_gates can be None (level={level})"""