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.
My concern with that was it's still only going to work for a subset of basis out there. I don't think we can remove the basis translator and still handle any universal basis set. Like what if the basis is composed of completely custom gates?

I think we probably should do both approaches together, with https://github.com/ewinston/qiskit-terra/commit/90864c546454668b2575b7313c119c90813b20b9 commutative cancellation will use the basis if it knows about it, but if it doesn't it'll use a standard gate and rely on the basis translator to handle the conversion.
@mtreinish I think using both approaches sounds like a reasonable short term solution. If we don't guarantee transformation passes keep within a target basis then following up with translation would be necessary. Perhaps an intermediate solution (not in this issue) would for passes to indicate whether they respect target basis. Then applying the basis translator could be done only when needed.

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']
 
@@ -76,7 +105,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_name, 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_name, com_set_idx)].append(node)
                     if num_qargs == 1 and node.name in ['rx', 'x']:
                         cancellation_sets[('x_rotation', wire_name, com_set_idx)].append(node)
@@ -90,6 +119,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]
@@ -100,13 +131,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
@@ -117,19 +149,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
@@ -172,9 +172,10 @@ def _opt_control(property_set):
 
     if basis_gates and ('u1' in basis_gates or 'u2' in basis_gates or
                         'u3' in basis_gates):
-        _opt = [Optimize1qGates(basis_gates), CommutativeCancellation()]
+        _opt = [Optimize1qGates(basis_gates), CommutativeCancellation(basis_gates=basis_gates)]
     else:
-        _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)