def gen_pauli_measurement_circuits(state_circuit, compiler_pass, operator): # compile main circuit once state_cu = CompilationUnit(state_circuit) compiler_pass.apply(state_cu) compiled_state = state_cu.circuit final_map = state_cu.final_map # make a measurement circuit for each pauli pauli_circuits = [] coeffs = [] energy = 0 for p, c in operator.terms.items(): if p == (): # constant term energy += c else: # make measurement circuits and compile them pauli_circ = Circuit(state_circuit.n_qubits - 1) # ignore syndrome qubit append_pauli_measurement(qps_from_openfermion(p), pauli_circ) pauli_cu = CompilationUnit(pauli_circ) compiler_pass.apply(pauli_cu) pauli_circ = pauli_cu.circuit init_map = pauli_cu.initial_map # map measurements onto the placed qubits from the state rename_map = { i: final_map[o] for o, i in init_map.items() if o in final_map } pauli_circ.rename_units(rename_map) state_and_measure = compiled_state.copy() state_and_measure.append(pauli_circ) pauli_circuits.append(state_and_measure) coeffs.append(c) return pauli_circuits, coeffs, energy
def predicate_route_device(my_circuit, my_device): gp = GraphPlacement(my_device) gp.place(my_circuit) cu = CompilationUnit(my_circuit, [ConnectivityPredicate(my_device)]) routing_passes = SequencePass( [RoutingPass(my_device), DecomposeSwapsToCXs(my_device, False)]) routing_passes.apply(cu) return cu.circuit, cu.check_all_predicates()
def decompose_circuit(tk_circuit, decomposition_lvl): """Function to decompose circuit, with decomposition_lvl 1 decomposing BRIDGEs and 2 decomposing as much as possible (SWAPs). While normal pytket Circuits may contain bridge gates, these are not compatible with IBM Qiskit architectures. Therefore, one might have to decompose the circuit before converting it to an IBM Qiskit compatible version. Parameters ---------- tk_circuit : pytket Circuit A pytket circuit decomposition_lvl : integer use 1 to decompose BRIDGEs, 2 to decompose as far as possible including SWAPs Returns ------- pytket Circuit A pytket circuit decomposed """ cu = CompilationUnit(tk_circuit) # 1 to decompose BRIDGE gates, 2 to decompose as far as possible (includes acceptable SWAPs!) if decomposition_lvl == 1: seqpass = SequencePass([DecomposeMultiQubitsIBM()]) elif decomposition_lvl == 2: seqpass = SequencePass( [DecomposeMultiQubitsIBM(), DecomposeSingleQubitsIBM()]) # apply decomposition and return the resulting circuit seqpass.apply(cu) return cu.circuit
def place_on_device( circuit: cirq.Circuit, device: cirq.google.XmonDevice, ) -> Tuple[cirq.Circuit, Dict[cirq.Qid, cirq.Qid], Dict[cirq.Qid, cirq.Qid]]: """Place a circuit on an device. Converts a circuit to a new circuit that respects the adjacency of a given device and is equivalent to the given circuit up to qubit ordering. Args: circuit: The circuit to place on a grid. device: The device to place the circuit on. Returns: routed_circuit: The new circuit initial_map: Initial placement of qubits final_map: The final placement of qubits after action of the circuit """ index_to_qubit = sorted(device.qubit_set()) qubit_to_index = {q: i for i, q in enumerate(index_to_qubit)} tk_circuit = pytket.cirq.cirq_to_tk(circuit) tk_device = _device_to_tket_device(device, qubit_to_index) unit = CompilationUnit(tk_circuit, [ConnectivityPredicate(tk_device)]) passes = SequencePass( [PlacementPass(GraphPlacement(tk_device)), RoutingPass(tk_device)]) passes.apply(unit) valid = unit.check_all_predicates() if not valid: raise RuntimeError("Routing failed") initial_map = { cirq.LineQubit(_tk_to_i(n1)): index_to_qubit[_tk_to_i(n2)] for n1, n2 in unit.initial_map.items() } final_map = { cirq.LineQubit(_tk_to_i(n1)): index_to_qubit[_tk_to_i(n2)] for n1, n2 in unit.final_map.items() } routed_circuit = pytket.cirq.tk_to_cirq(unit.circuit) routed_circuit = routed_circuit.transform_qubits( lambda q: index_to_qubit[q.x]) return routed_circuit, initial_map, final_map
def place_on_device( circuit: cirq.Circuit, device: cg.XmonDevice, ) -> Tuple[cirq.Circuit, Dict[cirq.Qid, cirq.Qid], Dict[cirq.Qid, cirq.Qid]]: """Place a circuit on an device. Converts a circuit to a new circuit that respects the adjacency of a given device and is equivalent to the given circuit up to qubit ordering. Args: circuit: The circuit to place on a grid. device: The device to place the circuit on. Returns: routed_circuit: The new circuit initial_map: Initial placement of qubits final_map: The final placement of qubits after action of the circuit """ tk_circuit = pytket.extensions.cirq.cirq_to_tk(circuit) tk_device = _device_to_tket_device(device) unit = CompilationUnit(tk_circuit, [ConnectivityPredicate(tk_device)]) passes = SequencePass( [PlacementPass(GraphPlacement(tk_device)), RoutingPass(tk_device)]) passes.apply(unit) valid = unit.check_all_predicates() if not valid: raise RuntimeError("Routing failed") initial_map = { tk_to_cirq_qubit(n1): tk_to_cirq_qubit(n2) for n1, n2 in unit.initial_map.items() } final_map = { tk_to_cirq_qubit(n1): tk_to_cirq_qubit(n2) for n1, n2 in unit.final_map.items() } routed_circuit = pytket.extensions.cirq.tk_to_cirq(unit.circuit) return routed_circuit, initial_map, final_map
def compile_measurements(all_measures, backend, ansatz_final_map): compiled_measures = [] for measure_circ in all_measures: cu = CompilationUnit(measure_circ) backend.default_compilation_pass.apply(cu) measure_circ = cu.circuit # Permute qubits to match their locations after the ansatz qb_map = { cu.final_map[m_qb]: original_qb for original_qb, m_qb in ansatz_final_map.items() } measure_circ.rename_units(qb_map) compiled_measures.append(measure_circ) return compiled_measures
def run(self, qobj: QasmQobj) -> TketJob: module = disassemble(qobj) circ_list = [qiskit_to_tk(qc) for qc in module[0]] if self._comp_pass: final_maps = [] compiled_list = [] for c in circ_list: cu = CompilationUnit(c) self._comp_pass.apply(cu) compiled_list.append(cu.circuit) final_maps.append(cu.final_map) circ_list = compiled_list else: final_maps = [None for c in circ_list] handles = self._backend.process_circuits(circ_list, n_shots=qobj.config.shots) return TketJob(self, handles, qobj, final_maps)
def test_swaps_basisorder() -> None: # Check that implicit swaps can be corrected irrespective of BasisOrder b = AerStateBackend() c = Circuit(4) c.X(0) c.CX(0, 1) c.CX(1, 0) c.CX(1, 3) c.CX(3, 1) c.X(2) cu = CompilationUnit(c) CliffordSimp(True).apply(cu) c1 = cu.circuit assert c1.n_gates_of_type(OpType.CX) == 2 b.compile_circuit(c) b.compile_circuit(c1) handles = b.process_circuits([c, c1]) s_ilo = b.get_state(c1, basis=BasisOrder.ilo) correct_ilo = b.get_state(c, basis=BasisOrder.ilo) assert np.allclose(s_ilo, correct_ilo) s_dlo = b.get_state(c1, basis=BasisOrder.dlo) correct_dlo = b.get_state(c, basis=BasisOrder.dlo) assert np.allclose(s_dlo, correct_dlo) qbs = c.qubits for result in b.get_results(handles): assert (result.get_state([qbs[1], qbs[2], qbs[3], qbs[0]]).real.tolist().index(1.0) == 6) assert (result.get_state([qbs[2], qbs[1], qbs[0], qbs[3]]).real.tolist().index(1.0) == 9) assert (result.get_state([qbs[2], qbs[3], qbs[0], qbs[1]]).real.tolist().index(1.0) == 12) bu = AerUnitaryBackend() u_ilo = bu.get_unitary(c1, basis=BasisOrder.ilo) correct_ilo = bu.get_unitary(c, basis=BasisOrder.ilo) assert np.allclose(u_ilo, correct_ilo) u_dlo = bu.get_unitary(c1, basis=BasisOrder.dlo) correct_dlo = bu.get_unitary(c, basis=BasisOrder.dlo) assert np.allclose(u_dlo, correct_dlo)
def run_tket_pass(circ: Circuit, total_pass, backend: str): try: cu = CompilationUnit(circ) start_time = time.process_time() total_pass.apply(cu) time_elapsed = time.process_time() - start_time print(time_elapsed) circ2 = cu.circuit if backend in (_BACKEND_GOOGLE, _BACKEND_RIGETTI): two_qb_gate = OpType.CZ else: two_qb_gate = OpType.CX return [ circ2.n_gates, circ2.depth(), circ2.n_gates_of_type(two_qb_gate), circ2.depth_by_type(two_qb_gate), time_elapsed ] except Exception as e: print(e) print("t|ket> error") return [nan, nan, nan, nan, nan]
def test_compilation_correctness() -> None: c = Circuit(5) c.H(0).H(1).H(2) c.CX(0, 1).CX(1, 2) c.Rx(0.25, 1).Ry(0.75, 1).Rz(0.5, 2) c.CCX(2, 1, 0) c.CY(1, 0).CY(2, 1) c.H(0).H(1).H(2) c.Rz(0.125, 0) c.X(1) c.Rz(0.125, 2).X(2).Rz(0.25, 2) c.SX(3).Rz(0.125, 3).SX(3) c.CX(0, 3).CX(0, 4) u_backend = AerUnitaryBackend() u = u_backend.get_unitary(c) ibm_backend = IBMQBackend("ibmq_santiago", hub="ibm-q", group="open", project="main") for ol in range(3): p = ibm_backend.default_compilation_pass(optimisation_level=ol) cu = CompilationUnit(c) p.apply(cu) c1 = cu.circuit compiled_u = u_backend.get_unitary(c1) # Adjust for placement imap = cu.initial_map fmap = cu.final_map c_idx = {c.qubits[i]: i for i in range(5)} c1_idx = {c1.qubits[i]: i for i in range(5)} ini = {c_idx[qb]: c1_idx[node] for qb, node in imap.items()} inv_fin = {c1_idx[node]: c_idx[qb] for qb, node in fmap.items()} m_ini = lift_perm(ini) m_inv_fin = lift_perm(inv_fin) assert compare_unitaries(u, m_inv_fin @ compiled_u @ m_ini)
def compile_ansatz(ansatz, backend): # Solve for device constraints cu = CompilationUnit(ansatz) backend.default_compilation_pass.apply(cu) return cu.circuit, cu.final_map
(15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (12, 23), (16, 24), (20, 25), (23, 26), (24, 30), (25, 34), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (31, 32), (32, 33), (33, 34), (34, 35), (35, 36), (28, 37), (32, 38), (36, 39), (37, 42), (38, 46), (39, 50), (40, 41), (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (47, 48), (48, 49), (49, 50), (40, 51), (44, 52), (48, 53), (51, 54), (52, 58), (53, 62), (54, 55), (55, 56), (56, 57), (57, 58), (58, 59), (59, 60), (60, 61), (61, 62), (62, 63), (63, 64), (56, 65), (60, 66), (64, 67), (65, 70), (66, 74), (67, 78), (68, 69), (69, 70), (70, 71), (71, 72), (72, 73), (73, 74), (74, 75), (75, 76), (76, 77), (77, 78), (68, 79), (72, 80), (76, 81)] tk_circuit = pytket.cirq.cirq_to_tk(circuit) tk_device = _device_connection_list_to_tket_device(device_connection_list) unit = CompilationUnit(tk_circuit, [ConnectivityPredicate(tk_device)]) passes = SequencePass([ PlacementPass(GraphPlacement(tk_device)), RoutingPass(tk_device, bridge_lookahead=0, bridge_interactions=0) ]) # NO BRIDGE passes.apply(unit) valid = unit.check_all_predicates() assert valid routed_circuit = pytket.cirq.tk_to_cirq(unit.circuit)
pass1 = DecomposeMultiQubitsIBM() # This pass converts all multi-qubit gates into CX and single-qubit gates. So let's create a circuit containing some non-CX multi-qubit gates: from pytket.circuit import Circuit circ = Circuit(3) circ.CRz(0.5, 0, 1) circ.T(2) circ.CSWAP(2, 0, 1) # In order to apply a pass to a circuit, we must first create a `CompilationUnit` from it. We can think of this as a 'bridge' between the circuit and the pass. The `CompilationUnit` is constructed from the circuit; the pass is applied to the `CompilationUnit`; and the transformed circuit is extracted from the `CompilationUnit`: from pytket.predicates import CompilationUnit cu = CompilationUnit(circ) pass1.apply(cu) circ1 = cu.circuit # Let's have a look at the result of the transformation: print(circ1.get_commands()) # ## Predicates # Every `CompilationUnit` has associated with it a set of 'predicates', which describe target properties that can be checked against the circuit. There are many types of predicates available in `pytket`. For example, the `GateSetPredicate` checks whether all gates in a circuit belong to a particular set: from pytket.predicates import GateSetPredicate from pytket.circuit import OpType pred1 = GateSetPredicate(