Repository: Qiskit/qiskit

Issue:
optimize_1q_gates does not preserve circuit order
Following #2000, the `Optimize1qGates` pass appends the 1q gate to the end of the DAG, rather than inserting it in place. e.g.

```
>>> import qiskit as qk
>>> qr = qk.QuantumRegister(2)
>>> qc = qk.QuantumCircuit(qr)
>>> qc.u1(0.1, qr[0])
<qiskit.extensions.standard.u1.U1Gate object at 0x101c98128>
>>> qc.u1(0.2, qr[0])
<qiskit.extensions.standard.u1.U1Gate object at 0x101c985f8>
>>> qc.cx(qr[1],qr[0])
<qiskit.extensions.standard.cx.CnotGate object at 0x101c98208>
>>> print(qc.draw())
         ┌─────────┐┌─────────┐┌───┐
q0_0: |0>┤ U1(0.1) ├┤ U1(0.2) ├┤ X ├
         └─────────┘└─────────┘└─┬─┘
q0_1: |0>────────────────────────■──
>>> from qiskit.transpiler.passes import Optimize1qGates
>>> from qiskit.converters import circuit_to_dag, dag_to_circuit
>>> print(dag_to_circuit(Optimize1qGates().run(circuit_to_dag(qc))).draw())
         ┌───┐┌─────────┐
q0_0: |0>┤ X ├┤ U1(0.3) ├
         └─┬─┘└─────────┘
q0_1: |0>──■─────────────
```


Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/passes/optimize_1q_gates.py b/qiskit/transpiler/passes/optimize_1q_gates.py
--- a/qiskit/transpiler/passes/optimize_1q_gates.py
+++ b/qiskit/transpiler/passes/optimize_1q_gates.py
@@ -21,6 +21,8 @@
 from qiskit.transpiler.basepasses import TransformationPass
 from qiskit.quantum_info.operators.quaternion import quaternion_from_euler
 from qiskit.transpiler.passes.unroller import Unroller
+from qiskit.dagcircuit import DAGCircuit
+from qiskit.circuit import QuantumRegister
 
 _CHOP_THRESHOLD = 1e-15
 
@@ -36,8 +38,6 @@ def run(self, dag):
         """Return a new circuit that has been optimized."""
         runs = dag.collect_runs(["u1", "u2", "u3", "id"])
         for run in runs:
-            run_qarg = run[0].qargs[0]
-
             right_name = "u1"
             right_parameters = (0, 0, 0)  # (theta, phi, lambda)
 
@@ -45,7 +45,6 @@ def run(self, dag):
                 left_name = current_node.name
                 if (current_node.condition is not None
                         or len(current_node.qargs) != 1
-                        or current_node.qargs[0] != run_qarg
                         or left_name not in ["u1", "u2", "u3", "id"]):
                     raise MapperError("internal error")
                 if left_name == "u1":
@@ -162,7 +161,10 @@ def run(self, dag):
                 if right_name == "u1" and np.mod(right_parameters[2], (2 * np.pi)) == 0:
                     right_name = "nop"
 
-            # Replace the data of the first node in the run
+            # Replace the the first node in the run with a dummy DAG which contains a dummy
+            # qubit. The name is irrelevant, because substitute_node_with_dag will take care of
+            # putting it in the right place.
+            run_qarg = (QuantumRegister(1, 'q'), 0)
             new_op = Instruction("", [], [], [])
             if right_name == "u1":
                 new_op = U1Gate(right_parameters[2], run_qarg)
@@ -173,11 +175,16 @@ def run(self, dag):
                                 run_qarg)
 
             if right_name != 'nop':
-                dag.apply_operation_back(new_op)
+                new_dag = DAGCircuit()
+                new_dag.add_qreg(run_qarg[0])
+                new_dag.apply_operation_back(new_op)
+                dag.substitute_node_with_dag(run[0], new_dag)
 
             # Delete the other nodes in the run
-            for current_node in run:
+            for current_node in run[1:]:
                 dag._remove_op_node(current_node)
+            if right_name == "nop":
+                dag._remove_op_node(run[0])
 
         return dag
 


Test patch:
diff --git a/test/python/transpiler/test_optimize_1q_gates.py b/test/python/transpiler/test_optimize_1q_gates.py
--- a/test/python/transpiler/test_optimize_1q_gates.py
+++ b/test/python/transpiler/test_optimize_1q_gates.py
@@ -143,6 +143,28 @@ def test_ignores_conditional_rotations(self):
 
         self.assertEqual(circuit_to_dag(expected), after)
 
+    def test_in_the_back(self):
+        """Optimizations can be in the back of the circuit.
+        See https://github.com/Qiskit/qiskit-terra/issues/2004.
+
+        qr0:--[U1]-[U1]-[H]--    qr0:--[U1]-[H]--
+        """
+        qr = QuantumRegister(1, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.u1(0.3, qr)
+        circuit.u1(0.4, qr)
+        circuit.h(qr)
+        dag = circuit_to_dag(circuit)
+
+        expected = QuantumCircuit(qr,)
+        expected.u1(0.7, qr)
+        expected.h(qr)
+
+        pass_ = Optimize1qGates()
+        after = pass_.run(dag)
+
+        self.assertEqual(circuit_to_dag(expected), after)
+
 
 if __name__ == '__main__':
     unittest.main()