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) 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(), 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 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 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 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(), RebaseIBM()]) elif optimisation_level == 1: return SequencePass([DecomposeBoxes(), SynthesiseIBM()]) else: return SequencePass([DecomposeBoxes(), FullPeepholeOptimise()])
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 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 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)
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
# 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()
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(), RebaseCirq(), RemoveRedundancies(), _cirq_squash, ] )
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)
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()