Repository: Qiskit/qiskit

Issue:
transpiler error
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

The transpiler is not behaving correctly with optimization_level=2 for some inputs.

- **Qiskit Terra version**:0.8.0
- **Python version**:3.7.3
- **Operating system**:MacOsX

### What is the current behavior?
Running the transpiler on the circuit  Cnot - XI -Cnot returns XI when the optimization level is set to 2.

### Steps to reproduce the problem
from qiskit.compiler import transpile
from qiskit import (QuantumCircuit, ClassicalRegister, QuantumRegister, execute)
q = QuantumRegister(2, name='qn')
c = ClassicalRegister(2, name='cn')

test = QuantumCircuit(q,c)
test.barrier()
test.cx(q[0],q[1])
test.x(q[0])
test.cx(q[0],q[1])
test.barrier()
print("Initial Circuit")
print(test.draw())
test2 = transpile(test,optimization_level=2)
print("Transpiled Circuit")
print(test2.draw())

### What is the expected behavior?
Expected behavior is probably to do nothing, which is the case in the circuit Cnot - IZ - Cnot. 

### Suggested solutions
 It looks like the transpiler is smart enough to reduce the equivalent ZI and IX circuits and somehow is mistaking XI for IX.  Either that or it is not respecting the barriers in this one specific case.





Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/passes/commutation_analysis.py b/qiskit/transpiler/passes/commutation_analysis.py
--- a/qiskit/transpiler/passes/commutation_analysis.py
+++ b/qiskit/transpiler/passes/commutation_analysis.py
@@ -19,9 +19,6 @@
 the commutation relations on a given wire, all the gates on a wire
 are grouped into a set of gates that commute.
 
-This pass also provides useful methods to determine if two gates
-can commute in the circuit.
-
 TODO: the current pass determines commutativity through matrix multiplication.
 A rule-based analysis would be potentially faster, but more limited.
 """
@@ -29,8 +26,8 @@
 from collections import defaultdict
 import numpy as np
 from qiskit.transpiler.exceptions import TranspilerError
-
 from qiskit.transpiler.basepasses import AnalysisPass
+from qiskit.quantum_info.operators import Operator
 
 _CUTOFF_PRECISION = 1E-10
 
@@ -51,6 +48,12 @@ def run(self, dag):
         self.property_set['commutation_set'] = defaultdict(list)
 
         # Build a dictionary to keep track of the gates on each qubit
+        # The key with format (wire_name) will store the lists of commutation sets
+        # The key with format (node, wire_name) will store the index of the commutation set
+        # on the wire with wire_name, thus, for example:
+        # self.property_set['commutation_set'][wire_name][(node, wire_name)] will give the
+        # commutation set that contains node.
+
         for wire in dag.wires:
             wire_name = "{0}[{1}]".format(str(wire.register.name), str(wire.index))
             self.property_set['commutation_set'][wire_name] = []
@@ -58,9 +61,11 @@ def run(self, dag):
         # Add edges to the dictionary for each qubit
         for node in dag.topological_op_nodes():
             for (_, _, edge_data) in dag.edges(node):
+
                 edge_name = edge_data['name']
                 self.property_set['commutation_set'][(node, edge_name)] = -1
 
+        # Construct the commutation set
         for wire in dag.wires:
             wire_name = "{0}[{1}]".format(str(wire.register.name), str(wire.index))
 
@@ -87,156 +92,26 @@ def run(self, dag):
                 self.property_set['commutation_set'][(current_gate, wire_name)] = temp_len - 1
 
 
-def _gate_master_def(name, params=None):
-    # pylint: disable=too-many-return-statements
-
-    if name == 'h':
-        return 1. / np.sqrt(2) * np.array([[1.0, 1.0],
-                                           [1.0, -1.0]], dtype=np.complex)
-    if name == 'x':
-        return np.array([[0.0, 1.0],
-                         [1.0, 0.0]], dtype=np.complex)
-    if name == 'y':
-        return np.array([[0.0, -1.0j],
-                         [1.0j, 0.0]], dtype=np.complex)
-    if name == 'cx':
-        return np.array([[1.0, 0.0, 0.0, 0.0],
-                         [0.0, 1.0, 0.0, 0.0],
-                         [0.0, 0.0, 0.0, 1.0],
-                         [0.0, 0.0, 1.0, 0.0]], dtype=np.complex)
-    if name == 'cz':
-        return np.array([[1.0, 0.0, 0.0, 0.0],
-                         [0.0, 1.0, 0.0, 0.0],
-                         [0.0, 0.0, 1.0, 0.0],
-                         [0.0, 0.0, 0.0, -1.0]], dtype=np.complex)
-    if name == 'cy':
-        return np.array([[1.0, 0.0, 0.0, 0.0],
-                         [0.0, 1.0, 0.0, 0.0],
-                         [0.0, 0.0, 0.0, 1.0j],
-                         [0.0, 0.0, -1.0j, 0.0]], dtype=np.complex)
-    if name == 'z':
-        return np.array([[1.0, 0.0],
-                         [0.0, -1.0]], dtype=np.complex)
-    if name == 't':
-        return np.array([[1.0, 0.0],
-                         [0.0, np.exp(1j * np.pi / 4.0)]], dtype=np.complex)
-    if name == 's':
-        return np.array([[1.0, 0.0],
-                         [0.0, np.exp(1j * np.pi / 2.0)]], dtype=np.complex)
-    if name == 'sdag':
-        return np.array([[1.0, 0.0],
-                         [0.0, -np.exp(1j * np.pi / 2.0)]], dtype=np.complex)
-    if name == 'tdag':
-        return np.array([[1.0, 0.0],
-                         [0.0, -np.exp(1j * np.pi / 4.0)]], dtype=np.complex)
-    if name in ('rz', 'u1'):
-        return np.array([[np.exp(-1j * float(params[0]) / 2), 0],
-                         [0, np.exp(1j * float(params[0]) / 2)]], dtype=np.complex)
-    if name == 'rx':
-        return np.array([[np.cos(float(params[0]) / 2), -1j * np.sin(float(params[0]) / 2)],
-                         [-1j * np.sin(float(params[0]) / 2), np.cos(float(params[0]) / 2)]],
-                        dtype=np.complex)
-    if name == 'ry':
-        return np.array([[np.cos(float(params[0]) / 2), - np.sin(float(params[0]) / 2)],
-                         [np.sin(float(params[0]) / 2), np.cos(float(params[0]) / 2)]],
-                        dtype=np.complex)
-    if name == 'u2':
-        return 1. / np.sqrt(2) * np.array(
-            [[1, -np.exp(1j * float(params[1]))],
-             [np.exp(1j * float(params[0])), np.exp(1j * (float(params[0]) + float(params[1])))]],
-            dtype=np.complex)
-
-    if name == 'u3':
-        return 1. / np.sqrt(2) * np.array(
-            [[np.cos(float(params[0]) / 2.),
-              -np.exp(1j * float(params[2])) * np.sin(float(params[0]) / 2.)],
-             [np.exp(1j * float(params[1])) * np.sin(float(params[0]) / 2.),
-              np.cos(float(params[0]) / 2.) * np.exp(1j * (float(params[2]) + float(params[1])))]],
-            dtype=np.complex)
-
-    if name == 'P0':
-        return np.array([[1.0, 0.0], [0.0, 0.0]], dtype=np.complex)
-
-    if name == 'P1':
-        return np.array([[0.0, 0.0], [0.0, 1.0]], dtype=np.complex)
-
-    if name == 'Id':
-        return np.identity(2)
-
-    raise TranspilerError("The gate %s isn't supported" % name)
-
-
-def _calc_product(node1, node2):
-    wire_num = len(set(node1.qargs + node2.qargs))
-    wires = sorted(list(map(lambda x: "{0}[{1}]".format(str(x.register.name), str(x.index)),
-                            list(set(node1.qargs + node2.qargs)))))
-    final_unitary = np.identity(2 ** wire_num, dtype=np.complex)
-
-    for node in [node1, node2]:
-
-        qstate_list = [np.identity(2)] * wire_num
-
-        if node.name in ['cx', 'cy', 'cz']:
-
-            qstate_list_ext = [np.identity(2)] * wire_num
-
-            node_ctrl = "{0}[{1}]".format(str(node.qargs[0].register.name),
-                                          str(node.qargs[0].index))
-            node_tgt = "{0}[{1}]".format(str(node.qargs[1].register.name), str(node.qargs[1].index))
-            ctrl = wires.index(node_ctrl)
-            tgt = wires.index(node_tgt)
-
-            qstate_list[ctrl] = _gate_master_def(name='P0')
-            qstate_list[tgt] = _gate_master_def(name='Id')
-            qstate_list_ext[ctrl] = _gate_master_def(name='P1')
-            if node.name == 'cx':
-                qstate_list_ext[tgt] = _gate_master_def(name='x')
-            if node.name == 'cy':
-                qstate_list_ext[tgt] = _gate_master_def(name='y')
-            if node.name == 'cz':
-                qstate_list_ext[tgt] = _gate_master_def(name='z')
-
-            rt_list = [qstate_list] + [qstate_list_ext]
-
-        else:
-
-            mat = _gate_master_def(name=node.name, params=node.op.params)
-            node_num = "{0}[{1}]".format(str(node.qargs[0].register.name),
-                                         str(node.qargs[0].index))
-            qstate_list[wires.index(node_num)] = mat
-
-            rt_list = [qstate_list]
-
-        crt = np.zeros([2 ** wire_num, 2 ** wire_num])
-
-        for state in rt_list:
-            crt = crt + _kron_list(state)
-
-        final_unitary = np.dot(crt, final_unitary)
-    return final_unitary
-
-
-def _kron_list(args):
-    ret = args[0]
-    for item in args[1:]:
-        ret = np.kron(ret, item)
-    return ret
-
-
-def _matrix_commute(node1, node2):
-    # Good for composite gates or any future
-    # user-defined gate of equal or less than 2 qubits.
-    ret = False
-    if set(node1.qargs) & set(node2.qargs) == set():
-        ret = True
-    if _calc_product(node1, node2) is not None:
-        ret = np.allclose(_calc_product(node1, node2),
-                          _calc_product(node2, node1),
-                          atol=_CUTOFF_PRECISION)
-    return ret
-
-
 def _commute(node1, node2):
+
     if node1.type != "op" or node2.type != "op":
         return False
-    return _matrix_commute(node1, node2)
+
+    if any([nd.name in {"barrier", "snapshot", "measure", "reset", "copy"}
+            for nd in [node1, node2]]):
+        return False
+
+    qarg = list(set(node1.qargs + node2.qargs))
+    qbit_num = len(qarg)
+
+    qarg1 = [qarg.index(q) for q in node1.qargs]
+    qarg2 = [qarg.index(q) for q in node2.qargs]
+
+    id_op = Operator(np.eye(2 ** qbit_num))
+
+    op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op, qargs=qarg2)
+    op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op, qargs=qarg1)
+
+    if_commute = (op12 == op21)
+
+    return if_commute
diff --git a/qiskit/transpiler/passes/commutative_cancellation.py b/qiskit/transpiler/passes/commutative_cancellation.py
--- a/qiskit/transpiler/passes/commutative_cancellation.py
+++ b/qiskit/transpiler/passes/commutative_cancellation.py
@@ -17,7 +17,7 @@
 commutation relations in the circuit. Gates considered include H, X, Y, Z, CX, CY, CZ.
 """
 from collections import defaultdict
-import sympy
+import numpy as np
 
 from qiskit.circuit.quantumregister import QuantumRegister
 from qiskit.transpiler.exceptions import TranspilerError
@@ -25,6 +25,9 @@
 from qiskit.transpiler.passes.commutation_analysis import CommutationAnalysis
 from qiskit.dagcircuit import DAGCircuit
 from qiskit.extensions.standard.u1 import U1Gate
+from qiskit.extensions.standard.rx import RXGate
+
+_CUTOFF_PRECISION = 1E-5
 
 
 class CommutativeCancellation(TransformationPass):
@@ -50,11 +53,20 @@ def run(self, dag):
             TranspilerError: when the 1 qubit rotation gates are not found
         """
 
-        q_gate_list = ['cx', 'cy', 'cz', 'h', 'x', 'y', 'z']
+        # Now the gates supported are hard-coded
+        q_gate_list = ['cx', 'cy', 'cz', 'h', 'y']
 
         # Gate sets to be cancelled
         cancellation_sets = defaultdict(lambda: [])
 
+        # Traverse each qubit to generate the cancel dictionaries
+        # Cancel dictionaries:
+        #  - For 1 qubit gates the key is (gate_type, qubit_id, commutation_set_id),
+        #    the value is the list of gates that share the same gate type, qubit, commutation set.
+        #  - For 2qbit gates the key: (gate_type, first_qbit, sec_qbit, first commutation_set_id,
+        #    sec_commutation_set_id), the value is the list gates that share the same gate type,
+        #    qubits and commutation sets.
+
         for wire in dag.wires:
             wire_name = "{0}[{1}]".format(str(wire.register.name), str(wire.index))
             wire_commutation_set = self.property_set['commutation_set'][wire_name]
@@ -66,12 +78,16 @@ 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 ['u1', 'rz', 't', 's']:
+                    if num_qargs == 1 and node.name in ['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)
+                    # Doen't deal with Y rotaion, because Y rotation doesn't commute with CNOT, so
+                    # it should be dealt with by optimized1qgate pass
                     elif num_qargs == 2 and node.qargs[0] == wire:
                         second_op_name = "{0}[{1}]".format(str(node.qargs[1].register.name),
                                                            str(node.qargs[1].index))
-                        q2_key = (node.name, wire_name, second_op_name,
+                        q2_key = (node.name, wire_name, second_op_name, com_set_idx,
                                   self.property_set['commutation_set'][(node, second_op_name)])
                         cancellation_sets[q2_key].append(node)
 
@@ -82,7 +98,7 @@ def run(self, dag):
                 for c_node in gates_to_cancel[:(set_len // 2) * 2]:
                     dag.remove_op_node(c_node)
 
-            elif((set_len) > 1 and cancel_set_key[0] == 'z_rotation'):
+            elif((set_len) > 1 and cancel_set_key[0] in ['z_rotation', 'x_rotation']):
                 run = cancellation_sets[cancel_set_key]
                 run_qarg = run[0].qargs[0]
                 total_angle = 0.0  # lambda
@@ -92,26 +108,36 @@ def run(self, dag):
                             or current_node.qargs[0] != run_qarg):
                         raise TranspilerError("internal error")
 
-                    if current_node.name in ['u1', 'rz']:
+                    if current_node.name in ['u1', 'rz', 'rx']:
                         current_angle = float(current_node.op.params[0])
+                    elif current_node.name in ['z', 'x']:
+                        current_angle = np.pi
                     elif current_node.name == 't':
-                        current_angle = sympy.pi / 4
+                        current_angle = np.pi / 4
                     elif current_node.name == 's':
-                        current_angle = sympy.pi / 2
+                        current_angle = np.pi / 2
 
                     # Compose gates
                     total_angle = current_angle + total_angle
 
                 # Replace the data of the first node in the run
-                new_op = U1Gate(total_angle)
-                new_qarg = QuantumRegister(1, 'q')[0]
-                new_dag = DAGCircuit()
-                new_dag.add_qreg(new_qarg.register)
-                new_dag.apply_operation_back(new_op, [new_qarg])
-                dag.substitute_node_with_dag(run[0], new_dag)
+                if cancel_set_key[0] == 'z_rotation':
+                    new_op = U1Gate(total_angle)
+                elif cancel_set_key[0] == 'x_rotation':
+                    new_op = RXGate(total_angle)
+
+                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)
 
                 # Delete the other nodes in the run
                 for current_node in run[1:]:
                     dag.remove_op_node(current_node)
 
+                if np.mod(total_angle, (2 * np.pi)) < _CUTOFF_PRECISION:
+                    dag.remove_op_node(run[0])
+
         return dag


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
@@ -15,13 +15,14 @@
 """Gate cancellation pass testing"""
 
 import unittest
+import numpy as np
 import sympy
 from qiskit.test import QiskitTestCase
 
 from qiskit import QuantumRegister, QuantumCircuit
 from qiskit.transpiler import PassManager, PropertySet
 from qiskit.compiler import transpile
-from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation
+from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation, FixedPoint, Size
 
 
 class TestCommutativeCancellation(QiskitTestCase):
@@ -37,9 +38,9 @@ def setUp(self):
     def test_all_gates(self):
         """Test all gates on 1 and 2 qubits
 
-        q0:-[H]-[H]--[x]-[x]--[y]-[y]--[rz]-[rz]--[u1]-[u1]---------.--.--.--.--.--.-
-                                                                    |  |  |  |  |  |
-        q1:---------------------------------------------------------X--X--Y--Y--.--.-
+        q0:-[H]-[H]--[x]-[x]--[y]-[y]--[rz]-[rz]--[u1]-[u1]-[rx]-[rx]---.--.--.--.--.--.-
+                                                                        |  |  |  |  |  |
+        q1:-------------------------------------------------------------X--X--Y--Y--.--.-
 
         =
 
@@ -59,6 +60,8 @@ def test_all_gates(self):
         circuit.rz(0.5, qr[0])
         circuit.u1(0.5, qr[0])
         circuit.u1(0.5, qr[0])
+        circuit.rx(0.5, qr[0])
+        circuit.rx(0.5, qr[0])
         circuit.cx(qr[0], qr[1])
         circuit.cx(qr[0], qr[1])
         circuit.cy(qr[0], qr[1])
@@ -72,6 +75,7 @@ def test_all_gates(self):
 
         expected = QuantumCircuit(qr)
         expected.u1(2.0, qr[0])
+        expected.rx(1.0, qr[0])
 
         self.assertEqual(expected, new_circuit)
 
@@ -101,6 +105,246 @@ def test_commutative_circuit1(self):
 
         self.assertEqual(expected, new_circuit)
 
+    def test_consecutive_cnots(self):
+        """A simple circuit equals identity
+
+        qr0:----.- ----.--       qr0:------------
+                |      |
+        qr1:---(+)----(+)-   =   qr1:------------
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_consecutive_cnots2(self):
+        """
+        Two CNOTs that equals identity, with rotation gates inserted.
+        """
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.rx(np.pi, qr[0])
+        circuit.cx(qr[0], qr[1])
+        circuit.cx(qr[0], qr[1])
+        circuit.rx(np.pi, qr[0])
+
+        passmanager = PassManager()
+        # passmanager.append(CommutativeCancellation())
+        passmanager.append([CommutationAnalysis(),
+                            CommutativeCancellation(),
+                            Size(), FixedPoint('size')],
+                           do_while=lambda property_set: not property_set['size_fixed_point'])
+        new_circuit = transpile(circuit, pass_manager=passmanager)
+        expected = QuantumCircuit(qr)
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_2_alternating_cnots(self):
+        """A simple circuit where nothing should be cancelled.
+
+        qr0:----.- ---(+)-       qr0:----.----(+)-
+                |      |                 |     |
+        qr1:---(+)-----.--   =   qr1:---(+)----.--
+
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.cx(qr[1], qr[0])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.cx(qr[0], qr[1])
+        expected.cx(qr[1], qr[0])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_control_bit_of_cnot(self):
+        """A simple circuit where nothing should be cancelled.
+
+        qr0:----.------[X]------.--       qr0:----.------[X]------.--
+                |               |                 |               |
+        qr1:---(+)-------------(+)-   =   qr1:---(+)-------------(+)-
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.x(qr[0])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.cx(qr[0], qr[1])
+        expected.x(qr[0])
+        expected.cx(qr[0], qr[1])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_control_bit_of_cnot1(self):
+        """A simple circuit where the two cnots shoule be cancelled.
+
+        qr0:----.------[Z]------.--       qr0:---[Z]---
+                |               |
+        qr1:---(+)-------------(+)-   =   qr1:---------
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.z(qr[0])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.z(qr[0])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_control_bit_of_cnot2(self):
+        """A simple circuit where the two cnots shoule be cancelled.
+
+        qr0:----.------[T]------.--       qr0:---[T]---
+                |               |
+        qr1:---(+)-------------(+)-   =   qr1:---------
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.t(qr[0])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.t(qr[0])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_control_bit_of_cnot3(self):
+        """A simple circuit where the two cnots shoule be cancelled.
+
+        qr0:----.------[Rz]------.--       qr0:---[Rz]---
+                |                |
+        qr1:---(+)-------- -----(+)-   =   qr1:----------
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.rz(sympy.pi / 3, qr[0])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.rz(sympy.pi / 3, qr[0])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_control_bit_of_cnot4(self):
+        """A simple circuit where the two cnots shoule be cancelled.
+
+        qr0:----.------[T]------.--       qr0:---[T]---
+                |               |
+        qr1:---(+)-------------(+)-   =   qr1:---------
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.t(qr[0])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.t(qr[0])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_target_bit_of_cnot(self):
+
+        """A simple circuit where nothing should be cancelled.
+
+        qr0:----.---------------.--       qr0:----.---------------.--
+                |               |                 |               |
+        qr1:---(+)-----[Z]-----(+)-   =   qr1:---(+)----[Z]------(+)-
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.z(qr[1])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.cx(qr[0], qr[1])
+        expected.z(qr[1])
+        expected.cx(qr[0], qr[1])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_target_bit_of_cnot1(self):
+        """A simple circuit where nothing should be cancelled.
+
+        qr0:----.---------------.--       qr0:----.---------------.--
+                |               |                 |               |
+        qr1:---(+)-----[T]-----(+)-   =   qr1:---(+)----[T]------(+)-
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.t(qr[1])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.cx(qr[0], qr[1])
+        expected.t(qr[1])
+        expected.cx(qr[0], qr[1])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_target_bit_of_cnot2(self):
+        """A simple circuit where nothing should be cancelled.
+
+        qr0:----.---------------.--       qr0:----.---------------.--
+                |               |                 |               |
+        qr1:---(+)-----[Rz]----(+)-   =   qr1:---(+)----[Rz]-----(+)-
+        """
+
+        qr = QuantumRegister(2, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.rz(np.pi / 3, qr[1])
+        circuit.cx(qr[0], qr[1])
+
+        new_pm = PassManager(CommutativeCancellation())
+        new_circuit = transpile(circuit, pass_manager=new_pm)
+        expected = QuantumCircuit(qr)
+        expected.cx(qr[0], qr[1])
+        expected.rz(np.pi / 3, qr[1])
+        expected.cx(qr[0], qr[1])
+
+        self.assertEqual(expected, new_circuit)
+
     def test_commutative_circuit2(self):
         """
         A simple circuit where three CNOTs commute, the first and the last cancel,
@@ -112,6 +356,7 @@ def test_commutative_circuit2(self):
                       |                                    |
         qr2:---[Rz]---.---[Rz]-[T]--[S]--     qr2:--[U1]---.---
         """
+
         qr = QuantumRegister(3, 'qr')
         circuit = QuantumCircuit(qr)
         circuit.cx(qr[0], qr[1])
@@ -133,6 +378,143 @@ def test_commutative_circuit2(self):
 
         self.assertEqual(expected, new_circuit)
 
+    def test_commutative_circuit3(self):
+        """
+        A simple circuit where three CNOTs commute, the first and the last cancel,
+        also two X gates cancel and two Rz gates combine.
+
+        qr0:-------.------------------.-------------     qr0:-------------
+                   |                  |
+        qr1:------(+)------(+)--[X]--(+)-------[X]--  =  qr1:--------(+)--
+                            |                                         |
+        qr2:------[Rz]--.---.----.---[Rz]-[T]--[S]--     qr2:--[U1]---.---
+                        |        |
+        qr3:-[Rz]--[X]-(+)------(+)--[X]-[Rz]-------     qr3:--[Rz]-------
+        """
+
+        qr = QuantumRegister(4, 'qr')
+        circuit = QuantumCircuit(qr)
+
+        circuit.cx(qr[0], qr[1])
+        circuit.rz(sympy.pi / 3, qr[2])
+        circuit.rz(sympy.pi / 3, qr[3])
+        circuit.x(qr[3])
+        circuit.cx(qr[2], qr[3])
+        circuit.cx(qr[2], qr[1])
+        circuit.cx(qr[2], qr[3])
+        circuit.rz(sympy.pi / 3, qr[2])
+        circuit.t(qr[2])
+        circuit.x(qr[3])
+        circuit.rz(sympy.pi / 3, qr[3])
+        circuit.s(qr[2])
+        circuit.x(qr[1])
+        circuit.cx(qr[0], qr[1])
+        circuit.x(qr[1])
+
+        passmanager = PassManager()
+        passmanager.append([CommutationAnalysis(),
+                            CommutativeCancellation(), Size(), FixedPoint('size')],
+                           do_while=lambda property_set: not property_set['size_fixed_point'])
+        new_circuit = transpile(circuit, pass_manager=passmanager)
+        expected = QuantumCircuit(qr)
+        expected.u1(sympy.pi * 17 / 12, qr[2])
+        expected.u1(sympy.pi * 2 / 3, qr[3])
+        expected.cx(qr[2], qr[1])
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_cnot_cascade(self):
+        """
+        A cascade of CNOTs that equals identity.
+        """
+
+        qr = QuantumRegister(10, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.cx(qr[0], qr[1])
+        circuit.cx(qr[1], qr[2])
+        circuit.cx(qr[2], qr[3])
+        circuit.cx(qr[3], qr[4])
+        circuit.cx(qr[4], qr[5])
+        circuit.cx(qr[5], qr[6])
+        circuit.cx(qr[6], qr[7])
+        circuit.cx(qr[7], qr[8])
+        circuit.cx(qr[8], qr[9])
+
+        circuit.cx(qr[8], qr[9])
+        circuit.cx(qr[7], qr[8])
+        circuit.cx(qr[6], qr[7])
+        circuit.cx(qr[5], qr[6])
+        circuit.cx(qr[4], qr[5])
+        circuit.cx(qr[3], qr[4])
+        circuit.cx(qr[2], qr[3])
+        circuit.cx(qr[1], qr[2])
+        circuit.cx(qr[0], qr[1])
+
+        passmanager = PassManager()
+        # passmanager.append(CommutativeCancellation())
+        passmanager.append([CommutationAnalysis(),
+                            CommutativeCancellation(), Size(), FixedPoint('size')],
+                           do_while=lambda property_set: not property_set['size_fixed_point'])
+        new_circuit = transpile(circuit, pass_manager=passmanager)
+        expected = QuantumCircuit(qr)
+
+        self.assertEqual(expected, new_circuit)
+
+    def test_cnot_cascade1(self):
+        """
+        A cascade of CNOTs that equals identity, with rotation gates inserted.
+        """
+
+        qr = QuantumRegister(10, 'qr')
+        circuit = QuantumCircuit(qr)
+        circuit.rx(np.pi, qr[0])
+        circuit.rx(np.pi, qr[1])
+        circuit.rx(np.pi, qr[2])
+        circuit.rx(np.pi, qr[3])
+        circuit.rx(np.pi, qr[4])
+        circuit.rx(np.pi, qr[5])
+        circuit.rx(np.pi, qr[6])
+        circuit.rx(np.pi, qr[7])
+        circuit.rx(np.pi, qr[8])
+        circuit.rx(np.pi, qr[9])
+        circuit.cx(qr[0], qr[1])
+        circuit.cx(qr[1], qr[2])
+        circuit.cx(qr[2], qr[3])
+        circuit.cx(qr[3], qr[4])
+        circuit.cx(qr[4], qr[5])
+        circuit.cx(qr[5], qr[6])
+        circuit.cx(qr[6], qr[7])
+        circuit.cx(qr[7], qr[8])
+        circuit.cx(qr[8], qr[9])
+        circuit.cx(qr[8], qr[9])
+        circuit.cx(qr[7], qr[8])
+        circuit.cx(qr[6], qr[7])
+        circuit.cx(qr[5], qr[6])
+        circuit.cx(qr[4], qr[5])
+        circuit.cx(qr[3], qr[4])
+        circuit.cx(qr[2], qr[3])
+        circuit.cx(qr[1], qr[2])
+        circuit.cx(qr[0], qr[1])
+        circuit.rx(np.pi, qr[0])
+        circuit.rx(np.pi, qr[1])
+        circuit.rx(np.pi, qr[2])
+        circuit.rx(np.pi, qr[3])
+        circuit.rx(np.pi, qr[4])
+        circuit.rx(np.pi, qr[5])
+        circuit.rx(np.pi, qr[6])
+        circuit.rx(np.pi, qr[7])
+        circuit.rx(np.pi, qr[8])
+        circuit.rx(np.pi, qr[9])
+        passmanager = PassManager()
+        # passmanager.append(CommutativeCancellation())
+        passmanager.append([CommutationAnalysis(),
+                            CommutativeCancellation(), Size(), FixedPoint('size')],
+                           do_while=lambda property_set: not property_set['size_fixed_point'])
+        new_circuit = transpile(circuit, pass_manager=passmanager)
+        expected = QuantumCircuit(qr)
+
+        self.assertEqual(expected, new_circuit)
+
 
 if __name__ == '__main__':
     unittest.main()