def test_basic_gate_inverse(self): """Test that a basic pair of gate inverse can be cancelled.""" qc = QuantumCircuit(2, 2) qc.rx(np.pi / 4, 0) qc.rx(-np.pi / 4, 0) pass_ = InverseCancellation([(RXGate(np.pi / 4), RXGate(-np.pi / 4))]) pm = PassManager(pass_) new_circ = pm.run(qc) gates_after = new_circ.count_ops() self.assertNotIn("rx", gates_after)
def test_get_instruction_from_name(self): with self.assertRaises(KeyError): self.empty_target.operation_from_name("measure") self.assertEqual(self.ibm_target.operation_from_name("measure"), Measure()) self.assertEqual(self.fake_backend_target.operation_from_name("rx_30"), RXGate(math.pi / 6)) self.assertEqual( self.fake_backend_target.operation_from_name("rx"), RXGate(self.fake_backend._theta), ) self.assertEqual(self.ideal_sim_target.operation_from_name("ccx"), CCXGate())
def test_non_inverse_do_not_cancel(self): """Test that non-inverse gate pairs do not cancel.""" qc = QuantumCircuit(2, 2) qc.rx(np.pi / 4, 0) qc.rx(np.pi / 4, 0) pass_ = InverseCancellation([(RXGate(np.pi / 4), RXGate(-np.pi / 4))]) pm = PassManager(pass_) new_circ = pm.run(qc) gates_after = new_circ.count_ops() self.assertIn("rx", gates_after) self.assertEqual(gates_after["rx"], 2)
def test_insert_midmeas_hahn_asap(self): """Test a single X gate as Hahn echo can absorb in the upstream circuit. ┌──────────────────┐ ┌────────────────┐┌─────────┐» q_0: ────────■─────────┤ U(3π/4,-π/2,π/2) ├─┤ Delay(600[dt]) ├┤ Rx(π/4) ├» ┌─┴─┐ └──────────────────┘┌┴────────────────┤└─────────┘» q_1: ──────┤ X ├────────────────■──────────┤ Delay(1000[dt]) ├─────■─────» ┌─────┴───┴──────┐ ┌─┴─┐ └───────┬─┬───────┘ ┌─┴─┐ » q_2: ┤ Delay(700[dt]) ├───────┤ X ├────────────────┤M├───────────┤ X ├───» └────────────────┘ └───┘ └╥┘ └───┘ » c: 1/═══════════════════════════════════════════════╩════════════════════» 0 » « ┌────────────────┐ «q_0: ┤ Delay(600[dt]) ├──■── « └────────────────┘┌─┴─┐ «q_1: ──────────────────┤ X ├ « ┌────────────────┐└───┘ «q_2: ┤ Delay(700[dt]) ├───── « └────────────────┘ «c: 1/═══════════════════════ « """ dd_sequence = [RXGate(pi / 4)] pm = PassManager([ ASAPScheduleAnalysis(self.durations), PadDynamicalDecoupling(self.durations, dd_sequence), ]) midmeas_dd = pm.run(self.midmeas) combined_u = UGate(3 * pi / 4, -pi / 2, pi / 2) expected = QuantumCircuit(3, 1) expected.cx(0, 1) expected.compose(combined_u, [0], inplace=True) expected.delay(600, 0) expected.rx(pi / 4, 0) expected.delay(600, 0) expected.delay(700, 2) expected.cx(1, 2) expected.delay(1000, 1) expected.measure(2, 0) expected.cx(1, 2) expected.cx(0, 1) expected.delay(700, 2) self.assertEqual(midmeas_dd, expected) # check the absorption into U was done correctly self.assertTrue( Operator(XGate()).equiv( Operator(UGate(3 * pi / 4, -pi / 2, pi / 2)) & Operator(RXGate(pi / 4))))
def test_for_loop_invalid_params_setter(self): """Verify we catch invalid param settings for ForLoopOp.""" body = QuantumCircuit(3, 1) loop_parameter = Parameter("foo") indexset = range(0, 10, 2) body.rx(loop_parameter, 0) op = ForLoopOp(indexset, loop_parameter, body) with self.assertWarnsRegex(UserWarning, r"loop_parameter was not found"): op.params = [indexset, Parameter("foo"), body] with self.assertRaisesRegex(CircuitError, r"to be of type QuantumCircuit"): op.params = [indexset, loop_parameter, RXGate(loop_parameter)] bad_body = QuantumCircuit(2, 1) with self.assertRaisesRegex( CircuitError, r"num_clbits different than that of the ForLoopOp"): op.params = [indexset, loop_parameter, bad_body] with self.assertRaisesRegex(CircuitError, r"to be either of type Parameter or None"): _ = ForLoopOp(indexset, "foo", body)
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_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 test_non_gate_inverse_raise_error(self): """Test that non-inverse gate inputs raise an error.""" qc = QuantumCircuit(2, 2) qc.rx(np.pi / 4, 0) qc.rx(np.pi / 4, 0) with self.assertRaises(TranspilerError): InverseCancellation([(RXGate(np.pi / 4))])
def test_calibrations_basis_gates(self): """Check if the calibrations for basis gates provided are added correctly.""" circ = QuantumCircuit(2) with pulse.build() as q0_x180: pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0)) with pulse.build() as q1_y90: pulse.play(pulse.library.Gaussian(20, -1.0, 3.0), pulse.DriveChannel(1)) # Add calibration circ.add_calibration(RXGate(3.14), [0], q0_x180) circ.add_calibration(RYGate(1.57), [1], q1_y90) self.assertEqual(set(circ.calibrations.keys()), {"rx", "ry"}) self.assertEqual(set(circ.calibrations["rx"].keys()), {((0, ), (3.14, ))}) self.assertEqual(set(circ.calibrations["ry"].keys()), {((1, ), (1.57, ))}) self.assertEqual( circ.calibrations["rx"][((0, ), (3.14, ))].instructions, q0_x180.instructions) self.assertEqual( circ.calibrations["ry"][((1, ), (1.57, ))].instructions, q1_y90.instructions)
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 test_multi_circuit_uncommon_calibrations(self): """Test that disassembler parses uncommon calibrations (stored at QOBJ experiment-level).""" with pulse.build() as sched: pulse.play(pulse.library.Drag(50, 0.15, 4, 2), pulse.DriveChannel(0)) qc_0 = QuantumCircuit(2) qc_0.h(0) qc_0.append(RXGate(np.pi), [1]) qc_0.add_calibration("h", [0], sched) qc_0.add_calibration(RXGate(np.pi), [1], sched) qc_1 = QuantumCircuit(2) qc_1.h(0) circuits = [qc_0, qc_1] qobj = assemble(circuits, FakeOpenPulse2Q()) output_circuits, _, _ = disassemble(qobj) self.assertCircuitCalibrationsEqual(circuits, output_circuits)
def rx_matrix(phi: float) -> np.ndarray: """ Computes an RX rotation by the angle of ``phi``. Args: phi: rotation angle. Returns: an RX rotation matrix. """ return RXGate(phi).to_matrix()
def test_multi_controlled_rotation_gate_matrices(self, num_controls, base_gate_name, use_basis_gates): """Test the multi controlled rotation gates without ancillas. Based on the test moved here from Aqua: https://github.com/Qiskit/qiskit-aqua/blob/769ca8f/test/aqua/test_mcr.py """ q_controls = QuantumRegister(num_controls) q_target = QuantumRegister(1) # iterate over all possible combinations of control qubits for ctrl_state in range(2**num_controls): bitstr = bin(ctrl_state)[2:].zfill(num_controls)[::-1] theta = 0.871236 * pi qc = QuantumCircuit(q_controls, q_target) for idx, bit in enumerate(bitstr): if bit == '0': qc.x(q_controls[idx]) # call mcrx/mcry/mcrz if base_gate_name == 'y': qc.mcry(theta, q_controls, q_target[0], None, mode='noancilla', use_basis_gates=use_basis_gates) else: # case 'x' or 'z' only support the noancilla mode and do not have this keyword getattr(qc, 'mcr' + base_gate_name)( theta, q_controls, q_target[0], use_basis_gates=use_basis_gates) for idx, bit in enumerate(bitstr): if bit == '0': qc.x(q_controls[idx]) backend = BasicAer.get_backend('unitary_simulator') simulated = execute(qc, backend).result().get_unitary(qc) if base_gate_name == 'x': rot_mat = RXGate(theta).to_matrix() elif base_gate_name == 'y': rot_mat = RYGate(theta).to_matrix() else: # case 'z' rot_mat = U1Gate(theta).to_matrix() expected = _compute_control_matrix(rot_mat, num_controls, ctrl_state=ctrl_state) with self.subTest(msg='control state = {}'.format(ctrl_state)): self.assertTrue(matrix_equal(simulated, expected))
def test_param_gate_instance(self): """Verify that the same Parameter gate instance is not being used in multiple circuits.""" a, b = Parameter("a"), Parameter("b") rx = RXGate(a) qc0, qc1 = QuantumCircuit(1), QuantumCircuit(1) qc0.append(rx, [0]) qc1.append(rx, [0]) qc0.assign_parameters({a: b}, inplace=True) qc0_instance = next(iter(qc0._parameter_table[b]))[0] qc1_instance = next(iter(qc1._parameter_table[a]))[0] self.assertNotEqual(qc0_instance, qc1_instance)
def test_hadamard_to_rot_gates(self): """Test a transpilation from H to Rx, Ry gates""" qr = QuantumRegister(1) qc = QuantumCircuit(qr) qc.h(0) expected = QuantumCircuit(qr, global_phase=np.pi / 2) expected.append(RYGate(theta=np.pi / 2), [0]) expected.append(RXGate(theta=np.pi), [0]) circuit = transpile(qc, basis_gates=['rx', 'ry'], optimization_level=0) self.assertEqual(circuit, expected)
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_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 test_for_loop_invalid_instantiation(self): """Verify we catch invalid instantiations of ForLoopOp.""" body = QuantumCircuit(3, 1) loop_parameter = Parameter("foo") indexset = range(0, 10, 2) body.rx(loop_parameter, 0) with self.assertWarnsRegex(UserWarning, r"loop_parameter was not found"): _ = ForLoopOp(indexset, Parameter("foo"), body) with self.assertRaisesRegex(CircuitError, r"to be of type QuantumCircuit"): _ = ForLoopOp(indexset, loop_parameter, RXGate(loop_parameter))
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 RXDataset(angle_step=10, probability_step=10, shots=1024, save_dir=None): # define constants basis_gates = ['u3'] simulator = QasmSimulator() df = pd.DataFrame() # generate circuits for angle in np.linspace(0, np.pi, angle_step, endpoint=True): # define circuit circ = QuantumCircuit(1, 1) rotate = RXGate(angle) circ.append(rotate, [0]) circ.measure(0, 0) new_circ = qiskit.compiler.transpile(circ, basis_gates=basis_gates, optimization_level=0) print(new_circ) # generate noise models: for probability in np.linspace(0, 1, probability_step, endpoint=True): # add noise noise_model = NoiseModel() error = depolarizing_error(probability, 1) noise_model.add_all_qubit_quantum_error(error, ['x', 'u1', 'u2', 'u3']) # execution - Noisy job = execute(new_circ, simulator, shots=shots, noise_model=noise_model) result = job.result() # add to Pandas DF data = { 'rx_theta': angle, 'p': probability, 'E': result.get_counts(0).get('0', 0) / shots } df = df.append(data, ignore_index=True) df = df[['rx_theta', 'E', 'p']] df.to_csv(save_dir + "/dataframe_RX.csv") return df
def test_single_circuit_calibrations(self): """Test that disassembler parses single circuit QOBJ calibrations (from QOBJ-level).""" theta = Parameter("theta") qc = QuantumCircuit(2) qc.h(0) qc.rx(np.pi, 0) qc.rx(theta, 1) qc = qc.assign_parameters({theta: np.pi}) with pulse.build() as h_sched: pulse.play(pulse.library.Drag(1, 0.15, 4, 2), pulse.DriveChannel(0)) with pulse.build() as x180: pulse.play(pulse.library.Gaussian(1, 0.2, 5), pulse.DriveChannel(0)) qc.add_calibration("h", [0], h_sched) qc.add_calibration(RXGate(np.pi), [0], x180) qobj = assemble(qc, FakeOpenPulse2Q()) output_circuits, _, _ = disassemble(qobj) self.assertCircuitCalibrationsEqual([qc], output_circuits)
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)
class TestTwoLocal(QiskitTestCase): """Tests for the TwoLocal circuit.""" def assertCircuitEqual(self, qc1, qc2, visual=False, transpiled=True): """An equality test specialized to circuits.""" if transpiled: basis_gates = ['id', 'u1', 'u3', 'cx'] qc1_transpiled = transpile(qc1, basis_gates=basis_gates, optimization_level=0) qc2_transpiled = transpile(qc2, basis_gates=basis_gates, optimization_level=0) qc1, qc2 = qc1_transpiled, qc2_transpiled if visual: self.assertEqual(qc1.draw(), qc2.draw()) else: self.assertEqual(qc1, qc2) def test_skip_final_rotation_layer(self): """Test skipping the final rotation layer works.""" two = TwoLocal(3, ['ry', 'h'], ['cz', 'cx'], reps=2, skip_final_rotation_layer=True) self.assertEqual(two.num_parameters, 6) # would be 9 with a final rotation layer @data((5, 'rx', 'cx', 'full', 2, 15), (3, 'x', 'z', 'linear', 1, 0), (3, ['rx', 'ry'], ['cry', 'cx'], 'circular', 2, 24), ) @unpack def test_num_parameters(self, num_qubits, rot, ent, ent_mode, reps, expected): """Test the number of parameters.""" two = TwoLocal(num_qubits, rotation_blocks=rot, entanglement_blocks=ent, entanglement=ent_mode, reps=reps) with self.subTest(msg='num_parameters_settable'): self.assertEqual(two.num_parameters_settable, expected) with self.subTest(msg='num_parameters'): self.assertEqual(two.num_parameters, expected) def test_empty_two_local(self): """Test the setup of an empty two-local circuit.""" two = TwoLocal() with self.subTest(msg='0 qubits'): self.assertEqual(two.num_qubits, 0) with self.subTest(msg='no blocks are set'): self.assertListEqual(two.rotation_blocks, []) self.assertListEqual(two.entanglement_blocks, []) with self.subTest(msg='equal to empty circuit'): self.assertEqual(two, QuantumCircuit()) @data('rx', RXGate(Parameter('p')), RXGate, 'circuit') def test_various_block_types(self, rot): """Test setting the rotation blocks to various type and assert the output type is RX.""" if rot == 'circuit': rot = QuantumCircuit(1) rot.rx(Parameter('angle'), 0) two = TwoLocal(3, rot, 'cz', reps=1) self.assertEqual(len(two.rotation_blocks), 1) rotation = two.rotation_blocks[0] # decompose self.assertIsInstance(rotation.data[0][0], RXGate) def test_parameter_setters(self): """Test different possibilities to set parameters.""" two = TwoLocal(3, rotation_blocks='rx', entanglement='cz', reps=2) params = [0, 1, 2, Parameter('x'), Parameter('y'), Parameter('z'), 6, 7, 0] params_set = set(param for param in params if isinstance(param, Parameter)) with self.subTest(msg='dict assign and copy'): ordered = two.ordered_parameters bound = two.assign_parameters(dict(zip(ordered, params)), inplace=False) self.assertEqual(bound.parameters, params_set) self.assertEqual(two.num_parameters, 9) with self.subTest(msg='list assign and copy'): ordered = two.ordered_parameters bound = two.assign_parameters(params, inplace=False) self.assertEqual(bound.parameters, params_set) self.assertEqual(two.num_parameters, 9) with self.subTest(msg='list assign inplace'): ordered = two.ordered_parameters two.assign_parameters(params, inplace=True) self.assertEqual(two.parameters, params_set) self.assertEqual(two.num_parameters, 3) self.assertEqual(two.num_parameters_settable, 9) def test_parameters_settable_is_constant(self): """Test the attribute num_parameters_settable does not change on parameter change.""" two = TwoLocal(3, rotation_blocks='rx', entanglement='cz', reps=2) ordered_params = two.ordered_parameters x = Parameter('x') two.assign_parameters(dict(zip(ordered_params, [x] * two.num_parameters)), inplace=True) with self.subTest(msg='num_parameters collapsed to 1'): self.assertEqual(two.num_parameters, 1) with self.subTest(msg='num_parameters_settable remained constant'): self.assertEqual(two.num_parameters_settable, len(ordered_params)) def test_compose_inplace_to_circuit(self): """Test adding a two-local to an existing circuit.""" two = TwoLocal(3, ['ry', 'rz'], 'cz', 'full', reps=1, insert_barriers=True) circuit = QuantumCircuit(3) circuit.compose(two, inplace=True) reference = QuantumCircuit(3) param_iter = iter(two.ordered_parameters) for i in range(3): reference.ry(next(param_iter), i) for i in range(3): reference.rz(next(param_iter), i) reference.barrier() reference.cz(0, 1) reference.cz(0, 2) reference.cz(1, 2) reference.barrier() for i in range(3): reference.ry(next(param_iter), i) for i in range(3): reference.rz(next(param_iter), i) self.assertCircuitEqual(circuit, reference) def test_composing_two(self): """Test adding two two-local circuits.""" entangler_map = [[0, 3], [0, 2]] two = TwoLocal(4, [], 'cry', entangler_map, reps=1) circuit = two.compose(two) reference = QuantumCircuit(4) params = two.ordered_parameters for _ in range(2): reference.cry(params[0], 0, 3) reference.cry(params[1], 0, 2) self.assertCircuitEqual(reference, circuit) def test_ry_blocks(self): """Test that the RealAmplitudes circuit is instantiated correctly.""" two = RealAmplitudes(4) with self.subTest(msg='test rotation gate'): self.assertEqual(len(two.rotation_blocks), 1) self.assertIsInstance(two.rotation_blocks[0].data[0][0], RYGate) with self.subTest(msg='test parameter bounds'): expected = [(-np.pi, np.pi)] * two.num_parameters np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_ry_circuit(self): """Test an RealAmplitudes circuit.""" num_qubits = 3 reps = 2 entanglement = 'full' parameters = ParameterVector('theta', num_qubits * (reps + 1)) param_iter = iter(parameters) expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.ry(next(param_iter), i) expected.cx(0, 1) expected.cx(0, 2) expected.cx(1, 2) for i in range(num_qubits): expected.ry(next(param_iter), i) library = RealAmplitudes(num_qubits, reps=reps, entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_ryrz_blocks(self): """Test that the EfficientSU2 circuit is instantiated correctly.""" two = EfficientSU2(3) with self.subTest(msg='test rotation gate'): self.assertEqual(len(two.rotation_blocks), 2) self.assertIsInstance(two.rotation_blocks[0].data[0][0], RYGate) self.assertIsInstance(two.rotation_blocks[1].data[0][0], RZGate) with self.subTest(msg='test parameter bounds'): expected = [(-np.pi, np.pi)] * two.num_parameters np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_ryrz_circuit(self): """Test an EfficientSU2 circuit.""" num_qubits = 3 reps = 2 entanglement = 'circular' parameters = ParameterVector('theta', 2 * num_qubits * (reps + 1)) param_iter = iter(parameters) expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.ry(next(param_iter), i) for i in range(num_qubits): expected.rz(next(param_iter), i) expected.cx(2, 0) expected.cx(0, 1) expected.cx(1, 2) for i in range(num_qubits): expected.ry(next(param_iter), i) for i in range(num_qubits): expected.rz(next(param_iter), i) library = EfficientSU2(num_qubits, reps=reps, entanglement=entanglement).assign_parameters( parameters ) self.assertCircuitEqual(library, expected) def test_swaprz_blocks(self): """Test that the ExcitationPreserving circuit is instantiated correctly.""" two = ExcitationPreserving(5) with self.subTest(msg='test rotation gate'): self.assertEqual(len(two.rotation_blocks), 1) self.assertIsInstance(two.rotation_blocks[0].data[0][0], RZGate) with self.subTest(msg='test entanglement gate'): self.assertEqual(len(two.entanglement_blocks), 1) block = two.entanglement_blocks[0] self.assertEqual(len(block.data), 2) self.assertIsInstance(block.data[0][0], RXXGate) self.assertIsInstance(block.data[1][0], RYYGate) with self.subTest(msg='test parameter bounds'): expected = [(-np.pi, np.pi)] * two.num_parameters np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_swaprz_circuit(self): """Test a ExcitationPreserving circuit in iswap mode.""" num_qubits = 3 reps = 2 entanglement = 'linear' parameters = ParameterVector('theta', num_qubits * (reps + 1) + reps * (num_qubits - 1)) param_iter = iter(parameters) expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.rz(next(param_iter), i) shared_param = next(param_iter) expected.rxx(shared_param, 0, 1) expected.ryy(shared_param, 0, 1) shared_param = next(param_iter) expected.rxx(shared_param, 1, 2) expected.ryy(shared_param, 1, 2) for i in range(num_qubits): expected.rz(next(param_iter), i) library = ExcitationPreserving(num_qubits, reps=reps, entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_fsim_circuit(self): """Test a ExcitationPreserving circuit in fsim mode.""" num_qubits = 3 reps = 2 entanglement = 'linear' # need the parameters in the entanglement blocks to be the same because the order # can get mixed up in ExcitationPreserving (since parameters are not ordered in circuits) parameters = [1] * (num_qubits * (reps + 1) + reps * (1 + num_qubits)) param_iter = iter(parameters) expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.rz(next(param_iter), i) shared_param = next(param_iter) expected.rxx(shared_param, 0, 1) expected.ryy(shared_param, 0, 1) expected.cp(next(param_iter), 0, 1) shared_param = next(param_iter) expected.rxx(shared_param, 1, 2) expected.ryy(shared_param, 1, 2) expected.cp(next(param_iter), 1, 2) for i in range(num_qubits): expected.rz(next(param_iter), i) library = ExcitationPreserving(num_qubits, reps=reps, mode='fsim', entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_circular_on_same_block_and_circuit_size(self): """Test circular entanglement works correctly if the circuit and block sizes match.""" two = TwoLocal(2, 'ry', 'cx', entanglement='circular', reps=1) parameters = np.arange(two.num_parameters) ref = QuantumCircuit(2) ref.ry(parameters[0], 0) ref.ry(parameters[1], 1) ref.cx(0, 1) ref.ry(parameters[2], 0) ref.ry(parameters[3], 1) self.assertCircuitEqual(two.assign_parameters(parameters), ref)
class TestTwoLocal(QiskitTestCase): """Tests for the TwoLocal circuit.""" def assertCircuitEqual(self, qc1, qc2, visual=False, transpiled=True): """An equality test specialized to circuits.""" if transpiled: basis_gates = ["id", "u1", "u3", "cx"] qc1_transpiled = transpile(qc1, basis_gates=basis_gates, optimization_level=0) qc2_transpiled = transpile(qc2, basis_gates=basis_gates, optimization_level=0) qc1, qc2 = qc1_transpiled, qc2_transpiled if visual: self.assertEqual(qc1.draw(), qc2.draw()) else: self.assertEqual(qc1, qc2) def test_skip_final_rotation_layer(self): """Test skipping the final rotation layer works.""" two = TwoLocal(3, ["ry", "h"], ["cz", "cx"], reps=2, skip_final_rotation_layer=True) self.assertEqual(two.num_parameters, 6) # would be 9 with a final rotation layer @data( (5, "rx", "cx", "full", 2, 15), (3, "x", "z", "linear", 1, 0), (3, "rx", "cz", "linear", 0, 3), (3, ["rx", "ry"], ["cry", "cx"], "circular", 2, 24), ) @unpack def test_num_parameters(self, num_qubits, rot, ent, ent_mode, reps, expected): """Test the number of parameters.""" two = TwoLocal( num_qubits, rotation_blocks=rot, entanglement_blocks=ent, entanglement=ent_mode, reps=reps, ) with self.subTest(msg="num_parameters_settable"): self.assertEqual(two.num_parameters_settable, expected) with self.subTest(msg="num_parameters"): self.assertEqual(two.num_parameters, expected) def test_empty_two_local(self): """Test the setup of an empty two-local circuit.""" two = TwoLocal() with self.subTest(msg="0 qubits"): self.assertEqual(two.num_qubits, 0) with self.subTest(msg="no blocks are set"): self.assertListEqual(two.rotation_blocks, []) self.assertListEqual(two.entanglement_blocks, []) with self.subTest(msg="equal to empty circuit"): self.assertEqual(two, QuantumCircuit()) @data("rx", RXGate(Parameter("p")), RXGate, "circuit") def test_various_block_types(self, rot): """Test setting the rotation blocks to various type and assert the output type is RX.""" if rot == "circuit": rot = QuantumCircuit(1) rot.rx(Parameter("angle"), 0) two = TwoLocal(3, rot, reps=0) self.assertEqual(len(two.rotation_blocks), 1) rotation = two.rotation_blocks[0] # decompose self.assertIsInstance(rotation.data[0].operation, RXGate) def test_parameter_setters(self): """Test different possibilities to set parameters.""" two = TwoLocal(3, rotation_blocks="rx", entanglement="cz", reps=2) params = [ 0, 1, 2, Parameter("x"), Parameter("y"), Parameter("z"), 6, 7, 0 ] params_set = { param for param in params if isinstance(param, Parameter) } with self.subTest(msg="dict assign and copy"): ordered = two.ordered_parameters bound = two.assign_parameters(dict(zip(ordered, params)), inplace=False) self.assertEqual(bound.parameters, params_set) self.assertEqual(two.num_parameters, 9) with self.subTest(msg="list assign and copy"): ordered = two.ordered_parameters bound = two.assign_parameters(params, inplace=False) self.assertEqual(bound.parameters, params_set) self.assertEqual(two.num_parameters, 9) with self.subTest(msg="list assign inplace"): ordered = two.ordered_parameters two.assign_parameters(params, inplace=True) self.assertEqual(two.parameters, params_set) self.assertEqual(two.num_parameters, 3) self.assertEqual(two.num_parameters_settable, 9) def test_parameters_settable_is_constant(self): """Test the attribute num_parameters_settable does not change on parameter change.""" two = TwoLocal(3, rotation_blocks="rx", entanglement="cz", reps=2) ordered_params = two.ordered_parameters x = Parameter("x") two.assign_parameters(dict( zip(ordered_params, [x] * two.num_parameters)), inplace=True) with self.subTest(msg="num_parameters collapsed to 1"): self.assertEqual(two.num_parameters, 1) with self.subTest(msg="num_parameters_settable remained constant"): self.assertEqual(two.num_parameters_settable, len(ordered_params)) def test_compose_inplace_to_circuit(self): """Test adding a two-local to an existing circuit.""" two = TwoLocal(3, ["ry", "rz"], "cz", "full", reps=1, insert_barriers=True) circuit = QuantumCircuit(3) circuit.compose(two, inplace=True) # ┌──────────┐┌──────────┐ ░ ░ ┌──────────┐ ┌──────────┐ # q_0: ┤ Ry(θ[0]) ├┤ Rz(θ[3]) ├─░──■──■─────░─┤ Ry(θ[6]) ├─┤ Rz(θ[9]) ├ # ├──────────┤├──────────┤ ░ │ │ ░ ├──────────┤┌┴──────────┤ # q_1: ┤ Ry(θ[1]) ├┤ Rz(θ[4]) ├─░──■──┼──■──░─┤ Ry(θ[7]) ├┤ Rz(θ[10]) ├ # ├──────────┤├──────────┤ ░ │ │ ░ ├──────────┤├───────────┤ # q_2: ┤ Ry(θ[2]) ├┤ Rz(θ[5]) ├─░─────■──■──░─┤ Ry(θ[8]) ├┤ Rz(θ[11]) ├ # └──────────┘└──────────┘ ░ ░ └──────────┘└───────────┘ reference = QuantumCircuit(3) param_iter = iter(two.ordered_parameters) for i in range(3): reference.ry(next(param_iter), i) for i in range(3): reference.rz(next(param_iter), i) reference.barrier() reference.cz(0, 1) reference.cz(0, 2) reference.cz(1, 2) reference.barrier() for i in range(3): reference.ry(next(param_iter), i) for i in range(3): reference.rz(next(param_iter), i) self.assertCircuitEqual(circuit.decompose(), reference) def test_composing_two(self): """Test adding two two-local circuits.""" entangler_map = [[0, 3], [0, 2]] two = TwoLocal(4, [], "cry", entangler_map, reps=1) circuit = two.compose(two) reference = QuantumCircuit(4) params = two.ordered_parameters for _ in range(2): reference.cry(params[0], 0, 3) reference.cry(params[1], 0, 2) self.assertCircuitEqual(reference, circuit) def test_ry_blocks(self): """Test that the RealAmplitudes circuit is instantiated correctly.""" two = RealAmplitudes(4) with self.subTest(msg="test rotation gate"): self.assertEqual(len(two.rotation_blocks), 1) self.assertIsInstance(two.rotation_blocks[0].data[0].operation, RYGate) with self.subTest(msg="test parameter bounds"): expected = [(-np.pi, np.pi)] * two.num_parameters np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_ry_circuit_reverse_linear(self): """Test a RealAmplitudes circuit with entanglement = "reverse_linear".""" num_qubits = 3 reps = 2 entanglement = "reverse_linear" parameters = ParameterVector("theta", num_qubits * (reps + 1)) param_iter = iter(parameters) expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.ry(next(param_iter), i) expected.cx(1, 2) expected.cx(0, 1) for i in range(num_qubits): expected.ry(next(param_iter), i) library = RealAmplitudes( num_qubits, reps=reps, entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_ry_circuit_full(self): """Test a RealAmplitudes circuit with entanglement = "full".""" num_qubits = 3 reps = 2 entanglement = "full" parameters = ParameterVector("theta", num_qubits * (reps + 1)) param_iter = iter(parameters) # ┌──────────┐ ┌──────────┐ ┌──────────┐ # q_0: ┤ Ry(θ[0]) ├──■────■──┤ Ry(θ[3]) ├──────────────■────■──┤ Ry(θ[6]) ├──────────── # ├──────────┤┌─┴─┐ │ └──────────┘┌──────────┐┌─┴─┐ │ └──────────┘┌──────────┐ # q_1: ┤ Ry(θ[1]) ├┤ X ├──┼─────────■────┤ Ry(θ[4]) ├┤ X ├──┼─────────■────┤ Ry(θ[7]) ├ # ├──────────┤└───┘┌─┴─┐ ┌─┴─┐ ├──────────┤└───┘┌─┴─┐ ┌─┴─┐ ├──────────┤ # q_2: ┤ Ry(θ[2]) ├─────┤ X ├─────┤ X ├──┤ Ry(θ[5]) ├─────┤ X ├─────┤ X ├──┤ Ry(θ[8]) ├ # └──────────┘ └───┘ └───┘ └──────────┘ └───┘ └───┘ └──────────┘ expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.ry(next(param_iter), i) expected.cx(0, 1) expected.cx(0, 2) expected.cx(1, 2) for i in range(num_qubits): expected.ry(next(param_iter), i) library = RealAmplitudes( num_qubits, reps=reps, entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_ryrz_blocks(self): """Test that the EfficientSU2 circuit is instantiated correctly.""" two = EfficientSU2(3) with self.subTest(msg="test rotation gate"): self.assertEqual(len(two.rotation_blocks), 2) self.assertIsInstance(two.rotation_blocks[0].data[0].operation, RYGate) self.assertIsInstance(two.rotation_blocks[1].data[0].operation, RZGate) with self.subTest(msg="test parameter bounds"): expected = [(-np.pi, np.pi)] * two.num_parameters np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_ryrz_circuit(self): """Test an EfficientSU2 circuit.""" num_qubits = 3 reps = 2 entanglement = "circular" parameters = ParameterVector("theta", 2 * num_qubits * (reps + 1)) param_iter = iter(parameters) # ┌──────────┐┌──────────┐┌───┐ ┌──────────┐┌──────────┐ » # q_0: ┤ Ry(θ[0]) ├┤ Rz(θ[3]) ├┤ X ├──■──┤ Ry(θ[6]) ├┤ Rz(θ[9]) ├─────────────» # ├──────────┤├──────────┤└─┬─┘┌─┴─┐└──────────┘├──────────┤┌───────────┐» # q_1: ┤ Ry(θ[1]) ├┤ Rz(θ[4]) ├──┼──┤ X ├─────■──────┤ Ry(θ[7]) ├┤ Rz(θ[10]) ├» # ├──────────┤├──────────┤ │ └───┘ ┌─┴─┐ ├──────────┤├───────────┤» # q_2: ┤ Ry(θ[2]) ├┤ Rz(θ[5]) ├──■──────────┤ X ├────┤ Ry(θ[8]) ├┤ Rz(θ[11]) ├» # └──────────┘└──────────┘ └───┘ └──────────┘└───────────┘» # « ┌───┐ ┌───────────┐┌───────────┐ # «q_0: ┤ X ├──■──┤ Ry(θ[12]) ├┤ Rz(θ[15]) ├───────────── # « └─┬─┘┌─┴─┐└───────────┘├───────────┤┌───────────┐ # «q_1: ──┼──┤ X ├──────■──────┤ Ry(θ[13]) ├┤ Rz(θ[16]) ├ # « │ └───┘ ┌─┴─┐ ├───────────┤├───────────┤ # «q_2: ──■───────────┤ X ├────┤ Ry(θ[14]) ├┤ Rz(θ[17]) ├ # « └───┘ └───────────┘└───────────┘ expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.ry(next(param_iter), i) for i in range(num_qubits): expected.rz(next(param_iter), i) expected.cx(2, 0) expected.cx(0, 1) expected.cx(1, 2) for i in range(num_qubits): expected.ry(next(param_iter), i) for i in range(num_qubits): expected.rz(next(param_iter), i) library = EfficientSU2( num_qubits, reps=reps, entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_swaprz_blocks(self): """Test that the ExcitationPreserving circuit is instantiated correctly.""" two = ExcitationPreserving(5) with self.subTest(msg="test rotation gate"): self.assertEqual(len(two.rotation_blocks), 1) self.assertIsInstance(two.rotation_blocks[0].data[0].operation, RZGate) with self.subTest(msg="test entanglement gate"): self.assertEqual(len(two.entanglement_blocks), 1) block = two.entanglement_blocks[0] self.assertEqual(len(block.data), 2) self.assertIsInstance(block.data[0].operation, RXXGate) self.assertIsInstance(block.data[1].operation, RYYGate) with self.subTest(msg="test parameter bounds"): expected = [(-np.pi, np.pi)] * two.num_parameters np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_swaprz_circuit(self): """Test a ExcitationPreserving circuit in iswap mode.""" num_qubits = 3 reps = 2 entanglement = "linear" parameters = ParameterVector( "theta", num_qubits * (reps + 1) + reps * (num_qubits - 1)) param_iter = iter(parameters) # ┌──────────┐┌────────────┐┌────────────┐ ┌──────────┐ » # q_0: ┤ Rz(θ[0]) ├┤0 ├┤0 ├─┤ Rz(θ[5]) ├───────────────» # ├──────────┤│ Rxx(θ[3]) ││ Ryy(θ[3]) │┌┴──────────┴┐┌────────────┐» # q_1: ┤ Rz(θ[1]) ├┤1 ├┤1 ├┤0 ├┤0 ├» # ├──────────┤└────────────┘└────────────┘│ Rxx(θ[4]) ││ Ryy(θ[4]) │» # q_2: ┤ Rz(θ[2]) ├────────────────────────────┤1 ├┤1 ├» # └──────────┘ └────────────┘└────────────┘» # « ┌────────────┐┌────────────┐┌───────────┐ » # «q_0: ────────────┤0 ├┤0 ├┤ Rz(θ[10]) ├───────────────» # « ┌──────────┐│ Rxx(θ[8]) ││ Ryy(θ[8]) │├───────────┴┐┌────────────┐» # «q_1: ┤ Rz(θ[6]) ├┤1 ├┤1 ├┤0 ├┤0 ├» # « ├──────────┤└────────────┘└────────────┘│ Rxx(θ[9]) ││ Ryy(θ[9]) │» # «q_2: ┤ Rz(θ[7]) ├────────────────────────────┤1 ├┤1 ├» # « └──────────┘ └────────────┘└────────────┘» # « # «q_0: ───────────── # « ┌───────────┐ # «q_1: ┤ Rz(θ[11]) ├ # « ├───────────┤ # «q_2: ┤ Rz(θ[12]) ├ # « └───────────┘ expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.rz(next(param_iter), i) shared_param = next(param_iter) expected.rxx(shared_param, 0, 1) expected.ryy(shared_param, 0, 1) shared_param = next(param_iter) expected.rxx(shared_param, 1, 2) expected.ryy(shared_param, 1, 2) for i in range(num_qubits): expected.rz(next(param_iter), i) library = ExcitationPreserving( num_qubits, reps=reps, entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_fsim_circuit(self): """Test a ExcitationPreserving circuit in fsim mode.""" num_qubits = 3 reps = 2 entanglement = "linear" # need the parameters in the entanglement blocks to be the same because the order # can get mixed up in ExcitationPreserving (since parameters are not ordered in circuits) parameters = [1] * (num_qubits * (reps + 1) + reps * (1 + num_qubits)) param_iter = iter(parameters) # ┌───────┐┌─────────┐┌─────────┐ ┌───────┐ » # q_0: ┤ Rz(1) ├┤0 ├┤0 ├─■──────┤ Rz(1) ├───────────────────» # ├───────┤│ Rxx(1) ││ Ryy(1) │ │P(1) ┌┴───────┴┐┌─────────┐ » # q_1: ┤ Rz(1) ├┤1 ├┤1 ├─■─────┤0 ├┤0 ├─■─────» # ├───────┤└─────────┘└─────────┘ │ Rxx(1) ││ Ryy(1) │ │P(1) » # q_2: ┤ Rz(1) ├─────────────────────────────┤1 ├┤1 ├─■─────» # └───────┘ └─────────┘└─────────┘ » # « ┌─────────┐┌─────────┐ ┌───────┐ » # «q_0: ─────────┤0 ├┤0 ├─■──────┤ Rz(1) ├───────────────────» # « ┌───────┐│ Rxx(1) ││ Ryy(1) │ │P(1) ┌┴───────┴┐┌─────────┐ » # «q_1: ┤ Rz(1) ├┤1 ├┤1 ├─■─────┤0 ├┤0 ├─■─────» # « ├───────┤└─────────┘└─────────┘ │ Rxx(1) ││ Ryy(1) │ │P(1) » # «q_2: ┤ Rz(1) ├─────────────────────────────┤1 ├┤1 ├─■─────» # « └───────┘ └─────────┘└─────────┘ » # « # «q_0: ───────── # « ┌───────┐ # «q_1: ┤ Rz(1) ├ # « ├───────┤ # «q_2: ┤ Rz(1) ├ # « └───────┘ expected = QuantumCircuit(3) for _ in range(reps): for i in range(num_qubits): expected.rz(next(param_iter), i) shared_param = next(param_iter) expected.rxx(shared_param, 0, 1) expected.ryy(shared_param, 0, 1) expected.cp(next(param_iter), 0, 1) shared_param = next(param_iter) expected.rxx(shared_param, 1, 2) expected.ryy(shared_param, 1, 2) expected.cp(next(param_iter), 1, 2) for i in range(num_qubits): expected.rz(next(param_iter), i) library = ExcitationPreserving( num_qubits, reps=reps, mode="fsim", entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_circular_on_same_block_and_circuit_size(self): """Test circular entanglement works correctly if the circuit and block sizes match.""" two = TwoLocal(2, "ry", "cx", entanglement="circular", reps=1) parameters = np.arange(two.num_parameters) # ┌───────┐ ┌───────┐ # q_0: ┤ Ry(0) ├──■──┤ Ry(2) ├ # ├───────┤┌─┴─┐├───────┤ # q_1: ┤ Ry(1) ├┤ X ├┤ Ry(3) ├ # └───────┘└───┘└───────┘ ref = QuantumCircuit(2) ref.ry(parameters[0], 0) ref.ry(parameters[1], 1) ref.cx(0, 1) ref.ry(parameters[2], 0) ref.ry(parameters[3], 1) self.assertCircuitEqual(two.assign_parameters(parameters), ref) def test_circuit_with_numpy_integers(self): """Test if TwoLocal can be made from numpy integers""" num_qubits = 6 reps = 3 expected_np32 = [(i, j) for i in np.arange(num_qubits, dtype=np.int32) for j in np.arange(num_qubits, dtype=np.int32) if i < j] expected_np64 = [(i, j) for i in np.arange(num_qubits, dtype=np.int64) for j in np.arange(num_qubits, dtype=np.int64) if i < j] two_np32 = TwoLocal(num_qubits, "ry", "cx", entanglement=expected_np32, reps=reps) two_np64 = TwoLocal(num_qubits, "ry", "cx", entanglement=expected_np64, reps=reps) expected_cx = reps * num_qubits * (num_qubits - 1) / 2 self.assertEqual(two_np32.decompose().count_ops()["cx"], expected_cx) self.assertEqual(two_np64.decompose().count_ops()["cx"], expected_cx) @combine(num_qubits=[4, 5]) def test_full_vs_reverse_linear(self, num_qubits): """Test that 'full' and 'reverse_linear' provide the same unitary element.""" reps = 2 full = RealAmplitudes(num_qubits=num_qubits, entanglement="full", reps=reps) num_params = (reps + 1) * num_qubits np.random.seed(num_qubits) params = np.random.rand(num_params) reverse = RealAmplitudes(num_qubits=num_qubits, entanglement="reverse_linear", reps=reps) full.assign_parameters(params, inplace=True) reverse.assign_parameters(params, inplace=True) assert Operator(full) == Operator(reverse)
import qiskit from qiskit import QuantumCircuit from qiskit.circuit.library import RXGate import numpy as np basis_gates = ['u3'] circ = QuantumCircuit(1, 1) RX = RXGate(0) # circ.append(RX, [0]) circ.h(0) circ.measure(0, 0) print("Before Transpiling:") print(circ) new_circ = qiskit.compiler.transpile(circ, basis_gates=basis_gates, optimization_level=0) print("After Transpiling:") print(new_circ)
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 test_controlled_rx(self): """Test the creation of a controlled RX gate.""" theta = 0.5 self.assertEqual(RXGate(theta).control(), CRXGate(theta))
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)