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 run(d,fname): f = open(fname, "w") f.write("name,1q,2q,total,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) circ = circuit_from_qasm(os.path.join(d, fname)) # Other useful optimizations include OptimisePhaseGadgets and PauliSimp. # We exclude them here because they make performance worse on our benchmarks. seq_pass=SequencePass([FullPeepholeOptimise(), RemoveRedundancies()]) start = time.perf_counter() seq_pass.apply(circ) stop = time.perf_counter() total_count = circ.n_gates two_count = circ.n_gates_of_type(OpType.CX) one_count = total_count - two_count # note: could potentially convert to other gate sets here with RebaseCustom print("\t Total %d, 1q %d, CNOT %d\n" % (total_count, one_count, two_count)) f.write("%s,%d,%d,%d,%f\n" % (fname, one_count, two_count, total_count, stop - start)) f.close()
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 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
from pytket.extensions.qiskit import qiskit_to_tk, tk_to_qiskit tk_circ = qiskit_to_tk(state_prep_circ) from pytket.passes import ( SequencePass, CliffordSimp, DecomposeBoxes, KAKDecomposition, SynthesiseIBM, ) DecomposeBoxes().apply(tk_circ) optimise = SequencePass([KAKDecomposition(), CliffordSimp(False), SynthesiseIBM()]) optimise.apply(tk_circ) # Display the optimised circuit: print(tk_to_qiskit(tk_circ)) # The Backends in `pytket` abstract away the differences between different devices and simulators as much as possible, allowing painless switching between them. The `pytket_pyquil` package provides two Backends: `ForestBackend` encapsulates both running on physical devices via Rigetti QCS and simulating those devices on the QVM, and `ForestStateBackend` acts as a wrapper to the pyQuil Wavefunction Simulator. # # Both of these still have a few restrictions on the circuits that can be run. Each only supports a subset of the gate types available in `pytket`, and a real device or associated simulation will have restricted qubit connectivity. The Backend objects will contain a default compilation pass that will statisfy these constraints as much as possible, with minimal or no optimisation. # # The `ForestStateBackend` will allow us to view the full statevector (wavefunction) expected from a perfect execution of the circuit. from pytket.extensions.pyquil import ForestStateBackend state_backend = ForestStateBackend() state_backend.compile_circuit(tk_circ)
(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)
print(circ.get_commands()) # ## Combining passes # There are various ways to combine the elementary passes into more complex ones. # # To combine several passes in sequence, we use a `SequencePass`: from pytket.passes import SequencePass, OptimisePhaseGadgets seqpass = SequencePass([DecomposeMultiQubitsIBM(), OptimisePhaseGadgets()]) # This pass will apply the two transforms in succession: cu = CompilationUnit(circ) seqpass.apply(cu) circ1 = cu.circuit print(circ1.get_commands()) # The `apply()` method for an elementary pass returns a boolean indicating whether or not the pass had any effect on the circuit. For a `SequencePass`, the return value indicates whether _any_ of the constituent passes had some effect. # # A `RepeatPass` repeatedly calls `apply()` on a pass until it returns `False`, indicating that there was no effect: from pytket.passes import CommuteThroughMultis, RemoveRedundancies, RepeatPass seqpass = SequencePass([CommuteThroughMultis(), RemoveRedundancies()]) reppass = RepeatPass(seqpass) # This pass will repeatedly apply `CommuteThroughMultis` (which commutes single-qubit operations through multi-qubit operations where possible towards the start of the circuit) and `RemoveRedundancies` (which cancels inverse pairs, merges coaxial rotations and removes redundant gates before measurement) until neither pass has any effect on the circuit. # # Let's use Qiskit's visualizer to see the effect on a circuit: