def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass( [DecomposeClassicalExp(), DecomposeBoxes(), RebaseHQS()]) elif optimisation_level == 1: return SequencePass([ DecomposeClassicalExp(), DecomposeBoxes(), SynthesiseIBM(), RebaseHQS(), RemoveRedundancies(), SquashHQS(), ]) else: return SequencePass([ DecomposeClassicalExp(), DecomposeBoxes(), FullPeepholeOptimise(), RebaseHQS(), RemoveRedundancies(), SquashHQS(), ])
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass([ DecomposeBoxes(), FlattenRegisters(), RenameQubitsPass(self._qm), ionq_pass, ]) elif optimisation_level == 1: return SequencePass([ DecomposeBoxes(), SynthesiseIBM(), FlattenRegisters(), RenameQubitsPass(self._qm), ionq_pass, ]) else: return SequencePass([ DecomposeBoxes(), FullPeepholeOptimise(), FlattenRegisters(), RenameQubitsPass(self._qm), ionq_pass, SquashCustom( ionq_singleqs, lambda a, b, c: Circuit(1).Rz(c, 0).Rx(b, 0).Rz(a, 0), ), ])
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 default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass([ FlattenRegisters(), RenameQubitsPass(self._qm), DecomposeBoxes(), _aqt_rebase(), ]) elif optimisation_level == 1: return SequencePass([ DecomposeBoxes(), SynthesiseIBM(), FlattenRegisters(), RenameQubitsPass(self._qm), _aqt_rebase(), RemoveRedundancies(), EulerAngleReduction(OpType.Ry, OpType.Rx), ]) else: return SequencePass([ DecomposeBoxes(), FullPeepholeOptimise(), FlattenRegisters(), RenameQubitsPass(self._qm), _aqt_rebase(), RemoveRedundancies(), EulerAngleReduction(OpType.Ry, OpType.Rx), ])
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 default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass([DecomposeBoxes(), RebaseIBM()]) elif optimisation_level == 1: return SequencePass([DecomposeBoxes(), SynthesiseIBM()]) else: return SequencePass([DecomposeBoxes(), FullPeepholeOptimise()])
def _optimize(self, c): c_tket = pyzx_to_tk(c) cost = lambda c : c.n_gates_of_type(OpType.CX) comp = RepeatWithMetricPass(SequencePass([CommuteThroughMultis(), RemoveRedundancies()]), cost) comp.apply(c_tket) c_opt = tk_to_pyzx(c_tket) return c_opt
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passes = [DecomposeBoxes()] if optimisation_level == 1: passes.append(SynthesiseIBM()) elif optimisation_level == 2: passes.append(FullPeepholeOptimise()) passes.append(self._rebase_pass) if self._device_type == _DeviceType.QPU: passes.append( CXMappingPass( self._tket_device, NoiseAwarePlacement(self._tket_device), directed_cx=False, delay_measures=True, )) # If CX weren't supported by the device then we'd need to do another # rebase_pass here. But we checked above that it is. if optimisation_level == 1: passes.extend([RemoveRedundancies(), self._squash_pass]) if optimisation_level == 2: passes.extend([ CliffordSimp(False), SynthesiseIBM(), self._rebase_pass, self._squash_pass, ]) return SequencePass(passes)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [DecomposeBoxes()] if optimisation_level == 0: passlist.append(self._rebase_pass) elif optimisation_level == 1: passlist.append(SynthesiseIBM()) else: passlist.append(FullPeepholeOptimise()) if self._noise_model and self._device: passlist.append( CXMappingPass( self._device, NoiseAwarePlacement(self._device), directed_cx=True, delay_measures=False, )) if optimisation_level == 0: passlist.append(self._rebase_pass) elif optimisation_level == 1: passlist.append(SynthesiseIBM()) else: passlist.extend([CliffordSimp(False), SynthesiseIBM()]) return SequencePass(passlist)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [ DecomposeBoxes(), FlattenRegisters(), ] if optimisation_level == 1: passlist.append(SynthesiseIBM()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) passlist.append( CXMappingPass( self._device, NoiseAwarePlacement(self._device), directed_cx=False, delay_measures=True, )) if optimisation_level == 2: passlist.append(CliffordSimp(False)) if optimisation_level > 0: passlist.append(SynthesiseIBM()) passlist.append(RebaseQuil()) if optimisation_level > 0: passlist.append(EulerAngleReduction(OpType.Rx, OpType.Rz)) return SequencePass(passlist)
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 default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [DecomposeBoxes(), FlattenRegisters()] if optimisation_level == 1: passlist.append(SynthesiseIBM()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) passlist.append(RebaseQuil()) if optimisation_level > 0: passlist.append(EulerAngleReduction(OpType.Rx, OpType.Rz)) return SequencePass(passlist)
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 default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: """ A suggested compilation pass that will guarantee the resulting circuit will be suitable to run on this backend with as few preconditions as possible. :param optimisation_level: The level of optimisation to perform during compilation. Level 0 just solves the device constraints without optimising. Level 1 additionally performs some light optimisations. Level 2 adds more intensive optimisations that can increase compilation time for large circuits. Defaults to 1. :type optimisation_level: int, optional :return: Compilation pass guaranteeing required predicates. :rtype: BasePass """ assert optimisation_level in range(3) cx_circ = Circuit(2) cx_circ.Sdg(0) cx_circ.V(1) cx_circ.Sdg(1) cx_circ.Vdg(1) cx_circ.add_gate(OpType.ZZMax, [0, 1]) cx_circ.Vdg(1) cx_circ.Sdg(1) cx_circ.add_phase(0.5) def sq(a, b, c): circ = Circuit(1) if c != 0: circ.Rz(c, 0) if b != 0: circ.Rx(b, 0) if a != 0: circ.Rz(a, 0) return circ rebase = RebaseCustom({OpType.ZZMax}, cx_circ, {OpType.Rx, OpType.Ry, OpType.Rz}, sq) squash = SquashCustom({OpType.Rz, OpType.Rx, OpType.Ry}, sq) seq = [DecomposeBoxes()] # Decompose boxes into basic gates if optimisation_level == 1: seq.append(SynthesiseIBM()) # Optional fast optimisation elif optimisation_level == 2: seq.append(FullPeepholeOptimise()) # Optional heavy optimisation seq.append(rebase) # Map to target gate set if optimisation_level != 0: seq.append( squash) # Optionally simplify 1qb gate chains within this gate set return SequencePass(seq)
def gen_tket_pass(optimisepass, backend: str): if backend == _BACKEND_FULL: return optimisepass elif backend == _BACKEND_RIGETTI: final_pass = RebaseQuil() device = rigetti_device elif backend == _BACKEND_GOOGLE: final_pass = RebaseCirq() device = google_sycamore_device elif backend == _BACKEND_IBM: final_pass = RebaseIBM() device = ibm_rochester_device mapper = CXMappingPass(device, GraphPlacement(device)) total_pass = SequencePass( [optimisepass, mapper, SynthesiseIBM(), final_pass]) return total_pass
def gen_tket_pass(optimisepass, backend: str): if backend == _BACKEND_FULL: return optimisepass elif backend == _BACKEND_RIGETTI: final_pass = RebaseQuil() device = rigetti_device elif backend == _BACKEND_GOOGLE: final_pass = RebaseCirq() device = google_sycamore_device elif backend == _BACKEND_IBM: final_pass = RebaseIBM() device = ibm_rochester_device router = DefaultMappingPass(device) #decomp = gen_decompose_routing_gates_to_cxs_pass(device) ###ignoring connectivity decomp = DecomposeSwapsToCXs(device) total_pass = SequencePass( [optimisepass, router, decomp, SynthesiseIBM(), final_pass]) return total_pass
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [DecomposeBoxes()] if optimisation_level == 0: passlist.append(self._rebase_pass) elif optimisation_level == 1: passlist.append(SynthesiseIBM()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) passlist.append( CXMappingPass( self._device, NoiseAwarePlacement(self._device), directed_cx=False, delay_measures=(not self._mid_measure), )) if optimisation_level == 1: passlist.append(SynthesiseIBM()) if optimisation_level == 2: passlist.extend([CliffordSimp(False), SynthesiseIBM()]) if not self._legacy_gateset: passlist.extend([self._rebase_pass, RemoveRedundancies()]) return SequencePass(passlist)
# Now that we have a circuit, `pytket` can take this and start operating on it directly. For example, we can apply some basic compilation passes to simplify it. 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()
grover_optimizer = GroverOptimizer(6, num_iterations=10, quantum_instance=qi) results = grover_optimizer.solve(qp) print("x={}".format(results.x)) print("fval={}".format(results.fval)) print("n_circs={}".format(len(results.operation_counts))) # For this particular run, the case of compiling with Qiskit converged with fewer circuit executions but the additional noise incurred in running the circuit caused the optimiser to miss the global optimum, whereas the case for `pytket` eventually reached the true minimum value. # # When using `TketPass` there is a conversion between `qiskit.DAGCircuit` and `pytket.Circuit`, meaning the circuit needs to be in a form suitable for conversion (i.e. the gates used have to be supported by both `qiskit` and `pytket`). If you encounter any issues with using this, we recommend using `Unroller(['cx', 'u1', 'u2', 'u3'])` and `RebaseIBM` at the point before conversion to guarantee appropriate gates are used. from pytket.passes import RebaseIBM, SequencePass seq = SequencePass( [ # Insert pytket pass of choice RebaseIBM(), ] ) tp = TketPass(seq) pm = PassManager( [ # Insert initial qiskit passes Unroller(["cx", "p", "u"]), tp, # Insert final qiskit passes ] ) qi = QuantumInstance(backend, pass_manager=pm) # Similarly, when using `TketBackend` it may be necessary to include some compilation in `qiskit` to enable the conversion into `pytket`, and then some further `pytket` compilation to get it suitable for the actual target backend. For example, `qiskit.circuit.library.standard_gates.DCXGate` currently does not have an equivalent elementary operation in `pytket`, so must be decomposed before we can map across, and likewise the `OpType.ZZMax` gate used by `pytket.extensions.honeywell.HoneywellBackend` (from the `pytket-honeywell` extension) has no equivalent in `qiskit` so the targeting of the final gateset must be performed by `pytket`.
# ### In-place compilation # The example above produced a new circuit, leaving the original circuit untouched. It is also possible to apply a pass to a circuit in-place: DecomposeMultiQubitsIBM().apply(circ) 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()])
(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)
tk_circ = qiskit_to_tk(q_circ) backend = FakeMelbourne() coupling_list = backend.configuration().coupling_map coupling_map = CouplingMap(coupling_list) characterisation = process_characterisation(backend) directed_arc = Device( characterisation.get("NodeErrors", {}), characterisation.get("EdgeErrors", {}), characterisation.get("Architecture", Architecture([])), ) comp_tk = tk_circ.copy() DecomposeBoxes().apply(comp_tk) FullPeepholeOptimise().apply(comp_tk) CXMappingPass(directed_arc, NoiseAwarePlacement(directed_arc), directed_cx=True, delay_measures=True).apply(comp_tk) DecomposeSwapsToCXs(directed_arc).apply(comp_tk) cost = lambda c: c.n_gates_of_type(OpType.CX) comp = RepeatWithMetricPass( SequencePass( [CommuteThroughMultis(), RemoveRedundancies(), CliffordSimp(False)]), cost) comp.apply(comp_tk) SynthesiseIBM().apply(comp_tk)
from qiskit.compiler import transpile from qiskit.transpiler import CouplingMap if comp_pass == _PASS_FULLPASS: # Default comp_pass = _PASS_QISO3 outfile = "{set}Results_{comp}_{cpass}_{back}.csv".format( set=set_outfile_str[test_set], comp=compiler_outfile_str[compiler], cpass=pass_outfile_str[comp_pass], back=backend_outfile_str[backend]) ### optimisation passes: tketpass = FullPeepholeOptimise() if compiler == _COMPILER_TKET and comp_pass == _PASS_CHEMPASS: tketpass = SequencePass([PauliSimp(), FullPeepholeOptimise()]) all_to_all_coupling = list() for i in range(53): for j in range(i + 1, 53): all_to_all_coupling.append([i, j]) rigetti_coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], [15, 8], [2, 15], [3, 14]] google_coupling = [[0, 5], [1, 5], [1, 6], [2, 6], [2, 7], [3, 8], [3, 9], [4, 9], [4, 10], [5, 11], [5, 12], [6, 12], [6, 13], [7, 13], [7, 14], [8, 14], [8, 15], [9, 15], [9, 16], [10, 16], [11, 17], [12, 17], [12, 18], [13, 18], [13, 19], [14, 19], [14, 20], [15, 20], [15, 21], [16, 21], [16, 22],
def compiler_pass(backend): return SequencePass( [OptimisePhaseGadgets(), backend.default_compilation_pass()])
OpType.Rz, OpType.Rx, OpType.Ry, OpType.Z, OpType.X, OpType.Y, OpType.V, OpType.Vdg, OpType.S, OpType.Sdg, OpType.H, }, _tk1_to_phasedxrz_clifford, ) regular_pass_0 = SequencePass([FlattenRegisters(), DecomposeBoxes(), RebaseCirq()]) regular_pass_1 = SequencePass( [ FlattenRegisters(), DecomposeBoxes(), SynthesiseIBM(), RebaseCirq(), RemoveRedundancies(), _cirq_squash, ] ) regular_pass_2 = SequencePass( [ FlattenRegisters(), DecomposeBoxes(), FullPeepholeOptimise(),