def test_caching_negative_results(self):
        """Check that hashing negative results in commutativity checker works as expected."""

        comm_checker = CommutationChecker()
        res = comm_checker.commute(XGate(), [0], [], CXGate(), [0, 1], [])
        self.assertFalse(res)
        self.assertGreater(len(comm_checker.cache), 0)
    def test_reset(self):
        """Check commutativity involving resets."""

        comm_checker = CommutationChecker()

        # A gate should not commute with reset when the qubits intersect.
        res = comm_checker.commute(Reset(), [0], [], CXGate(), [0, 2], [])
        self.assertFalse(res)

        # A gate should commute with reset when the qubits are disjoint.
        res = comm_checker.commute(Reset(), [0], [], CXGate(), [1, 2], [])
        self.assertTrue(res)
    def test_caching_different_qubit_sets(self):
        """Check that hashing same commutativity results over different qubit sets works as expected."""

        comm_checker = CommutationChecker()

        # All the following should be cached in the same way
        # though each relation gets cached twice: (A, B) and (B, A)
        comm_checker.commute(XGate(), [0], [], CXGate(), [0, 1], [])
        comm_checker.commute(XGate(), [10], [], CXGate(), [10, 20], [])
        comm_checker.commute(XGate(), [10], [], CXGate(), [10, 5], [])
        comm_checker.commute(XGate(), [5], [], CXGate(), [5, 7], [])
        self.assertEqual(len(comm_checker.cache), 2)
    def test_gates_with_parameters(self):
        """Check commutativity between (non-parameterized) gates with parameters."""

        comm_checker = CommutationChecker()
        res = comm_checker.commute(RZGate(0), [0], [], XGate(), [0], [])
        self.assertTrue(res)

        res = comm_checker.commute(RZGate(np.pi / 2), [0], [], XGate(), [0],
                                   [])
        self.assertFalse(res)

        res = comm_checker.commute(RZGate(np.pi / 2), [0], [], RZGate(0), [0],
                                   [])
        self.assertTrue(res)
    def test_parameterized_gates(self):
        """Check commutativity between parameterized gates, both with free and with
        bound parameters."""

        comm_checker = CommutationChecker()

        # gate that has parameters but is not considered parameterized
        rz_gate = RZGate(np.pi / 2)
        self.assertEqual(len(rz_gate.params), 1)
        self.assertFalse(rz_gate.is_parameterized())

        # gate that has parameters and is considered parameterized
        rz_gate_theta = RZGate(Parameter("Theta"))
        rz_gate_phi = RZGate(Parameter("Phi"))
        self.assertEqual(len(rz_gate_theta.params), 1)
        self.assertTrue(rz_gate_theta.is_parameterized())

        # gate that has no parameters and is not considered parameterized
        cx_gate = CXGate()
        self.assertEqual(len(cx_gate.params), 0)
        self.assertFalse(cx_gate.is_parameterized())

        # We should detect that these gates commute
        res = comm_checker.commute(rz_gate, [0], [], cx_gate, [0, 1], [])
        self.assertTrue(res)

        # We should detect that these gates commute
        res = comm_checker.commute(rz_gate, [0], [], rz_gate, [0], [])
        self.assertTrue(res)

        # We should detect that parameterized gates over disjoint qubit subsets commute
        res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate_theta, [1],
                                   [])
        self.assertTrue(res)

        # We should detect that parameterized gates over disjoint qubit subsets commute
        res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate_phi, [1],
                                   [])
        self.assertTrue(res)

        # We should detect that parameterized gates over disjoint qubit subsets commute
        res = comm_checker.commute(rz_gate_theta, [2], [], cx_gate, [1, 3], [])
        self.assertTrue(res)

        # However, for now commutativity checker should return False when checking
        # commutativity between a parameterized gate and some other gate, when
        # the two gates are over intersecting qubit subsets.
        # This check should be changed if commutativity checker is extended to
        # handle parameterized gates better.
        res = comm_checker.commute(rz_gate_theta, [0], [], cx_gate, [0, 1], [])
        self.assertFalse(res)

        res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate, [0], [])
        self.assertFalse(res)
    def test_passing_quantum_registers(self):
        """Check that passing QuantumRegisters works correctly."""
        comm_checker = CommutationChecker()

        qr = QuantumRegister(4)

        # should commute
        res = comm_checker.commute(CXGate(), [qr[1], qr[0]], [], CXGate(),
                                   [qr[1], qr[2]], [])
        self.assertTrue(res)

        # should not commute
        res = comm_checker.commute(CXGate(), [qr[0], qr[1]], [], CXGate(),
                                   [qr[1], qr[2]], [])
        self.assertFalse(res)
    def test_barrier(self):
        """Check commutativity involving barriers."""

        comm_checker = CommutationChecker()

        # A gate should not commute with a barrier
        # (at least if these are over intersecting qubit sets).
        res = comm_checker.commute(Barrier(4), [0, 1, 2, 3], [], CXGate(),
                                   [1, 2], [])
        self.assertFalse(res)

        # Does it even make sense to have a barrier over a subset of qubits?
        # Though in this case, it probably makes sense to say that barrier and gate can be swapped.
        res = comm_checker.commute(Barrier(4), [0, 1, 2, 3], [], CXGate(),
                                   [5, 6], [])
        self.assertTrue(res)
    def test_conditional_gates(self):
        """Check commutativity involving conditional gates."""

        comm_checker = CommutationChecker()

        qr = QuantumRegister(3)
        cr = ClassicalRegister(2)

        # Currently, in all cases commutativity checker should returns False.
        # This is definitely suboptimal.
        res = comm_checker.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [],
                                   XGate(), [qr[2]], [])
        self.assertFalse(res)

        res = comm_checker.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [],
                                   XGate(), [qr[1]], [])
        self.assertFalse(res)

        res = comm_checker.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [],
                                   CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [])
        self.assertFalse(res)

        res = comm_checker.commute(XGate().c_if(cr[0], 0), [qr[0]], [],
                                   XGate().c_if(cr[0], 1), [qr[0]], [])
        self.assertFalse(res)

        res = comm_checker.commute(XGate().c_if(cr[0], 0), [qr[0]], [],
                                   XGate(), [qr[0]], [])
        self.assertFalse(res)
    def test_measure(self):
        """Check commutativity involving measures."""

        comm_checker = CommutationChecker()

        # Measure is over qubit 0, while gate is over a disjoint subset of qubits
        # We should be able to swap these.
        res = comm_checker.commute(Measure(), [0], [0], CXGate(), [1, 2], [])
        self.assertTrue(res)

        # Measure and gate have intersecting set of qubits
        # We should not be able to swap these.
        res = comm_checker.commute(Measure(), [0], [0], CXGate(), [0, 2], [])
        self.assertFalse(res)

        # Measures over different qubits and clbits
        res = comm_checker.commute(Measure(), [0], [0], Measure(), [1], [1])
        self.assertTrue(res)

        # Measures over different qubits but same classical bit
        # We should not be able to swap these.
        res = comm_checker.commute(Measure(), [0], [0], Measure(), [1], [0])
        self.assertFalse(res)

        # Measures over same qubits but different classical bit
        # ToDo: can we swap these?
        # Currently checker takes the safe approach and returns False.
        res = comm_checker.commute(Measure(), [0], [0], Measure(), [0], [1])
        self.assertFalse(res)
    def test_complex_gates(self):
        """Check commutativity involving more complex gates."""

        comm_checker = CommutationChecker()

        lf1 = LinearFunction([[0, 1, 0], [1, 0, 0], [0, 0, 1]])
        lf2 = LinearFunction([[1, 0, 0], [0, 0, 1], [0, 1, 0]])

        # lf1 is equivalent to swap(0, 1), and lf2 to swap(1, 2).
        # These do not commute.
        res = comm_checker.commute(lf1, [0, 1, 2], [], lf2, [0, 1, 2], [])
        self.assertFalse(res)

        lf3 = LinearFunction([[0, 1, 0], [0, 0, 1], [1, 0, 0]])
        lf4 = LinearFunction([[0, 0, 1], [1, 0, 0], [0, 1, 0]])
        # lf3 is permutation 1->2, 2->3, 3->1.
        # lf3 is the inverse permutation 1->3, 2->1, 3->2.
        # These commute.
        res = comm_checker.commute(lf3, [0, 1, 2], [], lf4, [0, 1, 2], [])
        self.assertTrue(res)
    def test_simple_gates(self):
        """Check simple commutation relations between gates, experimenting with
        different orders of gates, different orders of qubits, different sets of
        qubits over which gates are defined, and so on."""
        comm_checker = CommutationChecker()

        # should commute
        res = comm_checker.commute(ZGate(), [0], [], CXGate(), [0, 1], [])
        self.assertTrue(res)

        # should not commute
        res = comm_checker.commute(ZGate(), [1], [], CXGate(), [0, 1], [])
        self.assertFalse(res)

        # should not commute
        res = comm_checker.commute(XGate(), [0], [], CXGate(), [0, 1], [])
        self.assertFalse(res)

        # should commute
        res = comm_checker.commute(XGate(), [1], [], CXGate(), [0, 1], [])
        self.assertTrue(res)

        # should not commute
        res = comm_checker.commute(XGate(), [1], [], CXGate(), [1, 0], [])
        self.assertFalse(res)

        # should commute
        res = comm_checker.commute(XGate(), [0], [], CXGate(), [1, 0], [])
        self.assertTrue(res)

        # should commute
        res = comm_checker.commute(CXGate(), [1, 0], [], XGate(), [0], [])
        self.assertTrue(res)

        # should not commute
        res = comm_checker.commute(CXGate(), [1, 0], [], XGate(), [1], [])
        self.assertFalse(res)

        # should commute
        res = comm_checker.commute(
            CXGate(),
            [1, 0],
            [],
            CXGate(),
            [1, 0],
            [],
        )
        self.assertTrue(res)

        # should not commute
        res = comm_checker.commute(
            CXGate(),
            [1, 0],
            [],
            CXGate(),
            [0, 1],
            [],
        )
        self.assertFalse(res)

        # should commute
        res = comm_checker.commute(
            CXGate(),
            [1, 0],
            [],
            CXGate(),
            [1, 2],
            [],
        )
        self.assertTrue(res)

        # should not commute
        res = comm_checker.commute(
            CXGate(),
            [1, 0],
            [],
            CXGate(),
            [2, 1],
            [],
        )
        self.assertFalse(res)

        # should commute
        res = comm_checker.commute(
            CXGate(),
            [1, 0],
            [],
            CXGate(),
            [2, 3],
            [],
        )
        self.assertTrue(res)

        res = comm_checker.commute(XGate(), [2], [], CCXGate(), [0, 1, 2], [])
        self.assertTrue(res)

        res = comm_checker.commute(CCXGate(), [0, 1, 2], [], CCXGate(),
                                   [0, 2, 1], [])
        self.assertFalse(res)