def test_odd_number_self_inverse(self): """Test that an odd number of self-inverse gates leaves one gate remaining.""" qc = QuantumCircuit(2, 2) qc.h(0) qc.h(0) qc.h(0) pass_ = InverseCancellation([HGate()]) pm = PassManager(pass_) new_circ = pm.run(qc) gates_after = new_circ.count_ops() self.assertIn("h", gates_after) self.assertEqual(gates_after["h"], 1)
def test_u_rewrites_to_phase(self): """Test that a phase-like U-gate gets rewritten into an RZ gate.""" qc = QuantumCircuit(1) qc.u(0, 0, np.pi / 6, 0) basis = ["sx", "p"] passmanager = PassManager() passmanager.append(Optimize1qGatesDecomposition(basis)) result = passmanager.run(qc) expected = QuantumCircuit(1) expected.p(np.pi / 6, 0) msg = f"expected:\n{expected}\nresult:\n{result}" self.assertEqual(expected, result, msg=msg)
def test_ccx(self): """Test that extra multi-qubit operations are properly adjusted. Here, we test that the circuit .. parsed-literal:: ┌──────────────────────────┐ q_0: ┤0 ├──■── │ │┌─┴─┐ q_1: ┤1 exp(-it (IZZ + ZIZ))(1) ├┤ X ├ │ │└─┬─┘ q_2: ┤2 ├──■── └──────────────────────────┘ q_3: ───────────────────────────────── becomes .. parsed-literal:: ┌─────────────────┐ ┌───┐ q_0: ┤0 ├─X────────────────────┤ X ├ │ exp(-it ZZ)(1) │ │ ┌─────────────────┐└─┬─┘ q_1: ┤1 ├─X─┤0 ├──■── └─────────────────┘ │ exp(-it ZZ)(2) │ │ q_2: ──────────────────────┤1 ├──■── └─────────────────┘ q_3: ────────────────────────────────────────────── as expected. I.e. the Toffoli is properly adjusted at the end. """ cmap = CouplingMap(couplinglist=[(0, 1), (1, 2)]) swap_strat = SwapStrategy(cmap, swap_layers=(((0, 1), ), )) pm_ = PassManager([ FindCommutingPauliEvolutions(), Commuting2qGateRouter(swap_strat), ]) op = PauliSumOp.from_list([("IZZ", 1), ("ZIZ", 2)]) circ = QuantumCircuit(4) circ.append(PauliEvolutionGate(op, 1), range(3)) circ.ccx(0, 2, 1) swapped = pm_.run(circ) expected = QuantumCircuit(4) expected.append(PauliEvolutionGate(Pauli("ZZ"), 1), (0, 1)) expected.swap(0, 1) expected.append(PauliEvolutionGate(Pauli("ZZ"), 2), (1, 2)) expected.ccx(1, 2, 0) self.assertEqual(swapped, expected)
def test_self_inverse_on_different_qubits(self): """Test that self_inverse gates cancel on the correct qubits.""" qc = QuantumCircuit(2, 2) qc.h(0) qc.h(1) qc.h(0) qc.h(1) pass_ = InverseCancellation([HGate()]) pm = PassManager(pass_) new_circ = pm.run(qc) gates_after = new_circ.count_ops() self.assertNotIn("h", gates_after)
def remap_apply_layout_virtual_qubit_registers(circuit: QuantumCircuit, coupling_map: CouplingMap, layout: Layout) -> \ Optional[QuantumCircuit]: dag = circuit_to_dag(circuit) if not check_dag_circuit_compatible(dag, coupling_map): return print("FINALIZE LAYOUT:", layout) remap_pass_manager = PassManager() remap_pass_manager.append(SetLayout(layout)) remap_pass_manager.append(ApplyLayout()) remap_circ = remap_pass_manager.run(circuit) return remap_circ
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 ═══════════╡ ╞═══╡ ╞═══════╡ ╞╡ ╞ └─────┘ └─────┘ └─────┘└─────┘ Blocks collected: [['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"]])
def test_hanh_echo_experiment_type(self): """Test Hahn echo experiment type circuit. (input) ┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌─┐ q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤M├ └────┘└────────────────┘└───┘└────────────────┘└────┘└╥┘ c: 1/══════════════════════════════════════════════════════╩═ 0 (output) ┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌──────────────┐┌─┐ q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤ Delay(8[dt]) ├┤M├ └────┘└────────────────┘└───┘└────────────────┘└────┘└──────────────┘└╥┘ c: 1/══════════════════════════════════════════════════════════════════════╩═ 0 This type of experiment doesn't change duration of interest (two in the middle). However induces slight delay less than alignment * dt before measurement. This might induce extra amplitude damping error. """ circuit = QuantumCircuit(1, 1) circuit.sx(0) circuit.delay(100, 0, unit="dt") circuit.x(0) circuit.delay(100, 0, unit="dt") circuit.sx(0) circuit.measure(0, 0) pm = PassManager([ # reproduce old behavior of 0.20.0 before #7655 # currently default write latency is 0 SetIOLatency(clbit_write_latency=1600, conditional_latency=0), ALAPScheduleAnalysis(durations=self.instruction_durations), ConstrainedReschedule(acquire_alignment=16), PadDelay(), ]) aligned_circuit = pm.run(circuit) ref_circuit = QuantumCircuit(1, 1) ref_circuit.sx(0) ref_circuit.delay(100, 0, unit="dt") ref_circuit.x(0) ref_circuit.delay(100, 0, unit="dt") ref_circuit.sx(0) ref_circuit.delay(8, 0, unit="dt") ref_circuit.measure(0, 0) self.assertEqual(aligned_circuit, ref_circuit)
def test_mid_circuit_measure(self): """Test circuit with mid circuit measurement. (input) ┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐ q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(120[dt]) ├┤M├ └───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘ c: 2/════════════════════════╩══════════════════════════════════════════╩═ 0 1 (output) ┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐ q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(134[dt]) ├┤M├ └───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘ c: 2/════════════════════════╩══════════════════════════════════════════╩═ 0 1 Extra delay is always added to the existing delay right before the measurement. Delay after measurement is unchanged. """ circuit = QuantumCircuit(1, 2) circuit.x(0) circuit.delay(100, 0, unit="dt") circuit.measure(0, 0) circuit.delay(10, 0, unit="dt") circuit.x(0) circuit.delay(120, 0, unit="dt") circuit.measure(0, 1) pm = PassManager([ # reproduce old behavior of 0.20.0 before #7655 # currently default write latency is 0 SetIOLatency(clbit_write_latency=1600, conditional_latency=0), ALAPScheduleAnalysis(durations=self.instruction_durations), ConstrainedReschedule(acquire_alignment=16), PadDelay(), ]) aligned_circuit = pm.run(circuit) ref_circuit = QuantumCircuit(1, 2) ref_circuit.x(0) ref_circuit.delay(112, 0, unit="dt") ref_circuit.measure(0, 0) ref_circuit.delay(10, 0, unit="dt") ref_circuit.x(0) ref_circuit.delay(134, 0, unit="dt") ref_circuit.measure(0, 1) self.assertEqual(aligned_circuit, ref_circuit)
def test_commutative_circuit3(self): """ A simple circuit where three CNOTs commute, the first and the last cancel, also two X gates cancel and two Rz gates combine. qr0:-------.------------------.------------- qr0:------------- | | qr1:------(+)------(+)--[X]--(+)-------[X]-- = qr1:--------(+)-- | | qr2:------[Rz]--.---.----.---[Rz]-[T]--[S]-- qr2:--[U1]---.--- | | qr3:-[Rz]--[X]-(+)------(+)--[X]-[Rz]------- qr3:--[Rz]------- """ qr = QuantumRegister(4, 'qr') circuit = QuantumCircuit(qr) circuit.cx(qr[0], qr[1]) circuit.rz(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_euler_decomposition_worse(self): """Ensure we don't decompose to a deeper circuit.""" circuit = QuantumCircuit(1) circuit.rx(-np.pi / 2, 0) circuit.rz(-np.pi / 2, 0) basis = ['rx', 'rz'] passmanager = PassManager() passmanager.append(BasisTranslator(sel, basis)) passmanager.append(Optimize1qGatesDecomposition(basis)) result = passmanager.run(circuit) # decomposition of circuit will result in 3 gates instead of 2 # assert optimization pass doesn't use it. self.assertEqual(result, circuit)
def test_optimize_u1_basis_u2(self): """U1(pi/4) -> Raises. Basis [u2]""" qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr) circuit.append(U1Gate(np.pi / 4), [qr[0]]) expected = QuantumCircuit(qr) expected.append(U3Gate(0, 0, np.pi / 4), [qr[0]]) passmanager = PassManager() passmanager.append(Optimize1qGates(["u2"])) with self.assertRaises(TranspilerError): _ = passmanager.run(circuit)
def test_do_not_go_across_barrier(self): """Validate that blocks are not collected across barriers ░ q_0: ──■───░───■── ┌─┴─┐ ░ ┌─┴─┐ q_1: ┤ X ├─░─┤ X ├ └───┘ ░ └───┘ q_2: ──────░────── ░ """ qr = QuantumRegister(3, "qr") qc = QuantumCircuit(qr) qc.cx(0, 1) qc.barrier() qc.cx(0, 1) pass_manager = PassManager() pass_manager.append(CollectMultiQBlocks()) pass_manager.run(qc) for block in pass_manager.property_set["block_list"]: self.assertTrue(len(block) <= 1)
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_two_qubit_natural_direction_true_gate_length_raises(self): """Verify not attempting pulse optimal decomposition when pulse_optimize==False.""" # this assumes iswawp pulse optimal decomposition doesn't exist backend = FakeVigo() conf = backend.configuration() for _, nduv in backend.properties()._gates["cx"].items(): nduv["gate_length"] = (4e-7, nduv["gate_length"][1]) nduv["gate_error"] = (7e-3, nduv["gate_error"][1]) qr = QuantumRegister(2) coupling_map = CouplingMap([[0, 1], [1, 0], [1, 2], [1, 3], [3, 4]]) triv_layout_pass = TrivialLayout(coupling_map) qc = QuantumCircuit(qr) qc.unitary(random_unitary(4, seed=12), [0, 1]) unisynth_pass = UnitarySynthesis( basis_gates=conf.basis_gates, backend_props=backend.properties(), pulse_optimize=True, natural_direction=True, ) pm = PassManager([triv_layout_pass, unisynth_pass]) with self.assertRaises(TranspilerError): pm.run(qc)
def test_optimize_u1_basis_u2(self): """U1(pi/4) -> Raises. Basis [u2]""" qr = QuantumRegister(1, 'qr') circuit = QuantumCircuit(qr) circuit.u1(np.pi / 4, qr[0]) expected = QuantumCircuit(qr) expected.u3(0, 0, np.pi / 4, qr[0]) passmanager = PassManager() passmanager.append(Optimize1qGates(['u2'])) with self.assertRaises(TranspilerError): _ = passmanager.run(circuit)
def test_conditional_gates_dont_commute(self): """Conditional gates do not commute and do not cancel""" circuit = QuantumCircuit(3, 2) circuit.h(0) circuit.measure(0, 0) circuit.cx(1, 2) circuit.cx(1, 2).c_if(circuit.cregs[0], 0) circuit.measure([1, 2], [0, 1]) new_pm = PassManager(CommutativeCancellation()) new_circuit = new_pm.run(circuit) self.assertEqual(circuit, new_circuit)
def test_no_cancellation_across_parameterized_gates(self): """Test that parameterized gates prevent cancellation. This test should be modified when inverse and commutativity checking get improved to handle parameterized gates. """ circuit = QuantumCircuit(1) circuit.rz(np.pi / 2, 0) circuit.rz(Parameter("Theta"), 0) circuit.rz(-np.pi / 2, 0) passmanager = PassManager(CommutativeInverseCancellation()) new_circuit = passmanager.run(circuit) self.assertEqual(circuit, new_circuit)
def test_self_inverse_on_different_qubits(self): """Test that self_inverse gates cancel on the correct qubits.""" circuit = QuantumCircuit(2, 2) circuit.h(0) circuit.h(1) circuit.h(0) circuit.h(1) passmanager = PassManager(CommutativeInverseCancellation()) new_circuit = passmanager.run(circuit) gates_after = new_circuit.count_ops() self.assertNotIn("h", gates_after)
def test_cx_do_not_wrongly_cancel(self): """Test that CX(0,1) and CX(1, 0) do not cancel out, when (CX, CX) is passed as an inverse pair.""" circuit = QuantumCircuit(2, 0) circuit.cx(0, 1) circuit.cx(1, 0) passmanager = PassManager(CommutativeInverseCancellation()) new_circuit = passmanager.run(circuit) gates_after = new_circuit.count_ops() self.assertIn("cx", gates_after) self.assertEqual(gates_after["cx"], 2)
def test_odd_number_self_inverse(self): """Test that an odd number of self-inverse gates leaves one gate remaining.""" circuit = QuantumCircuit(2, 2) circuit.h(0) circuit.h(0) circuit.h(0) passmanager = PassManager(CommutativeInverseCancellation()) new_circuit = passmanager.run(circuit) gates_after = new_circuit.count_ops() self.assertIn("h", gates_after) self.assertEqual(gates_after["h"], 1)
def pick_label(circ, backend, coupling_map, optimization_level, show=False): ''' Funzione che restituisce il dizionario con il mapping, scegliendo come label layout quello che minimizza la depth del circuito tra dense layout e noise_adaptive sommata alla depth delle operazioni dopo il routing. In questa maniera tengo anche conto di qual'è il layout che permette di minimizzare le operazioni di swap ''' new_circ_lv3 = transpile(circ, backend=backend, optimization_level=optimization_level) new_circ_lv3_na = transpile(circ, backend=backend, optimization_level=optimization_level, layout_method='noise_adaptive') #plot_circuit_layout(new_circ_lv3_na, backend).show() #plot_circuit_layout(new_circ_lv3, backend).show() cp = CouplingMap(couplinglist=coupling_map) depths = [] for qc in [new_circ_lv3_na, new_circ_lv3]: depth = qc.depth() pass_manager = PassManager(LookaheadSwap(coupling_map=cp)) lc_qc = pass_manager.run(qc) pass_manager = PassManager(StochasticSwap(coupling_map=cp)) st_qc = pass_manager.run(qc) depths.append(depth + lc_qc.depth()) depths.append(depth + st_qc.depth()) #print('depth=', depth, ' depth + routing_lc_qc= ', depth + lc_qc.depth(), ' depth + routing_st_qc=',depth + st_qc.depth()) if depths.index(min(depths)) < 2: print('na') if show == True: plot_circuit_layout(new_circ_lv3_na, backend).show() return new_circ_lv3_na._layout.get_physical_bits() if depths.index(min(depths)) >= 2: print('not na') if show == True: plot_circuit_layout(new_circ_lv3, backend).show() return new_circ_lv3._layout.get_physical_bits()
def test_four_alternating_inverse_gates(self): """Test that inverse cancellation works correctly for alternating sequences of inverse gates of even-length.""" qc = QuantumCircuit(2, 2) qc.p(np.pi / 4, 0) qc.p(-np.pi / 4, 0) qc.p(np.pi / 4, 0) qc.p(-np.pi / 4, 0) pass_ = InverseCancellation([(PhaseGate(np.pi / 4), PhaseGate(-np.pi / 4))]) pm = PassManager(pass_) new_circ = pm.run(qc) gates_after = new_circ.count_ops() self.assertNotIn("p", gates_after)
def test_optimize_u3_basis_u1(self): """U3(0, 0, pi/4) -> U1(pi/4). Basis [u1].""" qr = QuantumRegister(2, 'qr') circuit = QuantumCircuit(qr) circuit.u3(0, 0, np.pi / 4, qr[0]) expected = QuantumCircuit(qr) expected.u1(np.pi / 4, qr[0]) passmanager = PassManager() passmanager.append(Optimize1qGates(['u1'])) result = passmanager.run(circuit) self.assertEqual(expected, result)
def test_optimize_u3_to_u1_round(self): """U3(1e-16, 1e-16, pi/4) -> U1(pi/4)""" qr = QuantumRegister(1, 'qr') circuit = QuantumCircuit(qr) circuit.u3(1e-16, 1e-16, np.pi / 4, qr[0]) expected = QuantumCircuit(qr) expected.u1(np.pi / 4, qr[0]) passmanager = PassManager() passmanager.append(Optimize1qGates()) result = passmanager.run(circuit) self.assertEqual(expected, result)
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_four_alternating_inverse_gates(self): """Test that inverse cancellation works correctly for alternating sequences of inverse gates of even-length.""" circuit = QuantumCircuit(2, 2) circuit.p(np.pi / 4, 0) circuit.p(-np.pi / 4, 0) circuit.p(np.pi / 4, 0) circuit.p(-np.pi / 4, 0) passmanager = PassManager(CommutativeInverseCancellation()) new_circuit = passmanager.run(circuit) gates_after = new_circuit.count_ops() self.assertNotIn("p", gates_after)
def test_single_gate_block_outside_basis(self): """Test that a single gate block outside the configured basis gets converted.""" qc = QuantumCircuit(2) qc.swap(0, 1) consolidate_block_pass = ConsolidateBlocks( basis_gates=["id", "cx", "rz", "sx", "x"]) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.append(consolidate_block_pass) expected = QuantumCircuit(2) expected.unitary( np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]), [0, 1]) self.assertEqual(expected, pass_manager.run(qc))
def test_y_simplification_rz_sx_x(self): """Test that a y gate gets decomposed to x-zx with ibmq basis.""" qc = QuantumCircuit(1) qc.y(0) basis = ["id", "rz", "sx", "x", "cx"] passmanager = PassManager() passmanager.append(BasisTranslator(sel, basis)) passmanager.append(Optimize1qGatesDecomposition(basis)) result = passmanager.run(qc) expected = QuantumCircuit(1) expected.rz(-np.pi, 0) expected.x(0) msg = f"expected:\n{expected}\nresult:\n{result}" self.assertEqual(expected, result, msg=msg)
def test_short_string(self): """Test that a shorter-than-universal string is still rewritten.""" qc = QuantumCircuit(1) qc.h(0) qc.ry(np.pi / 2, 0) basis = ["sx", "rz"] passmanager = PassManager() passmanager.append(Optimize1qGatesDecomposition(basis)) result = passmanager.run(qc) expected = QuantumCircuit(1) expected.sx(0) expected.sx(0) msg = f"expected:\n{expected}\nresult:\n{result}" self.assertEqual(expected, result, msg=msg)
def test_classical_conditions_maintained(self): """Test that consolidate blocks doesn't drop the classical conditions This issue was raised in #2752 """ qc = QuantumCircuit(1, 1) qc.h(0).c_if(qc.cregs[0], 1) qc.measure(0, 0) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.append(ConsolidateBlocks()) qc1 = pass_manager.run(qc) self.assertEqual(qc, qc1)