def test_get_instructions_for_qargs(self): with self.assertRaises(KeyError): self.empty_target.operations_for_qargs((0, )) expected = [RZGate(self.theta), IGate(), SXGate(), XGate(), Measure()] res = self.ibm_target.operations_for_qargs((0, )) for gate in expected: self.assertIn(gate, res) expected = [ECRGate()] res = self.fake_backend_target.operations_for_qargs((1, 0)) for gate in expected: self.assertIn(gate, res) expected = [CXGate()] res = self.fake_backend_target.operations_for_qargs((0, 1)) self.assertEqual(expected, res) 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_for_qargs(None))
def _generate_xxyy_test_case(self): """ Generates a random pentuple of values (a_source, b_source), beta, (a_target, b_target) s.t. CAN(a_source, b_source) * exp(-i(s ZI + t IZ)) * CAN(beta) = exp(-i(u ZI + v IZ)) * CAN(a_target, b_target) * exp(-i(x ZI + y IZ)) admits a solution in (s, t, u, v, x, y). Returns (source_coordinate, interaction, target_coordinate). """ source_coordinate = [self.rng.random(), self.rng.random(), 0.0] source_coordinate = [ source_coordinate[0] * np.pi / 8, source_coordinate[1] * source_coordinate[0] * np.pi / 8, 0.0, ] interaction = [self.rng.random() * np.pi / 8] z_angles = [ self.rng.random() * np.pi / 8, self.rng.random() * np.pi / 8 ] prod = (canonical_matrix(*source_coordinate) @ np.kron( RZGate(2 * z_angles[0]).to_matrix(), RZGate(2 * z_angles[1]).to_matrix()) @ canonical_matrix( interaction[0], 0.0, 0.0)) target_coordinate = weyl_coordinates(prod) self.assertAlmostEqual(target_coordinate[-1], 0.0, delta=EPSILON) return source_coordinate, interaction, target_coordinate
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(np.pi / 3, qr[2]) circuit.rz(np.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(np.pi / 3, qr[2]) circuit.t(qr[2]) circuit.x(qr[3]) circuit.rz(np.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 = passmanager.run(circuit) expected = QuantumCircuit(qr) 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, msg=f"expected:\n{expected}\nnew_circuit:\n{new_circuit}")
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_do_not_cancel(self): """Test that parameterized gates do not cancel. This test should be modified when inverse and commutativity checking get improved to handle parameterized gates. """ gate = RZGate(Parameter("Theta")) circuit = QuantumCircuit(1) circuit.append(gate, [0]) circuit.append(gate.inverse(), [0]) passmanager = PassManager(CommutativeInverseCancellation()) new_circuit = passmanager.run(circuit) self.assertEqual(circuit, new_circuit)
def test_is_identity(self): """ The is_identity function determines whether a pair of gates forms the identity, when ignoring control qubits. """ seq = [DAGNode({'type': 'op', 'op': XGate().control()}), DAGNode({'type': 'op', 'op': XGate().control(2)})] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [DAGNode({'type': 'op', 'op': RZGate(-pi/2).control()}), DAGNode({'type': 'op', 'op': RZGate(pi/2).control(2)})] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [DAGNode({'type': 'op', 'op': CSwapGate()}), DAGNode({'type': 'op', 'op': SwapGate()})] self.assertTrue(HoareOptimizer()._is_identity(seq))
def test_is_identity(self): """The is_identity function determines whether a pair of gates forms the identity, when ignoring control qubits. """ seq = [DAGOpNode(op=XGate().control()), DAGOpNode(op=XGate().control(2))] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [ DAGOpNode(op=RZGate(-pi / 2).control()), DAGOpNode(op=RZGate(pi / 2).control(2)), ] self.assertTrue(HoareOptimizer()._is_identity(seq)) seq = [DAGOpNode(op=CSwapGate()), DAGOpNode(op=SwapGate())] self.assertTrue(HoareOptimizer()._is_identity(seq))
def test_grz_equivalence(self): """Test global RZ gate is same as 3 individual RZ gates.""" circuit = GRZ(num_qubits=3, phi=2 * np.pi / 3) expected = QuantumCircuit(3, name="grz") for i in range(3): expected.append(RZGate(phi=2 * np.pi / 3), [i]) self.assertEqual(expected, circuit)
def exp_i(self) -> OperatorBase: """ Return a ``CircuitOp`` equivalent to e^-iH for this operator H. """ # if only one qubit is significant, we can perform the evolution corrected_x = self.primitive.x[::-1] # type: ignore corrected_z = self.primitive.z[::-1] # type: ignore # pylint: disable=import-outside-toplevel,no-member sig_qubits = np.logical_or(corrected_x, corrected_z) if np.sum(sig_qubits) == 0: # e^I is just a global phase, but we can keep track of it! Should we? # For now, just return identity return PauliOp(self.primitive) if np.sum(sig_qubits) == 1: sig_qubit_index = sig_qubits.tolist().index(True) coeff = np.real(self.coeff) \ if not isinstance(self.coeff, ParameterExpression) \ else self.coeff # Y rotation if corrected_x[sig_qubit_index] and corrected_z[sig_qubit_index]: rot_op = PrimitiveOp(RYGate(coeff)) # Z rotation elif corrected_z[sig_qubit_index]: rot_op = PrimitiveOp(RZGate(coeff)) # X rotation elif corrected_x[sig_qubit_index]: rot_op = PrimitiveOp(RXGate(coeff)) from ..operator_globals import I left_pad = I.tensorpower(sig_qubit_index) right_pad = I.tensorpower(self.num_qubits - sig_qubit_index - 1) # Need to use overloaded operators here in case left_pad == I^0 return left_pad ^ rot_op ^ right_pad else: from ..evolutions.evolved_op import EvolvedOp return EvolvedOp(self)
def test_commutative_circuit2(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]---.--- """ qr = QuantumRegister(3, "qr") circuit = QuantumCircuit(qr) circuit.cx(qr[0], qr[1]) circuit.rz(np.pi / 3, qr[2]) circuit.cx(qr[2], qr[1]) circuit.rz(np.pi / 3, qr[2]) circuit.t(qr[2]) circuit.s(qr[2]) circuit.x(qr[1]) circuit.cx(qr[0], qr[1]) circuit.x(qr[1]) passmanager = PassManager() passmanager.append(CommutativeCancellation()) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) 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_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 gate_to_qiskit(gate2): if gate2.name == 'X': return XGate() elif gate2.name == 'Ry': return RYGate(-gate2.arg) elif gate2.name == 'Rz': return RZGate(-gate2.arg) elif gate2.name == 'R1': return U1Gate(gate2.arg) else: raise RuntimeError("Can't implement: %s" % gate2)
def rz_matrix(phi: float) -> np.ndarray: """ Computes an RZ rotation by the angle of ``phi``. Args: phi: rotation angle. Returns: an RZ rotation matrix. """ return RZGate(phi).to_matrix()
def test_decompose_xxyy(self): """ Test that decompose_xxyy_into_xxyy_xx correctly recovers decompositions. """ for _ in range(100): source_coordinate, interaction, target_coordinate = self._generate_xxyy_test_case( ) r, s, u, v, x, y = decompose_xxyy_into_xxyy_xx( target_coordinate[0], target_coordinate[1], source_coordinate[0], source_coordinate[1], interaction[0], ) prod = ( np.kron(RZGate(2 * r).to_matrix(), RZGate(2 * s).to_matrix()) @ canonical_matrix(*source_coordinate) @ np.kron( RZGate(2 * u).to_matrix(), RZGate(2 * v).to_matrix()) @ canonical_matrix( interaction[0], 0.0, 0.0) @ np.kron( RZGate(2 * x).to_matrix(), RZGate(2 * y).to_matrix())) expected = canonical_matrix(*target_coordinate) self.assertTrue(np.all(np.abs(prod - expected) < EPSILON))
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)
class TestParameterCtrlState(QiskitTestCase): """Test gate equality with ctrl_state parameter.""" @data((RXGate(0.5), CRXGate(0.5)), (RYGate(0.5), CRYGate(0.5)), (RZGate(0.5), CRZGate(0.5)), (XGate(), CXGate()), (YGate(), CYGate()), (ZGate(), CZGate()), (U1Gate(0.5), CU1Gate(0.5)), (SwapGate(), CSwapGate()), (HGate(), CHGate()), (U3Gate(0.1, 0.2, 0.3), CU3Gate(0.1, 0.2, 0.3))) @unpack def test_ctrl_state_one(self, gate, controlled_gate): """Test controlled gates with ctrl_state See https://github.com/Qiskit/qiskit-terra/pull/4025 """ self.assertEqual(gate.control(1, ctrl_state='1'), controlled_gate)
def test_parameterized_circuit_for_simulator(self): """Verify that a parameterized circuit can be transpiled for a simulator backend.""" qr = QuantumRegister(2, name='qr') qc = QuantumCircuit(qr) theta = Parameter('theta') qc.rz(theta, qr[0]) transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator')) expected_qc = QuantumCircuit(qr) expected_qc.append(RZGate(theta), [qr[0]]) self.assertEqual(expected_qc, transpiled_qc)
def test_parameter_expression_circuit_for_simulator(self): """Verify that a circuit including expressions of parameters can be transpiled for a simulator backend.""" qr = QuantumRegister(2, name='qr') qc = QuantumCircuit(qr) theta = Parameter('theta') square = theta * theta qc.rz(square, qr[0]) transpiled_qc = transpile(qc, backend=BasicAer.get_backend('qasm_simulator')) expected_qc = QuantumCircuit(qr) expected_qc.append(RZGate(square), [qr[0]]) self.assertEqual(expected_qc, transpiled_qc)
def test_compose_one_liner(self): """Test building a circuit in one line, for fun. """ circ = QuantumCircuit(3) h = HGate() rz = RZGate(0.1) cx = CXGate() ccx = CCXGate() circ = circ.compose(h, [0]).compose(cx, [0, 2]).compose(ccx, [2, 1, 0]).compose(rz, [1]) expected = QuantumCircuit(3) expected.h(0) expected.cx(0, 2) expected.ccx(2, 1, 0) expected.rz(0.1, 1) self.assertEqual(circ, expected)
def test_instruction_schedule_map_ideal_sim_backend(self): ideal_sim_target = Target(num_qubits=3) theta = Parameter("theta") phi = Parameter("phi") lam = Parameter("lambda") for inst in [ UGate(theta, phi, lam), RXGate(theta), RYGate(theta), RZGate(theta), CXGate(), ECRGate(), CCXGate(), Measure(), ]: ideal_sim_target.add_instruction(inst, {None: None}) inst_map = ideal_sim_target.instruction_schedule_map() self.assertEqual(InstructionScheduleMap(), inst_map)
def _define(self): # The y angle matrix stands for the absolute value, while the z angles stand for phases # The difficulty lies in the "global" phases that must be later accounted for in each subspace y_angle_matrix = self._to_angle_matrix_y() z_angle_matrix, global_phases = self._to_angle_matrix_z() # As the subspace phase correction is a very expensive module, we only want to do it if the # z rotation matrix is non-zero! no_z_rotations = abs(sparse.linalg.norm(z_angle_matrix)) < 1e-6 control = QuantumRegister(self.num_controls_qb, "q^c") target = QuantumRegister(self.num_targets_qb, "q^t") qc_y = QuantumCircuit(control, target, name=self.name) qc_z = QuantumCircuit(control, target, name=self.name) for l in range(1, self.num_targets_qb + 1): qargs = list(control) + target[0:l] # If there are no z-rotations we save a lot of gates, so we want to rule that out if not no_z_rotations: angles_z: sparse.spmatrix = z_angle_matrix[range(ControlledStatePreparationGate._chi(l) - 1, ControlledStatePreparationGate._chi(l + 1) - 1), :] angles_z = angles_z.reshape(-1, 1) gate_z = UniformRotationGate(gate=lambda a: RZGate(a), alpha=angles_z.todok()) qc_z.append(gate_z, qargs) qc_z.barrier() # The uniform rotation for the y rotation will take care of the absolute value angles_y: sparse.spmatrix = y_angle_matrix[range(ControlledStatePreparationGate._chi(l) - 1, ControlledStatePreparationGate._chi(l + 1) - 1), :] angles_y = angles_y.reshape(-1, 1) gate_y = UniformRotationGate(gate=lambda a: RYGate(a), alpha=angles_y.todok()) qc_y.append(gate_y, qargs) qc_y.barrier() if not no_z_rotations: # A relative phase correction is pretty intensive: a state preparation on the control global_phase_correction = MöttönenStatePreparationGate( sparse.dok_matrix(np.exp(1.0j * global_phases.T.toarray())), neglect_absolute_value=True ) qc_z.append(global_phase_correction, qargs=control) qc_z.barrier() qc = qc_y + qc_z self._definition = qc
def test_all_gates(self): """Test all gates on 1 and 2 qubits q0:-[H]-[H]--[x]-[x]--[y]-[y]--[rz]-[rz]--[u1]-[u1]-[rx]-[rx]---.--.--.--.--.--.- | | | | | | q1:-------------------------------------------------------------X--X--Y--Y--.--.- = qr0:---[u1]--- qr1:---------- """ qr = QuantumRegister(2, "q") circuit = QuantumCircuit(qr) circuit.h(qr[0]) circuit.h(qr[0]) circuit.x(qr[0]) circuit.x(qr[0]) circuit.y(qr[0]) circuit.y(qr[0]) circuit.rz(0.5, qr[0]) circuit.rz(0.5, qr[0]) circuit.append(U1Gate(0.5), [qr[0]]) # TODO this should work with Phase gates too circuit.append(U1Gate(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]) circuit.cy(qr[0], qr[1]) circuit.cz(qr[0], qr[1]) circuit.cz(qr[0], qr[1]) passmanager = PassManager() passmanager.append(CommutativeCancellation()) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) expected.append(RZGate(2.0), [qr[0]]) expected.rx(1.0, qr[0]) self.assertEqual(expected, new_circuit)
def test_instructions(self): self.assertEqual(self.empty_target.instructions, []) ibm_expected = [ (IGate(), (0, )), (IGate(), (1, )), (IGate(), (2, )), (IGate(), (3, )), (IGate(), (4, )), (RZGate(self.theta), (0, )), (RZGate(self.theta), (1, )), (RZGate(self.theta), (2, )), (RZGate(self.theta), (3, )), (RZGate(self.theta), (4, )), (SXGate(), (0, )), (SXGate(), (1, )), (SXGate(), (2, )), (SXGate(), (3, )), (SXGate(), (4, )), (XGate(), (0, )), (XGate(), (1, )), (XGate(), (2, )), (XGate(), (3, )), (XGate(), (4, )), (CXGate(), (3, 4)), (CXGate(), (4, 3)), (CXGate(), (3, 1)), (CXGate(), (1, 3)), (CXGate(), (1, 2)), (CXGate(), (2, 1)), (CXGate(), (0, 1)), (CXGate(), (1, 0)), (Measure(), (0, )), (Measure(), (1, )), (Measure(), (2, )), (Measure(), (3, )), (Measure(), (4, )), ] self.assertEqual(ibm_expected, self.ibm_target.instructions) ideal_sim_expected = [ (UGate(self.theta, self.phi, self.lam), None), (RXGate(self.theta), None), (RYGate(self.theta), None), (RZGate(self.theta), None), (CXGate(), None), (ECRGate(), None), (CCXGate(), None), (Measure(), None), ] self.assertEqual(ideal_sim_expected, self.ideal_sim_target.instructions)
def setUp(self): super().setUp() self.target = Target() # U gate in qubit 0. self.theta = Parameter("theta") self.phi = Parameter("phi") self.lam = Parameter("lambda") u_props = { (0, ): InstructionProperties(duration=5.23e-8, error=0.00038115), } self.target.add_instruction(UGate(self.theta, self.phi, self.lam), u_props) # Rz gate in qubit 1. rz_props = { (1, ): InstructionProperties(duration=0.0, error=0), } self.target.add_instruction(RZGate(self.phi), rz_props) # X gate in qubit 1. x_props = { (1, ): InstructionProperties(duration=3.5555555555555554e-08, error=0.00020056469709026198), } self.target.add_instruction(XGate(), x_props) # SX gate in qubit 1. sx_props = { (1, ): InstructionProperties(duration=3.5555555555555554e-08, error=0.00020056469709026198), } self.target.add_instruction(SXGate(), sx_props) cx_props = { (0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115), (1, 0): InstructionProperties(duration=4.52e-7, error=0.00132115), } self.target.add_instruction(CXGate(), cx_props)
def test_multiple_gate_error_matrix(self): """Test error matrix ona small target with multiple gets on each qubit generates""" target = Target(num_qubits=3) phi = Parameter("phi") lam = Parameter("lam") theta = Parameter("theta") target.add_instruction( RZGate(phi), {(i, ): InstructionProperties(duration=0, error=0) for i in range(3)}) target.add_instruction( UGate(theta, phi, lam), {(i, ): InstructionProperties(duration=1e-7, error=1e-2) for i in range(3)}, ) cx_props = { (0, 1): InstructionProperties(error=1e-3), (0, 2): InstructionProperties(error=1e-3), (1, 0): InstructionProperties(error=1e-3), (1, 2): InstructionProperties(error=1e-3), (2, 0): InstructionProperties(error=1e-3), (2, 1): InstructionProperties(error=1e-3), } target.add_instruction(CXGate(), cx_props) ecr_props = { (0, 1): InstructionProperties(error=2e-2), (1, 2): InstructionProperties(error=2e-2), (2, 0): InstructionProperties(error=2e-2), } target.add_instruction(ECRGate(), ecr_props) expected_error_matrix = np.array([ [1e-2, 2e-2, 1e-3], [1e-3, 1e-2, 2e-2], [2e-2, 1e-3, 1e-2], ]) np.testing.assert_array_equal(expected_error_matrix, DenseLayout(target=target).error_mat)
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)
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})
def convert( self, operator: CircuitStateFn, params: Optional[Union[ParameterExpression, ParameterVector, List[ParameterExpression]]] = None, ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` for which we compute the QFI. params: The parameters :math:`\omega` with respect to which we are computing the QFI. Returns: A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix element :math:`k, l` of the QFI. Raises: AquaError: If one of the circuits could not be constructed. TypeError: If ``operator`` is an unsupported type. """ # QFI & phase fix observable qfi_observable = ~StateFn(4 * Z ^ (I ^ operator.num_qubits)) phase_fix_observable = ~StateFn((X + 1j * Y) ^ (I ^ operator.num_qubits)) # see https://arxiv.org/pdf/quant-ph/0108146.pdf # Check if the given operator corresponds to a quantum state given as a circuit. if not isinstance(operator, CircuitStateFn): raise TypeError( 'LinCombFull is only compatible with states that are given as CircuitStateFn' ) # If a single parameter is given wrap it into a list. if not isinstance(params, (list, np.ndarray)): params = [params] state_qc = operator.primitive # First, the operators are computed which can compensate for a potential phase-mismatch # between target and trained state, i.e.〈ψ|∂lψ〉 phase_fix_states = None # Add working qubit qr_work = QuantumRegister(1, 'work_qubit') work_q = qr_work[0] additional_qubits: Tuple[List[Qubit], List[Qubit]] = ([work_q], []) for param in params: # Get the gates of the given quantum state which are parameterized by param param_gates = state_qc._parameter_table[param] # Loop through the occurrences of param in the quantum state for m, param_occurence in enumerate(param_gates): # Get the coefficients and gates for the linear combination gradient for each # occurrence, see e.g. https://arxiv.org/abs/2006.06004 coeffs_i, gates_i = LinComb._gate_gradient_dict( param_occurence[0])[param_occurence[1]] # Construct the quantum states which are then evaluated for the respective QFI # element. for k, gate_to_insert_i in enumerate(gates_i): grad_state = state_qc.copy() grad_state.add_register(qr_work) # apply Hadamard on work_q LinComb.insert_gate(grad_state, param_occurence[0], HGate(), qubits=[work_q]) # Fix work_q phase such that the gradient is correct. coeff_i = coeffs_i[k] sign = np.sign(coeff_i) is_complex = np.iscomplex(coeff_i) if sign == -1: if is_complex: LinComb.insert_gate(grad_state, param_occurence[0], SdgGate(), qubits=[work_q]) else: LinComb.insert_gate(grad_state, param_occurence[0], ZGate(), qubits=[work_q]) else: if is_complex: LinComb.insert_gate(grad_state, param_occurence[0], SGate(), qubits=[work_q]) # Insert controlled, intercepting gate - controlled by |0> if isinstance(param_occurence[0], UGate): if param_occurence[1] == 0: LinComb.insert_gate( grad_state, param_occurence[0], RZGate(param_occurence[0].params[2])) LinComb.insert_gate(grad_state, param_occurence[0], RXGate(np.pi / 2)) LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, additional_qubits=additional_qubits) LinComb.insert_gate(grad_state, param_occurence[0], RXGate(-np.pi / 2)) LinComb.insert_gate( grad_state, param_occurence[0], RZGate(-param_occurence[0].params[2])) elif param_occurence[1] == 1: LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, after=True, additional_qubits=additional_qubits) else: LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, additional_qubits=additional_qubits) else: LinComb.insert_gate( grad_state, param_occurence[0], gate_to_insert_i, additional_qubits=additional_qubits) # Remove unnecessary gates. grad_state = self.trim_circuit(grad_state, param_occurence[0]) # Apply the final Hadamard on the working qubit. grad_state.h(work_q) # Add the coefficient needed for the gradient as well as the original # coefficient of the given quantum state. state = np.sqrt(np.abs( coeff_i)) * operator.coeff * CircuitStateFn(grad_state) # Check if the gate parameter corresponding to param is a parameter expression gate_param = param_occurence[0].params[param_occurence[1]] if gate_param == param: state = phase_fix_observable @ state else: # If the gate parameter is a parameter expressions the chain rule needs # to be taken into account. if isinstance(gate_param, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param, param) state = (expr_grad * phase_fix_observable) @ state else: state *= 0 if m == 0 and k == 0: phase_fix_state = state else: # Take the product rule into account phase_fix_state += state # Create a list for the phase fix states if not phase_fix_states: phase_fix_states = [phase_fix_state] else: phase_fix_states += [phase_fix_state] # Get 4 * Re[〈∂kψ|∂lψ] qfi_operators = [] # Add a working qubit qr_work_qubit = QuantumRegister(1, 'work_qubit') work_qubit = qr_work_qubit[0] additional_qubits = ([work_qubit], []) # create a copy of the original circuit with an additional work_qubit register circuit = state_qc.copy() circuit.add_register(qr_work_qubit) # Apply a Hadamard on the working qubit LinComb.insert_gate(circuit, state_qc._parameter_table[params[0]][0][0], HGate(), qubits=[work_qubit]) # Get the circuits needed to compute〈∂iψ|∂jψ〉 for i, param_i in enumerate(params): # loop over parameters qfi_ops = None for j, param_j in enumerate(params): # Get the gates of the quantum state which are parameterized by param_i param_gates_i = state_qc._parameter_table[param_i] for m_i, param_occurence_i in enumerate(param_gates_i): coeffs_i, gates_i = LinComb._gate_gradient_dict( param_occurence_i[0])[param_occurence_i[1]] for k_i, gate_to_insert_i in enumerate(gates_i): coeff_i = coeffs_i[k_i] # Get the gates of the quantum state which are parameterized by param_j param_gates_j = state_qc._parameter_table[param_j] for m_j, param_occurence_j in enumerate(param_gates_j): coeffs_j, gates_j = \ LinComb._gate_gradient_dict(param_occurence_j[0])[ param_occurence_j[1]] for k_j, gate_to_insert_j in enumerate(gates_j): coeff_j = coeffs_j[k_j] # create a copy of the original circuit with the same registers qfi_circuit = QuantumCircuit(*circuit.qregs) qfi_circuit.data = circuit.data # Correct the phase of the working qubit according to coefficient i # and coefficient j sign = np.sign(np.conj(coeff_i) * coeff_j) is_complex = np.iscomplex( np.conj(coeff_i) * coeff_j) if sign == -1: if is_complex: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], SdgGate(), qubits=[work_qubit]) else: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], ZGate(), qubits=[work_qubit]) else: if is_complex: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], SGate(), qubits=[work_qubit]) LinComb.insert_gate(qfi_circuit, param_occurence_i[0], XGate(), qubits=[work_qubit]) # Insert controlled, intercepting gate i - controlled by |1> if isinstance(param_occurence_i[0], UGate): if param_occurence_i[1] == 0: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RZGate(param_occurence_i[0]. params[2])) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RXGate(np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, additional_qubits=additional_qubits ) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RXGate(-np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_i[0], RZGate(-param_occurence_i[0]. params[2])) elif param_occurence_i[1] == 1: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, after=True, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_i[0], gate_to_insert_i, additional_qubits=additional_qubits) LinComb.insert_gate(qfi_circuit, gate_to_insert_i, XGate(), qubits=[work_qubit], after=True) # Insert controlled, intercepting gate j - controlled by |0> if isinstance(param_occurence_j[0], UGate): if param_occurence_j[1] == 0: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RZGate(param_occurence_j[0]. params[2])) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RXGate(np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, additional_qubits=additional_qubits ) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RXGate(-np.pi / 2)) LinComb.insert_gate( qfi_circuit, param_occurence_j[0], RZGate(-param_occurence_j[0]. params[2])) elif param_occurence_j[1] == 1: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, after=True, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, additional_qubits=additional_qubits ) else: LinComb.insert_gate( qfi_circuit, param_occurence_j[0], gate_to_insert_j, additional_qubits=additional_qubits) # Remove redundant gates if j <= i: qfi_circuit = self.trim_circuit( qfi_circuit, param_occurence_i[0]) else: qfi_circuit = self.trim_circuit( qfi_circuit, param_occurence_j[0]) # Apply final Hadamard gate qfi_circuit.h(work_qubit) # Convert the quantum circuit into a CircuitStateFn and add the # coefficients i, j and the original operator coefficient term = np.sqrt( np.abs(coeff_i) * np.abs(coeff_j)) * operator.coeff term = term * CircuitStateFn(qfi_circuit) # Check if the gate parameters i and j are parameter expressions gate_param_i = param_occurence_i[0].params[ param_occurence_i[1]] gate_param_j = param_occurence_j[0].params[ param_occurence_j[1]] meas = deepcopy(qfi_observable) # If the gate parameter i is a parameter expression use the chain # rule. if isinstance(gate_param_i, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_i, param_i) meas *= expr_grad # If the gate parameter j is a parameter expression use the chain # rule. if isinstance(gate_param_j, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_j, param_j) meas *= expr_grad term = meas @ term if m_i == 0 and k_i == 0 and m_j == 0 and k_j == 0: qfi_op = term else: # Product Rule qfi_op += term # Compute −4 * Re(〈∂kψ|ψ〉〈ψ|∂lψ〉) def phase_fix_combo_fn(x): return 4 * (-0.5) * (x[0] * np.conjugate(x[1]) + x[1] * np.conjugate(x[0])) phase_fix = ListOp([phase_fix_states[i], phase_fix_states[j]], combo_fn=phase_fix_combo_fn) # Add the phase fix quantities to the entries of the QFI # Get 4 * Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] if not qfi_ops: qfi_ops = [qfi_op + phase_fix] else: qfi_ops += [qfi_op + phase_fix] qfi_operators.append(ListOp(qfi_ops)) # Return the full QFI return ListOp(qfi_operators)
def _bogoliubov_transform_num_conserving_jw( # pylint: disable=invalid-name register: QuantumRegister, transformation_matrix: np.ndarray) -> Iterator[ tuple[Gate, tuple[Qubit, ...]]]: n, _ = transformation_matrix.shape current_matrix = transformation_matrix left_rotations = [] right_rotations = [] # compute left and right Givens rotations for i in range(n - 1): if i % 2 == 0: # rotate columns by right multiplication for j in range(i + 1): target_index = i - j row = n - j - 1 if not np.isclose(current_matrix[row, target_index], 0.0): # zero out element at target index in given row givens_mat = givens_matrix( current_matrix[row, target_index + 1], current_matrix[row, target_index], ) right_rotations.append( (givens_mat, (target_index + 1, target_index))) current_matrix = apply_matrix_to_slices( current_matrix, givens_mat, [(Ellipsis, target_index + 1), (Ellipsis, target_index)], ) else: # rotate rows by left multiplication for j in range(i + 1): target_index = n - i + j - 1 col = j if not np.isclose(current_matrix[target_index, col], 0.0): # zero out element at target index in given column givens_mat = givens_matrix( current_matrix[target_index - 1, col], current_matrix[target_index, col], ) left_rotations.append( (givens_mat, (target_index - 1, target_index))) current_matrix = apply_matrix_to_slices( current_matrix, givens_mat, [target_index - 1, target_index]) # convert left rotations to right rotations for givens_mat, (i, j) in reversed(left_rotations): givens_mat = givens_mat.T.conj() givens_mat[:, 0] *= current_matrix[i, i] givens_mat[:, 1] *= current_matrix[j, j] new_givens_mat = givens_matrix(givens_mat[1, 1], givens_mat[1, 0]) right_rotations.append((new_givens_mat.T, (i, j))) phase_matrix = givens_mat @ new_givens_mat current_matrix[i, i] = phase_matrix[0, 0] current_matrix[j, j] = phase_matrix[1, 1] # yield operations for i in range(n): phi = np.angle(current_matrix[i, i]) yield RZGate(phi), (register[i], ) for givens_mat, (i, j) in reversed(right_rotations): theta = np.arccos(np.real(givens_mat[0, 0])) phi = np.angle(givens_mat[0, 1]) yield XXPlusYYGate(2 * theta, phi - np.pi / 2), (register[j], register[i])
def test_controlled_rz(self): """Test the creation of a controlled RZ gate.""" theta = 0.5 self.assertEqual(RZGate(theta).control(), CRZGate(theta))