Repository: Qiskit/qiskit

Issue:
Left over U1 gates at optimization level 2
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: master
- **Python version**:
- **Operating system**:

### What is the current behavior?
```python

import numpy as np
from qiskit import *

qc = QuantumCircuit(3)
qc.h(0)
qc.h(1)
qc.cp(np.pi/8,0,1)

qc.cp(np.pi/4,0,2)
trans_qc = transpile(qc, basis_gates=['id', 'rz', 'sx', 'x', 'cx'], optimization_level=2)
trans_qc.draw()
```

gives

```
global phase: -26.4090132442392
     ┌─────────┐┌────┐┌────────────┐                                         »
q_0: ┤ RZ(π/2) ├┤ √X ├┤ U1(11π/16) ├──■──────────────────■────────────────■──»
     ├─────────┤├────┤└┬─────────┬─┘┌─┴─┐┌────────────┐┌─┴─┐┌──────────┐  │  »
q_1: ┤ RZ(π/2) ├┤ √X ├─┤ RZ(π/2) ├──┤ X ├┤ RZ(6.0868) ├┤ X ├┤ RZ(π/16) ├──┼──»
     └─────────┘└────┘ └─────────┘  └───┘└────────────┘└───┘└──────────┘┌─┴─┐»
q_2: ───────────────────────────────────────────────────────────────────┤ X ├»
                                                                        └───┘»
«                                  
«q_0: ───────────────■─────────────
«                    │             
«q_1: ───────────────┼─────────────
«     ┌───────────┐┌─┴─┐┌─────────┐
«q_2: ┤ RZ(15π/8) ├┤ X ├┤ RZ(π/8) ├
«     └───────────┘└───┘└─────────┘
```


### Steps to reproduce the problem



### What is the expected behavior?



### Suggested solutions





Discussion:
I looked into this when I saw #5665 filed earlier this morning:

https://github.com/Qiskit/qiskit-terra/issues/5665#issuecomment-764616777

> So I took a quick look to dig into this issue this morning. The issue is the `CommutativeCancellation` transpiler pass isn't basis gate aware and only knows how to use the U1 gates for z rotations: https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/transpiler/passes/optimization/commutative_cancellation.py
> 
> To fix this we probably either need to update the pass to basis aware, or update the default level2 pass manager to run basis translation in the fixed point loop after we run commutative cancellation.
@mtreinish I have a version which updates the pass to be basis aware. I thought this might avoid calling basis translation again.

Resolving patch:
diff --git a/qiskit/transpiler/passes/optimization/commutative_cancellation.py b/qiskit/transpiler/passes/optimization/commutative_cancellation.py
--- a/qiskit/transpiler/passes/optimization/commutative_cancellation.py
+++ b/qiskit/transpiler/passes/optimization/commutative_cancellation.py
@@ -22,6 +22,9 @@
 from qiskit.dagcircuit import DAGCircuit
 from qiskit.circuit.library.standard_gates.u1 import U1Gate
 from qiskit.circuit.library.standard_gates.rx import RXGate
+from qiskit.circuit.library.standard_gates.p import PhaseGate
+from qiskit.circuit.library.standard_gates.rz import RZGate
+
 
 _CUTOFF_PRECISION = 1E-5
 
@@ -35,8 +38,22 @@ class CommutativeCancellation(TransformationPass):
         H, X, Y, Z, CX, CY, CZ
     """
 
-    def __init__(self):
+    def __init__(self, basis_gates=None):
+        """
+        CommutativeCancellation initializer.
+
+        Args:
+            basis_gates (list[str]): Basis gates to consider, e.g. `['u3', 'cx']`. For the effects
+                of this pass, the basis is the set intersection between the `basis` parameter
+                and the gates in the dag.
+        """
         super().__init__()
+        if basis_gates:
+            self.basis = set(basis_gates)
+        else:
+            self.basis = set()
+
+        self._var_z_map = {'rz': RZGate, 'p': PhaseGate, 'u1': U1Gate}
         self.requires.append(CommutationAnalysis())
 
     def run(self, dag):
@@ -51,6 +68,18 @@ def run(self, dag):
         Raises:
             TranspilerError: when the 1-qubit rotation gates are not found
         """
+        var_z_gate = None
+        z_var_gates = [gate for gate in dag.count_ops().keys()
+                       if gate in self._var_z_map]
+        if z_var_gates:
+            # priortize z gates in circuit
+            var_z_gate = self._var_z_map[next(iter(z_var_gates))]
+        else:
+            z_var_gates = [gate for gate in self.basis
+                           if gate in self._var_z_map]
+            if z_var_gates:
+                var_z_gate = self._var_z_map[next(iter(z_var_gates))]
+
         # Now the gates supported are hard-coded
         q_gate_list = ['cx', 'cy', 'cz', 'h', 'y']
 
@@ -75,7 +104,7 @@ def run(self, dag):
                     num_qargs = len(node.qargs)
                     if num_qargs == 1 and node.name in q_gate_list:
                         cancellation_sets[(node.name, wire, com_set_idx)].append(node)
-                    if num_qargs == 1 and node.name in ['z', 'u1', 'rz', 't', 's']:
+                    if num_qargs == 1 and node.name in ['p', 'z', 'u1', 'rz', 't', 's']:
                         cancellation_sets[('z_rotation', wire, com_set_idx)].append(node)
                     if num_qargs == 1 and node.name in ['rx', 'x']:
                         cancellation_sets[('x_rotation', wire, com_set_idx)].append(node)
@@ -88,6 +117,8 @@ def run(self, dag):
                         cancellation_sets[q2_key].append(node)
 
         for cancel_set_key in cancellation_sets:
+            if cancel_set_key[0] == 'z_rotation' and var_z_gate is None:
+                continue
             set_len = len(cancellation_sets[cancel_set_key])
             if set_len > 1 and cancel_set_key[0] in q_gate_list:
                 gates_to_cancel = cancellation_sets[cancel_set_key]
@@ -98,13 +129,14 @@ def run(self, dag):
                 run = cancellation_sets[cancel_set_key]
                 run_qarg = run[0].qargs[0]
                 total_angle = 0.0  # lambda
+                total_phase = 0.0
                 for current_node in run:
                     if (current_node.condition is not None
                             or len(current_node.qargs) != 1
                             or current_node.qargs[0] != run_qarg):
                         raise TranspilerError("internal error")
 
-                    if current_node.name in ['u1', 'rz', 'rx']:
+                    if current_node.name in ['p', 'u1', 'rz', 'rx']:
                         current_angle = float(current_node.op.params[0])
                     elif current_node.name in ['z', 'x']:
                         current_angle = np.pi
@@ -115,19 +147,26 @@ def run(self, dag):
 
                     # Compose gates
                     total_angle = current_angle + total_angle
+                    if current_node.op.definition:
+                        total_phase += current_node.op.definition.global_phase
 
                 # Replace the data of the first node in the run
                 if cancel_set_key[0] == 'z_rotation':
-                    new_op = U1Gate(total_angle)
+                    new_op = var_z_gate(total_angle)
                 elif cancel_set_key[0] == 'x_rotation':
                     new_op = RXGate(total_angle)
 
+                new_op_phase = 0
                 if np.mod(total_angle, (2 * np.pi)) > _CUTOFF_PRECISION:
                     new_qarg = QuantumRegister(1, 'q')
                     new_dag = DAGCircuit()
                     new_dag.add_qreg(new_qarg)
                     new_dag.apply_operation_back(new_op, [new_qarg[0]])
                     dag.substitute_node_with_dag(run[0], new_dag)
+                    if new_op.definition:
+                        new_op_phase = new_op.definition.global_phase
+
+                dag.global_phase = total_phase - new_op_phase
 
                 # Delete the other nodes in the run
                 for current_node in run[1:]:
diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py
--- a/qiskit/transpiler/preset_passmanagers/level2.py
+++ b/qiskit/transpiler/preset_passmanagers/level2.py
@@ -174,7 +174,8 @@ def _direction_condition(property_set):
     def _opt_control(property_set):
         return not property_set['depth_fixed_point']
 
-    _opt = [Optimize1qGatesDecomposition(basis_gates), CommutativeCancellation()]
+    _opt = [Optimize1qGatesDecomposition(basis_gates),
+            CommutativeCancellation(basis_gates=basis_gates)]
 
     # 9. Schedule the circuit only when scheduling_method is supplied
     if scheduling_method:


Test patch:
diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py
--- a/test/python/transpiler/test_commutative_cancellation.py
+++ b/test/python/transpiler/test_commutative_cancellation.py
@@ -17,9 +17,10 @@
 from qiskit.test import QiskitTestCase
 
 from qiskit import QuantumRegister, QuantumCircuit
-from qiskit.circuit.library import U1Gate
+from qiskit.circuit.library import U1Gate, RZGate
 from qiskit.transpiler import PassManager, PropertySet
 from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation, FixedPoint, Size
+from qiskit.quantum_info import Operator
 
 
 class TestCommutativeCancellation(QiskitTestCase):
@@ -71,7 +72,7 @@ def test_all_gates(self):
         new_circuit = passmanager.run(circuit)
 
         expected = QuantumCircuit(qr)
-        expected.append(U1Gate(2.0), [qr[0]])
+        expected.append(RZGate(2.0), [qr[0]])
         expected.rx(1.0, qr[0])
 
         self.assertEqual(expected, new_circuit)
@@ -368,9 +369,9 @@ def test_commutative_circuit2(self):
         passmanager.append(CommutativeCancellation())
         new_circuit = passmanager.run(circuit)
         expected = QuantumCircuit(qr)
-        expected.append(U1Gate(np.pi * 17 / 12), [qr[2]])
+        expected.append(RZGate(np.pi * 17 / 12), [qr[2]])
         expected.cx(qr[2], qr[1])
-
+        expected.global_phase = (np.pi * 17 / 12 - (2 * np.pi / 3)) / 2
         self.assertEqual(expected, new_circuit)
 
     def test_commutative_circuit3(self):
@@ -412,8 +413,8 @@ def test_commutative_circuit3(self):
                            do_while=lambda property_set: not property_set['size_fixed_point'])
         new_circuit = passmanager.run(circuit)
         expected = QuantumCircuit(qr)
-        expected.append(U1Gate(np.pi * 17 / 12), [qr[2]])
-        expected.append(U1Gate(np.pi * 2 / 3), [qr[3]])
+        expected.append(RZGate(np.pi * 17 / 12), [qr[2]])
+        expected.append(RZGate(np.pi * 2 / 3), [qr[3]])
         expected.cx(qr[2], qr[1])
 
         self.assertEqual(expected, new_circuit)
@@ -524,6 +525,86 @@ def test_conditional_gates_dont_commute(self):
 
         self.assertEqual(circuit, new_circuit)
 
+    def test_basis_01(self):
+        """Test basis priority change, phase gate"""
+        circuit = QuantumCircuit(1)
+        circuit.s(0)
+        circuit.z(0)
+        circuit.t(0)
+        circuit.rz(np.pi, 0)
+        passmanager = PassManager()
+        passmanager.append(CommutativeCancellation(basis_gates=['cx', 'p', 'sx']))
+        new_circuit = passmanager.run(circuit)
+        expected = QuantumCircuit(1)
+        expected.rz(11 * np.pi / 4, 0)
+        expected.global_phase = 11 * np.pi / 4 / 2 - np.pi / 2
+
+        self.assertEqual(new_circuit, expected)
+
+    def test_basis_02(self):
+        """Test basis priority change, Rz gate"""
+        circuit = QuantumCircuit(1)
+        circuit.s(0)
+        circuit.z(0)
+        circuit.t(0)
+        passmanager = PassManager()
+        passmanager.append(CommutativeCancellation(basis_gates=['cx', 'rz', 'sx']))
+        new_circuit = passmanager.run(circuit)
+
+        expected = QuantumCircuit(1)
+        expected.rz(7 * np.pi / 4, 0)
+        expected.global_phase = 7 * np.pi / 4 / 2
+        self.assertEqual(new_circuit, expected)
+
+    def test_basis_03(self):
+        """Test no specified basis"""
+        circuit = QuantumCircuit(1)
+        circuit.s(0)
+        circuit.z(0)
+        circuit.t(0)
+        passmanager = PassManager()
+        passmanager.append(CommutativeCancellation())
+        new_circuit = passmanager.run(circuit)
+
+        expected = QuantumCircuit(1)
+        expected.s(0)
+        expected.z(0)
+        expected.t(0)
+        self.assertEqual(new_circuit, expected)
+
+    def test_basis_global_phase_01(self):
+        """Test no specified basis, rz"""
+        circ = QuantumCircuit(1)
+        circ.rz(np.pi/2, 0)
+        circ.p(np.pi/2, 0)
+        circ.p(np.pi/2, 0)
+        passmanager = PassManager()
+        passmanager.append(CommutativeCancellation())
+        ccirc = passmanager.run(circ)
+        self.assertEqual(Operator(circ), Operator(ccirc))
+
+    def test_basis_global_phase_02(self):
+        """Test no specified basis, p"""
+        circ = QuantumCircuit(1)
+        circ.p(np.pi/2, 0)
+        circ.rz(np.pi/2, 0)
+        circ.p(np.pi/2, 0)
+        passmanager = PassManager()
+        passmanager.append(CommutativeCancellation())
+        ccirc = passmanager.run(circ)
+        self.assertEqual(Operator(circ), Operator(ccirc))
+
+    def test_basis_global_phase_03(self):
+        """Test global phase preservation if cummulative z-rotation is 0"""
+        circ = QuantumCircuit(1)
+        circ.rz(np.pi/2, 0)
+        circ.p(np.pi/2, 0)
+        circ.z(0)
+        passmanager = PassManager()
+        passmanager.append(CommutativeCancellation())
+        ccirc = passmanager.run(circ)
+        self.assertEqual(Operator(circ), Operator(ccirc))
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/python/transpiler/test_passmanager.py b/test/python/transpiler/test_passmanager.py
--- a/test/python/transpiler/test_passmanager.py
+++ b/test/python/transpiler/test_passmanager.py
@@ -97,7 +97,7 @@ def callback(**kwargs):
             calls.append(out_dict)
 
         passmanager = PassManager()
-        passmanager.append(CommutativeCancellation())
+        passmanager.append(CommutativeCancellation(basis_gates=['u1', 'u2', 'u3', 'cx']))
         passmanager.run(circuit, callback=callback)
         self.assertEqual(len(calls), 2)
         self.assertEqual(len(calls[0]), 5)