def test_twoq_equivalence(self): """Test GMS on 2 qubits is same as RXX.""" circuit = GMS(num_qubits=2, theta=[[0, np.pi / 3], [0, 0]]) expected = RXXGate(np.pi / 3) expected = Operator(expected) simulated = Operator(circuit) self.assertTrue(expected.equiv(simulated))
def canonical_matrix(a=0.0, b=0.0, c=0.0): """ Produces the matrix form of a "canonical operator" exp(-i (a XX + b YY + c ZZ)) . """ return RXXGate(2 * a).to_matrix() @ RYYGate(2 * b).to_matrix() @ RZZGate(-2 * c).to_matrix()
def test_euler_basis_selection(self): """Verify decomposition uses euler_basis for 1q gates.""" euler_bases = [ ('U3', ['u3']), ('U1X', ['u1', 'rx']), ('RR', ['r']), ('ZYZ', ['rz', 'ry']), ('ZXZ', ['rz', 'rx']), ('XYX', ['rx', 'ry']), ] kak_gates = [ (CXGate(), 'cx'), (CZGate(), 'cz'), (iSwapGate(), 'iswap'), (RXXGate(np.pi / 2), 'rxx'), ] for basis in product(euler_bases, kak_gates): (euler_basis, oneq_gates), (kak_gate, kak_gate_name) = basis with self.subTest(euler_basis=euler_basis, kak_gate=kak_gate): decomposer = TwoQubitBasisDecomposer(kak_gate, euler_basis=euler_basis) unitary = random_unitary(4) self.check_exact_decomposition(unitary.data, decomposer) decomposition_basis = set(decomposer(unitary).count_ops()) requested_basis = set(oneq_gates + [kak_gate_name]) self.assertTrue(decomposition_basis.issubset(requested_basis))
def test_operations(self): self.assertEqual(self.empty_target.operations, []) ibm_expected = [ RZGate(self.theta), IGate(), SXGate(), XGate(), CXGate(), Measure() ] for gate in ibm_expected: self.assertIn(gate, self.ibm_target.operations) aqt_expected = [ RZGate(self.theta), RXGate(self.theta), RYGate(self.theta), RGate(self.theta, self.phi), RXXGate(self.theta), ] for gate in aqt_expected: self.assertIn(gate, self.aqt_target.operations) fake_expected = [ UGate(self.fake_backend._theta, self.fake_backend._phi, self.fake_backend._lam), CXGate(), Measure(), ECRGate(), RXGate(math.pi / 6), RXGate(self.fake_backend._theta), ] for gate in fake_expected: self.assertIn(gate, self.fake_backend_target.operations) ideal_sim_expected = [ UGate(self.theta, self.phi, self.lam), RXGate(self.theta), RYGate(self.theta), RZGate(self.theta), CXGate(), ECRGate(), CCXGate(), Measure(), ] for gate in ideal_sim_expected: self.assertIn(gate, self.ideal_sim_target.operations)
def setUp(self): super().setUp() self.fake_backend = FakeBackendV2() self.fake_backend_target = self.fake_backend.target self.theta = Parameter("theta") self.phi = Parameter("phi") self.ibm_target = Target() i_props = { (0, ): InstructionProperties(duration=35.5e-9, error=0.000413), (1, ): InstructionProperties(duration=35.5e-9, error=0.000502), (2, ): InstructionProperties(duration=35.5e-9, error=0.0004003), (3, ): InstructionProperties(duration=35.5e-9, error=0.000614), (4, ): InstructionProperties(duration=35.5e-9, error=0.006149), } self.ibm_target.add_instruction(IGate(), i_props) rz_props = { (0, ): InstructionProperties(duration=0, error=0), (1, ): InstructionProperties(duration=0, error=0), (2, ): InstructionProperties(duration=0, error=0), (3, ): InstructionProperties(duration=0, error=0), (4, ): InstructionProperties(duration=0, error=0), } self.ibm_target.add_instruction(RZGate(self.theta), rz_props) sx_props = { (0, ): InstructionProperties(duration=35.5e-9, error=0.000413), (1, ): InstructionProperties(duration=35.5e-9, error=0.000502), (2, ): InstructionProperties(duration=35.5e-9, error=0.0004003), (3, ): InstructionProperties(duration=35.5e-9, error=0.000614), (4, ): InstructionProperties(duration=35.5e-9, error=0.006149), } self.ibm_target.add_instruction(SXGate(), sx_props) x_props = { (0, ): InstructionProperties(duration=35.5e-9, error=0.000413), (1, ): InstructionProperties(duration=35.5e-9, error=0.000502), (2, ): InstructionProperties(duration=35.5e-9, error=0.0004003), (3, ): InstructionProperties(duration=35.5e-9, error=0.000614), (4, ): InstructionProperties(duration=35.5e-9, error=0.006149), } self.ibm_target.add_instruction(XGate(), x_props) cx_props = { (3, 4): InstructionProperties(duration=270.22e-9, error=0.00713), (4, 3): InstructionProperties(duration=305.77e-9, error=0.00713), (3, 1): InstructionProperties(duration=462.22e-9, error=0.00929), (1, 3): InstructionProperties(duration=497.77e-9, error=0.00929), (1, 2): InstructionProperties(duration=227.55e-9, error=0.00659), (2, 1): InstructionProperties(duration=263.11e-9, error=0.00659), (0, 1): InstructionProperties(duration=519.11e-9, error=0.01201), (1, 0): InstructionProperties(duration=554.66e-9, error=0.01201), } self.ibm_target.add_instruction(CXGate(), cx_props) measure_props = { (0, ): InstructionProperties(duration=5.813e-6, error=0.0751), (1, ): InstructionProperties(duration=5.813e-6, error=0.0225), (2, ): InstructionProperties(duration=5.813e-6, error=0.0146), (3, ): InstructionProperties(duration=5.813e-6, error=0.0215), (4, ): InstructionProperties(duration=5.813e-6, error=0.0333), } self.ibm_target.add_instruction(Measure(), measure_props) self.aqt_target = Target(description="AQT Target") rx_props = { (0, ): None, (1, ): None, (2, ): None, (3, ): None, (4, ): None, } self.aqt_target.add_instruction(RXGate(self.theta), rx_props) ry_props = { (0, ): None, (1, ): None, (2, ): None, (3, ): None, (4, ): None, } self.aqt_target.add_instruction(RYGate(self.theta), ry_props) rz_props = { (0, ): None, (1, ): None, (2, ): None, (3, ): None, (4, ): None, } self.aqt_target.add_instruction(RZGate(self.theta), rz_props) r_props = { (0, ): None, (1, ): None, (2, ): None, (3, ): None, (4, ): None, } self.aqt_target.add_instruction(RGate(self.theta, self.phi), r_props) rxx_props = { (0, 1): None, (0, 2): None, (0, 3): None, (0, 4): None, (1, 0): None, (2, 0): None, (3, 0): None, (4, 0): None, (1, 2): None, (1, 3): None, (1, 4): None, (2, 1): None, (3, 1): None, (4, 1): None, (2, 3): None, (2, 4): None, (3, 2): None, (4, 2): None, (3, 4): None, (4, 3): None, } self.aqt_target.add_instruction(RXXGate(self.theta), rxx_props) measure_props = { (0, ): None, (1, ): None, (2, ): None, (3, ): None, (4, ): None, } self.aqt_target.add_instruction(Measure(), measure_props) self.empty_target = Target() self.ideal_sim_target = Target(num_qubits=3, description="Ideal Simulator") self.lam = Parameter("lam") for inst in [ UGate(self.theta, self.phi, self.lam), RXGate(self.theta), RYGate(self.theta), RZGate(self.theta), CXGate(), ECRGate(), CCXGate(), Measure(), ]: self.ideal_sim_target.add_instruction(inst, {None: None})
class TestTwoQubitDecomposeExact(CheckDecompositions): """Test TwoQubitBasisDecomposer() for exact decompositions """ def test_cnot_rxx_decompose(self): """Verify CNOT decomposition into RXX gate is correct""" cnot = Operator(CXGate()) decomps = [cnot_rxx_decompose(), cnot_rxx_decompose(plus_ry=True, plus_rxx=True), cnot_rxx_decompose(plus_ry=True, plus_rxx=False), cnot_rxx_decompose(plus_ry=False, plus_rxx=True), cnot_rxx_decompose(plus_ry=False, plus_rxx=False)] for decomp in decomps: self.assertTrue(cnot.equiv(decomp)) @combine(seed=range(10), name='test_exact_two_qubit_cnot_decompose_random_{seed}') def test_exact_two_qubit_cnot_decompose_random(self, seed): """Verify exact CNOT decomposition for random Haar 4x4 unitary (seed={seed}). """ unitary = random_unitary(4, seed=seed) self.check_exact_decomposition(unitary.data, two_qubit_cnot_decompose) def test_exact_two_qubit_cnot_decompose_paulis(self): """Verify exact CNOT decomposition for Paulis """ unitary = Operator.from_label('XZ') self.check_exact_decomposition(unitary.data, two_qubit_cnot_decompose) @combine(seed=range(10), name='test_exact_supercontrolled_decompose_random_{seed}') def test_exact_supercontrolled_decompose_random(self, seed): """Exact decomposition for random supercontrolled basis and random target (seed={seed})""" # pylint: disable=invalid-name k1 = np.kron(random_unitary(2, seed=seed).data, random_unitary(2, seed=seed + 1).data) k2 = np.kron(random_unitary(2, seed=seed + 2).data, random_unitary(2, seed=seed + 3).data) basis_unitary = k1 @ Ud(np.pi / 4, 0, 0) @ k2 decomposer = TwoQubitBasisDecomposer(UnitaryGate(basis_unitary)) self.check_exact_decomposition(random_unitary(4, seed=seed + 4).data, decomposer) def test_exact_nonsupercontrolled_decompose(self): """Check that the nonsupercontrolled basis throws a warning""" with self.assertWarns(UserWarning, msg="Supposed to warn when basis non-supercontrolled"): TwoQubitBasisDecomposer(UnitaryGate(Ud(np.pi / 4, 0.2, 0.1))) def test_cx_equivalence_0cx(self, seed=0): """Check circuits with 0 cx gates locally equivalent to identity """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=6) qr = QuantumRegister(2, name='q') qc = QuantumCircuit(qr) qc.u(rnd[0], rnd[1], rnd[2], qr[0]) qc.u(rnd[3], rnd[4], rnd[5], qr[1]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 0) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary)) def test_cx_equivalence_1cx(self, seed=1): """Check circuits with 1 cx gates locally equivalent to a cx """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=12) qr = QuantumRegister(2, name='q') qc = QuantumCircuit(qr) qc.u(rnd[0], rnd[1], rnd[2], qr[0]) qc.u(rnd[3], rnd[4], rnd[5], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[6], rnd[7], rnd[8], qr[0]) qc.u(rnd[9], rnd[10], rnd[11], qr[1]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 1) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary)) def test_cx_equivalence_2cx(self, seed=2): """Check circuits with 2 cx gates locally equivalent to some circuit with 2 cx. """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=18) qr = QuantumRegister(2, name='q') qc = QuantumCircuit(qr) qc.u(rnd[0], rnd[1], rnd[2], qr[0]) qc.u(rnd[3], rnd[4], rnd[5], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[6], rnd[7], rnd[8], qr[0]) qc.u(rnd[9], rnd[10], rnd[11], qr[1]) qc.cx(qr[0], qr[1]) qc.u(rnd[12], rnd[13], rnd[14], qr[0]) qc.u(rnd[15], rnd[16], rnd[17], qr[1]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 2) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary)) def test_cx_equivalence_3cx(self, seed=3): """Check circuits with 3 cx gates are outside the 0, 1, and 2 qubit regions. """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=24) qr = QuantumRegister(2, name='q') qc = QuantumCircuit(qr) qc.u(rnd[0], rnd[1], rnd[2], qr[0]) qc.u(rnd[3], rnd[4], rnd[5], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[6], rnd[7], rnd[8], qr[0]) qc.u(rnd[9], rnd[10], rnd[11], qr[1]) qc.cx(qr[0], qr[1]) qc.u(rnd[12], rnd[13], rnd[14], qr[0]) qc.u(rnd[15], rnd[16], rnd[17], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[18], rnd[19], rnd[20], qr[0]) qc.u(rnd[21], rnd[22], rnd[23], qr[1]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 3) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary)) def test_seed_289(self): """This specific case failed when PR #3585 was applied See https://github.com/Qiskit/qiskit-terra/pull/3652""" unitary = random_unitary(4, seed=289) self.check_exact_decomposition(unitary.data, two_qubit_cnot_decompose) @combine(seed=range(10), euler_bases=[('U3', ['u3']), ('U', ['u']), ('U1X', ['u1', 'rx']), ('RR', ['r']), ('PSX', ['p', 'sx']), ('ZYZ', ['rz', 'ry']), ('ZXZ', ['rz', 'rx']), ('XYX', ['rx', 'ry']), ('ZSX', ['rz', 'sx'])], kak_gates=[(CXGate(), 'cx'), (CZGate(), 'cz'), (iSwapGate(), 'iswap'), (RXXGate(np.pi / 2), 'rxx')], name='test_euler_basis_selection_{seed}_{euler_bases[0]}_{kak_gates[1]}') def test_euler_basis_selection(self, euler_bases, kak_gates, seed): """Verify decomposition uses euler_basis for 1q gates.""" (euler_basis, oneq_gates) = euler_bases (kak_gate, kak_gate_name) = kak_gates with self.subTest(euler_basis=euler_basis, kak_gate=kak_gate): decomposer = TwoQubitBasisDecomposer(kak_gate, euler_basis=euler_basis) unitary = random_unitary(4, seed=seed) self.check_exact_decomposition(unitary.data, decomposer) decomposition_basis = set(decomposer(unitary).count_ops()) requested_basis = set(oneq_gates + [kak_gate_name]) self.assertTrue( decomposition_basis.issubset(requested_basis))
class TestCollect2qBlocks(QiskitTestCase): """ Tests to verify that blocks of 2q interactions are found correctly. """ def test_blocks_in_topological_order(self): """the pass returns blocks in correct topological order ______ q0:--[p]-------.---- q0:-------------| |-- | ______ | U2 | q1:--[u]--(+)-(+)--- = q1:---| |--|______|-- | | U1 | q2:--------.-------- q2:---|______|------------ """ qr = QuantumRegister(3, "qr") qc = QuantumCircuit(qr) qc.p(0.5, qr[0]) qc.u(0.0, 0.2, 0.6, qr[1]) qc.cx(qr[2], qr[1]) qc.cx(qr[0], qr[1]) dag = circuit_to_dag(qc) topo_ops = list(dag.topological_op_nodes()) block_1 = [topo_ops[1], topo_ops[2]] block_2 = [topo_ops[0], topo_ops[3]] pass_ = Collect2qBlocks() pass_.run(dag) self.assertTrue(pass_.property_set["block_list"], [block_1, block_2]) def test_block_interrupted_by_gate(self): """Test that blocks interrupted by a gate that can't be added to the block can be collected correctly This was raised in #2775 where a measure in the middle of a block stopped the block collection from working properly. This was because the pass didn't expect to have measures in the middle of the circuit. blocks : [['cx', 'id', 'id', 'id'], ['id', 'cx']] ┌───┐┌───┐┌─┐ ┌───┐┌───┐ q_0: |0>┤ X ├┤ I ├┤M├─────┤ I ├┤ X ├ └─┬─┘├───┤└╥┘┌───┐└───┘└─┬─┘ q_1: |0>──■──┤ I ├─╫─┤ I ├───────■── └───┘ ║ └───┘ c_0: 0 ═══════════╩════════════════ """ qc = QuantumCircuit(2, 1) qc.cx(1, 0) qc.i(0) qc.i(1) qc.measure(0, 0) qc.i(0) qc.i(1) qc.cx(1, 0) dag = circuit_to_dag(qc) pass_ = Collect2qBlocks() pass_.run(dag) # list from Collect2QBlocks of nodes that it should have put into blocks good_names = ["cx", "u1", "u2", "u3", "id"] dag_nodes = [ node for node in dag.topological_op_nodes() if node.name in good_names ] # we have to convert them to sets as the ordering can be different # but equivalent between python 3.5 and 3.7 # there is no implied topology in a block, so this isn't an issue dag_nodes = [set(dag_nodes[:4]), set(dag_nodes[4:])] pass_nodes = [set(bl) for bl in pass_.property_set["block_list"]] self.assertEqual(dag_nodes, pass_nodes) def test_block_with_classical_register(self): """Test that only blocks that share quantum wires are added to the block. It was the case that gates which shared a classical wire could be added to the same block, despite not sharing the same qubits. This was fixed in #2956. ┌─────────────────────┐ q_0: |0>────────────────────┤ U2(0.25*pi,0.25*pi) ├ ┌─────────────┐└──────────┬──────────┘ q_1: |0>──■──┤ U1(0.25*pi) ├───────────┼─────────── ┌─┴─┐└──────┬──────┘ │ q_2: |0>┤ X ├───────┼──────────────────┼─────────── └───┘ ┌──┴──┐ ┌──┴──┐ c0_0: 0 ═════════╡ = 0 ╞════════════╡ = 0 ╞════════ └─────┘ └─────┘ Previously the blocks collected were : [['cx', 'u1', 'u2']] This is now corrected to : [['cx', 'u1']] """ qasmstr = """ OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; creg c0[1]; cx q[1],q[2]; if(c0==0) u1(0.25*pi) q[1]; if(c0==0) u2(0.25*pi, 0.25*pi) q[0]; """ qc = QuantumCircuit.from_qasm_str(qasmstr) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.run(qc) self.assertEqual( [["cx"]], [[n.name for n in block] for block in pass_manager.property_set["block_list"]]) def test_do_not_merge_conditioned_gates(self): """Validate that classically conditioned gates are never considered for inclusion in a block. Note that there are cases where gates conditioned on the same (register, value) pair could be correctly merged, but this is not yet implemented. ┌────────┐┌────────┐┌────────┐ ┌───┐ qr_0: |0>┤ P(0.1) ├┤ P(0.2) ├┤ P(0.3) ├──■───┤ X ├────■─── └────────┘└───┬────┘└───┬────┘┌─┴─┐ └─┬─┘ ┌─┴─┐ qr_1: |0>──────────────┼─────────┼─────┤ X ├───■────┤ X ├─ │ │ └───┘ │ └─┬─┘ qr_2: |0>──────────────┼─────────┼─────────────┼──────┼─── ┌──┴──┐ ┌──┴──┐ ┌──┴──┐┌──┴──┐ cr_0: 0 ═══════════╡ ╞═══╡ ╞═══════╡ ╞╡ ╞ │ = 0 │ │ = 0 │ │ = 0 ││ = 1 │ cr_1: 0 ═══════════╡ ╞═══╡ ╞═══════╡ ╞╡ ╞ └─────┘ └─────┘ └─────┘└─────┘ Previously the blocks collected were : [['p', 'p', 'p', 'cx', 'cx', 'cx']] This is now corrected to : [['cx']] """ # ref: https://github.com/Qiskit/qiskit-terra/issues/3215 qr = QuantumRegister(3, "qr") cr = ClassicalRegister(2, "cr") qc = QuantumCircuit(qr, cr) qc.p(0.1, 0) qc.p(0.2, 0).c_if(cr, 0) qc.p(0.3, 0).c_if(cr, 0) qc.cx(0, 1) qc.cx(1, 0).c_if(cr, 0) qc.cx(0, 1).c_if(cr, 1) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.run(qc) self.assertEqual( [["cx"]], [[n.name for n in block] for block in pass_manager.property_set["block_list"]]) @unpack @data( (CXGate(), U1Gate(0.1), U2Gate(0.2, 0.3)), (RXXGate(pi / 2), RZGate(0.1), RXGate(pi / 2)), ( Gate("custom2qgate", 2, []), Gate("custom1qgate1", 1, []), Gate("custom1qgate2", 1, []), ), ) def test_collect_arbitrary_gates(self, twoQ_gate, oneQ_gate1, oneQ_gate2): """Validate we can collect blocks irrespective of gate types in the circuit.""" qc = QuantumCircuit(3) # Block 1 - q[0] and q[1] qc.append(oneQ_gate1, [0]) qc.append(oneQ_gate2, [1]) qc.append(twoQ_gate, [0, 1]) qc.append(oneQ_gate1, [0]) qc.append(oneQ_gate2, [1]) # Block 2 - q[1] and q[2] qc.append(oneQ_gate1, [1]) qc.append(oneQ_gate2, [2]) qc.append(twoQ_gate, [1, 2]) qc.append(oneQ_gate1, [1]) qc.append(oneQ_gate2, [2]) # Block 3 - q[0] and q[1] qc.append(oneQ_gate1, [0]) qc.append(oneQ_gate2, [1]) qc.append(twoQ_gate, [0, 1]) qc.append(oneQ_gate1, [0]) qc.append(oneQ_gate2, [1]) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.run(qc) self.assertEqual(len(pass_manager.property_set["block_list"]), 3)