From 83d83d22c7cd80e63752c5e8687e90a60ded8fdf Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:00:23 +0100 Subject: [PATCH 01/39] add lightsabre custom pass --- pytket/extensions/qiskit/backends/aer.py | 35 ++++------------ pytket/extensions/qiskit/backends/ibm.py | 42 +++---------------- .../extensions/qiskit/backends/ibm_utils.py | 36 +++++++++++++++- .../qiskit/backends/ibmq_emulator.py | 4 +- pytket/extensions/qiskit/qiskit_convert.py | 3 +- 5 files changed, 51 insertions(+), 69 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index ba64c275..dc6f374d 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -36,16 +36,15 @@ from pytket.passes import ( BasePass, CliffordSimp, - CXMappingPass, DecomposeBoxes, FullPeepholeOptimise, SequencePass, SynthesiseTket, AutoRebase, NaivePlacementPass, + CustomPass, ) from pytket.pauli import Pauli, QubitPauliString -from pytket.placement import NoiseAwarePlacement from pytket.predicates import ( ConnectivityPredicate, GateSetPredicate, @@ -60,7 +59,7 @@ from pytket.utils.results import KwargTypes from pytket.utils import prepare_circuit -from .ibm_utils import _STATUS_MAP, _batch_circuits +from .ibm_utils import _STATUS_MAP, _batch_circuits, _gen_lightsabre_transformation from .._metadata import __extension_version__ from ..qiskit_convert import ( tk_to_qiskit, @@ -159,32 +158,10 @@ def _arch_dependent_default_compilation_pass( self, arch: Architecture, optimisation_level: int = 2, - placement_options: Optional[dict[str, Any]] = None, ) -> BasePass: assert optimisation_level in range(3) - if placement_options is not None: - noise_aware_placement = NoiseAwarePlacement( - arch, - self._backend_info.averaged_node_gate_errors, # type: ignore - self._backend_info.averaged_edge_gate_errors, # type: ignore - self._backend_info.averaged_readout_errors, # type: ignore - **placement_options, - ) - else: - noise_aware_placement = NoiseAwarePlacement( - arch, - self._backend_info.averaged_node_gate_errors, # type: ignore - self._backend_info.averaged_edge_gate_errors, # type: ignore - self._backend_info.averaged_readout_errors, # type: ignore - ) - arch_specific_passes = [ - CXMappingPass( - arch, - noise_aware_placement, - directed_cx=True, - delay_measures=False, - ), + CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)), NaivePlacementPass(arch), ] if optimisation_level == 0: @@ -195,6 +172,7 @@ def _arch_dependent_default_compilation_pass( *arch_specific_passes, self.rebase_pass(), ] + , False ) if optimisation_level == 1: return SequencePass( @@ -204,6 +182,7 @@ def _arch_dependent_default_compilation_pass( *arch_specific_passes, SynthesiseTket(), ] + , False ) return SequencePass( [ @@ -213,6 +192,7 @@ def _arch_dependent_default_compilation_pass( CliffordSimp(False), SynthesiseTket(), ] + , False ) def _arch_independent_default_compilation_pass( @@ -228,7 +208,6 @@ def _arch_independent_default_compilation_pass( def default_compilation_pass( self, optimisation_level: int = 2, - placement_options: Optional[dict[str, Any]] = None, ) -> BasePass: """ See documentation for :py:meth:`IBMQBackend.default_compilation_pass`. @@ -240,7 +219,7 @@ def default_compilation_pass( and self._backend_info.get_misc("characterisation") ): return self._arch_dependent_default_compilation_pass( - arch, optimisation_level, placement_options=placement_options # type: ignore + arch, optimisation_level # type: ignore ) return self._arch_independent_default_compilation_pass(optimisation_level) diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 8669ac32..a1eacddc 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -54,11 +54,11 @@ from pytket.passes import ( BasePass, AutoRebase, + CustomPass, KAKDecomposition, RemoveRedundancies, SequencePass, SynthesiseTket, - CXMappingPass, DecomposeBoxes, FullPeepholeOptimise, CliffordSimp, @@ -77,11 +77,10 @@ ) from pytket.architecture import FullyConnected, Architecture -from pytket.placement import NoiseAwarePlacement from pytket.utils import prepare_circuit from pytket.utils.outcomearray import OutcomeArray from pytket.utils.results import KwargTypes -from .ibm_utils import _STATUS_MAP, _batch_circuits +from .ibm_utils import _STATUS_MAP, _batch_circuits, _gen_lightsabre_transformation from .config import QiskitConfig from ..qiskit_convert import tk_to_qiskit, _tk_gate_set from ..qiskit_convert import ( @@ -292,7 +291,7 @@ def _get_backend_info( averaged_node_gate_errors=averaged_errors["node_errors"], averaged_edge_gate_errors=averaged_errors["edge_errors"], # type: ignore averaged_readout_errors=averaged_errors["readout_errors"], - misc={"characterisation": filtered_characterisation}, + misc={"characterisation": filtered_characterisation }, ) return backend_info @@ -342,7 +341,6 @@ def required_predicates(self) -> list[Predicate]: def default_compilation_pass( self, optimisation_level: int = 2, - placement_options: Optional[dict[str, Any]] = None, ) -> BasePass: """ A suggested compilation pass that will will, if possible, produce an equivalent @@ -356,8 +354,6 @@ def default_compilation_pass( is tailored to the backend's requirements. The default compilation passes for the :py:class:`IBMQBackend` and the - Aer simulators support an optional ``placement_options`` dictionary containing - arguments to override the default settings in :py:class:`NoiseAwarePlacement`. :param optimisation_level: The level of optimisation to perform during compilation. @@ -369,22 +365,20 @@ def default_compilation_pass( that should give the best results from execution. - :param placement_options: Optional argument allowing the user to override - the default settings in :py:class:`NoiseAwarePlacement`. :return: Compilation pass guaranteeing required predicates. """ config: PulseBackendConfiguration = self._backend.configuration() props: Optional[BackendProperties] = self._backend.properties() return IBMQBackend.default_compilation_pass_offline( - config, props, optimisation_level, placement_options + config, props, optimisation_level ) + @staticmethod def default_compilation_pass_offline( config: PulseBackendConfiguration, props: Optional[BackendProperties], optimisation_level: int = 2, - placement_options: Optional[dict[str, Any]] = None, ) -> BasePass: backend_info = IBMQBackend._get_backend_info(config, props) primitive_gates = _get_primitive_gates(_tk_gate_set(config)) @@ -406,34 +400,10 @@ def default_compilation_pass_offline( passlist.append(SynthesiseTket()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) - mid_measure = backend_info.supports_midcircuit_measurement arch = backend_info.architecture assert arch is not None if not isinstance(arch, FullyConnected): - if placement_options is not None: - noise_aware_placement = NoiseAwarePlacement( - arch, - backend_info.averaged_node_gate_errors, # type: ignore - backend_info.averaged_edge_gate_errors, # type: ignore - backend_info.averaged_readout_errors, # type: ignore - **placement_options, - ) - else: - noise_aware_placement = NoiseAwarePlacement( - arch, - backend_info.averaged_node_gate_errors, # type: ignore - backend_info.averaged_edge_gate_errors, # type: ignore - backend_info.averaged_readout_errors, # type: ignore - ) - - passlist.append( - CXMappingPass( - arch, - noise_aware_placement, - directed_cx=True, - delay_measures=(not mid_measure), - ) - ) + passlist.append(CustomPass(_gen_lightsabre_transformation(arch, optimisation_level))) passlist.append(NaivePlacementPass(arch)) if optimisation_level == 1: passlist.append(SynthesiseTket()) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 19292c76..4744579f 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -16,14 +16,21 @@ """ import itertools -from typing import Collection, Optional, Sequence, TYPE_CHECKING +from typing import Collection, Optional, Sequence, TYPE_CHECKING, Tuple, List, Callable import numpy as np from qiskit.providers import JobStatus # type: ignore +from qiskit.transpiler import PassManager, CouplingMap # type: ignore +from qiskit.transpiler.preset_passmanagers.builtin_plugins import SabreLayoutPassManager # type: ignore +from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore +from pytket import Circuit +from pytket.architecture import Architecture from pytket.backends.status import StatusEnum +from ..qiskit_convert import tk_to_qiskit, qiskit_to_tk + if TYPE_CHECKING: from pytket.circuit import Circuit @@ -70,3 +77,30 @@ def _batch_circuits( for n, indices in itertools.groupby(order, key=lambda i: n_shots[i]) ] return batches, batch_order + +def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: + + # we can make some assumptions from how the Architecture object is + # constructed from the Qiskit CouplingMap + # 1) All nodes are single indexed + # 2) All nodes are default register + # 3) Node with index "i" corresponds to integer "i" in the original coupling map + # We confirm assumption 1) and 2) while producing the coupling map + coupling_map: List[Tuple[int, int]] = [] + for edge in architecture.coupling: + assert len(edge[0].index) == 1 + assert len(edge[1].index) == 1 + assert edge[0].reg_name == "node" + assert edge[1].reg_name == "node" + coupling_map.append((edge[0].index[0], edge[1].index[0])) + + return CouplingMap(coupling_map) + +def _gen_lightsabre_transformation(architecture: Architecture, optimization_level: int = 2, seed = 0) -> Callable[Circuit, Circuit]: + + config: PassManagerConfig = PassManagerConfig(coupling_map = _architecture_to_couplingmap(architecture), routing_method="sabre", seed_transpiler = seed) + def lightsabre(circuit: Circuit) -> Circuit: + sabre_pass: PassManager = SabreLayoutPassManager().pass_manager(config, optimization_level=optimization_level) + return qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) + + return lightsabre diff --git a/pytket/extensions/qiskit/backends/ibmq_emulator.py b/pytket/extensions/qiskit/backends/ibmq_emulator.py index 77b25e61..d2d1a249 100644 --- a/pytket/extensions/qiskit/backends/ibmq_emulator.py +++ b/pytket/extensions/qiskit/backends/ibmq_emulator.py @@ -16,7 +16,6 @@ from typing import ( Optional, Sequence, - Any, ) from qiskit_aer.noise.noise_model import NoiseModel # type: ignore @@ -85,13 +84,12 @@ def required_predicates(self) -> list[Predicate]: def default_compilation_pass( self, optimisation_level: int = 2, - placement_options: Optional[dict[str, Any]] = None, ) -> BasePass: """ See documentation for :py:meth:`IBMQBackend.default_compilation_pass`. """ return self._ibmq.default_compilation_pass( - optimisation_level=optimisation_level, placement_options=placement_options + optimisation_level=optimisation_level ) @property diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index cadea2ae..7dd17ead 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -1026,7 +1026,8 @@ def return_value_if_found(iterator: Iterable["Nduv"], name: str) -> Optional[Any characterisation["t2times"] = t2_times characterisation["Frequencies"] = frequencies characterisation["GateTimes"] = gate_times - + characterisation["CouplingMap"] = coupling_map + return characterisation From ed4d691359e2ee9c5b8d7a2dd4fc30b8fcc1de6e Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:47:06 +0100 Subject: [PATCH 02/39] reformat, add descriptions --- pytket/extensions/qiskit/backends/aer.py | 12 +++---- pytket/extensions/qiskit/backends/ibm.py | 7 ++-- .../extensions/qiskit/backends/ibm_utils.py | 34 +++++++++++++++---- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index dc6f374d..cfdff475 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -171,8 +171,8 @@ def _arch_dependent_default_compilation_pass( self.rebase_pass(), *arch_specific_passes, self.rebase_pass(), - ] - , False + ], + False, ) if optimisation_level == 1: return SequencePass( @@ -181,8 +181,8 @@ def _arch_dependent_default_compilation_pass( SynthesiseTket(), *arch_specific_passes, SynthesiseTket(), - ] - , False + ], + False, ) return SequencePass( [ @@ -191,8 +191,8 @@ def _arch_dependent_default_compilation_pass( *arch_specific_passes, CliffordSimp(False), SynthesiseTket(), - ] - , False + ], + False, ) def _arch_independent_default_compilation_pass( diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index a1eacddc..61601a8e 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -291,7 +291,7 @@ def _get_backend_info( averaged_node_gate_errors=averaged_errors["node_errors"], averaged_edge_gate_errors=averaged_errors["edge_errors"], # type: ignore averaged_readout_errors=averaged_errors["readout_errors"], - misc={"characterisation": filtered_characterisation }, + misc={"characterisation": filtered_characterisation}, ) return backend_info @@ -373,7 +373,6 @@ def default_compilation_pass( config, props, optimisation_level ) - @staticmethod def default_compilation_pass_offline( config: PulseBackendConfiguration, @@ -403,7 +402,9 @@ def default_compilation_pass_offline( arch = backend_info.architecture assert arch is not None if not isinstance(arch, FullyConnected): - passlist.append(CustomPass(_gen_lightsabre_transformation(arch, optimisation_level))) + passlist.append( + CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)) + ) passlist.append(NaivePlacementPass(arch)) if optimisation_level == 1: passlist.append(SynthesiseTket()) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 4744579f..d1822158 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -78,10 +78,15 @@ def _batch_circuits( ] return batches, batch_order + def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: + """ + Converts a pytket Architecture object to a Qiskit CouplingMap object. + :param architecture: Architecture to be converted + """ # we can make some assumptions from how the Architecture object is - # constructed from the Qiskit CouplingMap + # originally constructed from the Qiskit CouplingMap: # 1) All nodes are single indexed # 2) All nodes are default register # 3) Node with index "i" corresponds to integer "i" in the original coupling map @@ -93,14 +98,29 @@ def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: assert edge[0].reg_name == "node" assert edge[1].reg_name == "node" coupling_map.append((edge[0].index[0], edge[1].index[0])) - return CouplingMap(coupling_map) - -def _gen_lightsabre_transformation(architecture: Architecture, optimization_level: int = 2, seed = 0) -> Callable[Circuit, Circuit]: - - config: PassManagerConfig = PassManagerConfig(coupling_map = _architecture_to_couplingmap(architecture), routing_method="sabre", seed_transpiler = seed) + + +def _gen_lightsabre_transformation( + architecture: Architecture, optimization_level: int = 2, seed=0 +) -> Callable[Circuit, Circuit]: + """ + Generates a function that can be passed to CustomPass for running LightSABRE routing. + + :param architecture: Architecture LightSABRE routes circuits to match + :param optimization_level: Corresponds to qiskit optmization levels + :param seed: LightSABRE routing is stochastic, with this parameter setting the seed + """ + config: PassManagerConfig = PassManagerConfig( + coupling_map=_architecture_to_couplingmap(architecture), + routing_method="sabre", + seed_transpiler=seed, + ) + def lightsabre(circuit: Circuit) -> Circuit: - sabre_pass: PassManager = SabreLayoutPassManager().pass_manager(config, optimization_level=optimization_level) + sabre_pass: PassManager = SabreLayoutPassManager().pass_manager( + config, optimization_level=optimization_level + ) return qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) return lightsabre From 9f7846f813beb7d1bde2038d26e496a53889aaf4 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:48:57 +0100 Subject: [PATCH 03/39] Update qiskit_convert.py --- pytket/extensions/qiskit/qiskit_convert.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 7dd17ead..cadea2ae 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -1026,8 +1026,7 @@ def return_value_if_found(iterator: Iterable["Nduv"], name: str) -> Optional[Any characterisation["t2times"] = t2_times characterisation["Frequencies"] = frequencies characterisation["GateTimes"] = gate_times - characterisation["CouplingMap"] = coupling_map - + return characterisation From 2f0d2320cb8998ba3cc5a241ccef8cb4ea7d6992 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:50:47 +0100 Subject: [PATCH 04/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index d1822158..5a09f353 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -105,7 +105,8 @@ def _gen_lightsabre_transformation( architecture: Architecture, optimization_level: int = 2, seed=0 ) -> Callable[Circuit, Circuit]: """ - Generates a function that can be passed to CustomPass for running LightSABRE routing. + Generates a function that can be passed to CustomPass for running + LightSABRE routing. :param architecture: Architecture LightSABRE routes circuits to match :param optimization_level: Corresponds to qiskit optmization levels From 2ba3eae85af87dc1d3c7db46ffee2a0a4233030c Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:50:56 +0100 Subject: [PATCH 05/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 5a09f353..ee245350 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -105,7 +105,7 @@ def _gen_lightsabre_transformation( architecture: Architecture, optimization_level: int = 2, seed=0 ) -> Callable[Circuit, Circuit]: """ - Generates a function that can be passed to CustomPass for running + Generates a function that can be passed to CustomPass for running LightSABRE routing. :param architecture: Architecture LightSABRE routes circuits to match From 100d4c33c31c6f5280f080948fff63abe1d86fa6 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:09:00 +0100 Subject: [PATCH 06/39] Update ibm.py --- pytket/extensions/qiskit/backends/ibm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 61601a8e..84a109a3 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -421,7 +421,7 @@ def default_compilation_pass_offline( passlist.extend( [IBMQBackend.rebase_pass_offline(primitive_gates), RemoveRedundancies()] ) - return SequencePass(passlist) + return SequencePass(passlist, False) @property def _result_id_type(self) -> _ResultIdTuple: From 68c9005e5c5bb579d08e96c31d76908ee757811c Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:16:33 +0100 Subject: [PATCH 07/39] redirect CX gates --- pytket/extensions/qiskit/backends/aer.py | 4 +++- pytket/extensions/qiskit/backends/ibm.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index cfdff475..8f988d43 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -55,6 +55,7 @@ DefaultRegisterPredicate, Predicate, ) +from pytket.transform import Transform from pytket.utils.operators import QubitPauliOperator from pytket.utils.results import KwargTypes from pytket.utils import prepare_circuit @@ -162,6 +163,7 @@ def _arch_dependent_default_compilation_pass( assert optimisation_level in range(3) arch_specific_passes = [ CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)), + passlist.append(CustomPass(Transform.DecomposeCXDirected(arch))), NaivePlacementPass(arch), ] if optimisation_level == 0: @@ -761,7 +763,7 @@ def _process_noise_model( qubits_with_link_errors.add(q0) qubits_with_link_errors.add(q1) # to simulate a worse reverse direction square the fidelity - link_errors[(Node(q1), Node(q0))].update({optype: float(1 - gate_fid**2)}) + link_errors[(Node(q1), Node(q0))].update({optype: float(1 - gate_fid ** 2)}) generic_2q_qerrors_dict[(q0, q1)].append( [error["instructions"], error["probabilities"]] ) diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 84a109a3..6e9b4f34 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -75,6 +75,7 @@ Predicate, DirectednessPredicate, ) +from pytket.transform import Transform from pytket.architecture import FullyConnected, Architecture from pytket.utils import prepare_circuit @@ -405,6 +406,7 @@ def default_compilation_pass_offline( passlist.append( CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)) ) + passlist.append(CustomPass(Transform.DecomposeCXDirected(arch))) passlist.append(NaivePlacementPass(arch)) if optimisation_level == 1: passlist.append(SynthesiseTket()) From 8afa0c513434f751bad5f3c4b385404e75f14661 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:19:10 +0100 Subject: [PATCH 08/39] Update aer.py --- pytket/extensions/qiskit/backends/aer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 8f988d43..18f8fb63 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -763,7 +763,7 @@ def _process_noise_model( qubits_with_link_errors.add(q0) qubits_with_link_errors.add(q1) # to simulate a worse reverse direction square the fidelity - link_errors[(Node(q1), Node(q0))].update({optype: float(1 - gate_fid ** 2)}) + link_errors[(Node(q1), Node(q0))].update({optype: float(1 - gate_fid**2)}) generic_2q_qerrors_dict[(q0, q1)].append( [error["instructions"], error["probabilities"]] ) From d4f494a35c7b1072552e6bb21ac84de3e2ec250f Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:24:37 +0100 Subject: [PATCH 09/39] Update aer.py --- pytket/extensions/qiskit/backends/aer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 18f8fb63..3db01521 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -163,7 +163,7 @@ def _arch_dependent_default_compilation_pass( assert optimisation_level in range(3) arch_specific_passes = [ CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)), - passlist.append(CustomPass(Transform.DecomposeCXDirected(arch))), + CustomPass(Transform.DecomposeCXDirected(arch)), NaivePlacementPass(arch), ] if optimisation_level == 0: From a4f5de279cd65461a6cfbc0dbb912b550a2c0495 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:32:47 +0100 Subject: [PATCH 10/39] move Directed rewrite into `gen_lightsabre_transformation` --- pytket/extensions/qiskit/backends/aer.py | 2 -- pytket/extensions/qiskit/backends/ibm.py | 2 -- pytket/extensions/qiskit/backends/ibm_utils.py | 4 +++- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 3db01521..cfdff475 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -55,7 +55,6 @@ DefaultRegisterPredicate, Predicate, ) -from pytket.transform import Transform from pytket.utils.operators import QubitPauliOperator from pytket.utils.results import KwargTypes from pytket.utils import prepare_circuit @@ -163,7 +162,6 @@ def _arch_dependent_default_compilation_pass( assert optimisation_level in range(3) arch_specific_passes = [ CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)), - CustomPass(Transform.DecomposeCXDirected(arch)), NaivePlacementPass(arch), ] if optimisation_level == 0: diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 6e9b4f34..84a109a3 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -75,7 +75,6 @@ Predicate, DirectednessPredicate, ) -from pytket.transform import Transform from pytket.architecture import FullyConnected, Architecture from pytket.utils import prepare_circuit @@ -406,7 +405,6 @@ def default_compilation_pass_offline( passlist.append( CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)) ) - passlist.append(CustomPass(Transform.DecomposeCXDirected(arch))) passlist.append(NaivePlacementPass(arch)) if optimisation_level == 1: passlist.append(SynthesiseTket()) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index ee245350..18750912 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -28,6 +28,7 @@ from pytket import Circuit from pytket.architecture import Architecture from pytket.backends.status import StatusEnum +from pytket.transform import Transform from ..qiskit_convert import tk_to_qiskit, qiskit_to_tk @@ -122,6 +123,7 @@ def lightsabre(circuit: Circuit) -> Circuit: sabre_pass: PassManager = SabreLayoutPassManager().pass_manager( config, optimization_level=optimization_level ) - return qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) + c: Circuit = qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) + Transform.DecomposeCXDirected(arch).apply(circuit) return lightsabre From ad8b917bd59a44ec853b0e61428efd6d9b5ce7aa Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:33:51 +0100 Subject: [PATCH 11/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 18750912..19c0894e 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -124,6 +124,7 @@ def lightsabre(circuit: Circuit) -> Circuit: config, optimization_level=optimization_level ) c: Circuit = qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) - Transform.DecomposeCXDirected(arch).apply(circuit) + Transform.DecomposeCXDirected(architecture).apply(c) + return c return lightsabre From 185f785a1f4a61ab446ad56dcfbc40df152300a6 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Wed, 16 Oct 2024 12:58:30 +0100 Subject: [PATCH 12/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 19c0894e..bb79dda2 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -25,7 +25,7 @@ from qiskit.transpiler.preset_passmanagers.builtin_plugins import SabreLayoutPassManager # type: ignore from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore -from pytket import Circuit +from pytket import Circuit, Node from pytket.architecture import Architecture from pytket.backends.status import StatusEnum from pytket.transform import Transform @@ -124,6 +124,7 @@ def lightsabre(circuit: Circuit) -> Circuit: config, optimization_level=optimization_level ) c: Circuit = qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) + c.rename_units({q: Node(q.index[0]) for q in c.qubits}) Transform.DecomposeCXDirected(architecture).apply(c) return c From 80e9e899d591a73cf0c9325fe3193ca9cba112c5 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:00:57 +0100 Subject: [PATCH 13/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index bb79dda2..b46c929c 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -25,7 +25,7 @@ from qiskit.transpiler.preset_passmanagers.builtin_plugins import SabreLayoutPassManager # type: ignore from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore -from pytket import Circuit, Node +from pytket.circuit import Circuit, Node from pytket.architecture import Architecture from pytket.backends.status import StatusEnum from pytket.transform import Transform From c1db4939a6411b9b0ad571f0eb21944e92262900 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:37:37 +0100 Subject: [PATCH 14/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index b46c929c..98641b63 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -124,6 +124,7 @@ def lightsabre(circuit: Circuit) -> Circuit: config, optimization_level=optimization_level ) c: Circuit = qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) + c.remove_blank_wires() c.rename_units({q: Node(q.index[0]) for q in c.qubits}) Transform.DecomposeCXDirected(architecture).apply(c) return c From a8ff75940b306ac8e8991f30b287ed886c9da2e6 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:39:09 +0100 Subject: [PATCH 15/39] rebase to CX gates to redirect ECR gates --- pytket/extensions/qiskit/backends/ibm.py | 7 +++---- pytket/extensions/qiskit/backends/ibm_utils.py | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 84a109a3..1e395c7b 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -417,10 +417,9 @@ def default_compilation_pass_offline( ] ) - if supports_rz: - passlist.extend( - [IBMQBackend.rebase_pass_offline(primitive_gates), RemoveRedundancies()] - ) + passlist.extend( + [IBMQBackend.rebase_pass_offline(primitive_gates), RemoveRedundancies()] + ) return SequencePass(passlist, False) @property diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 98641b63..e1a9d924 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -29,6 +29,7 @@ from pytket.architecture import Architecture from pytket.backends.status import StatusEnum from pytket.transform import Transform +from pytket.passes import RebaseTket from ..qiskit_convert import tk_to_qiskit, qiskit_to_tk @@ -126,6 +127,7 @@ def lightsabre(circuit: Circuit) -> Circuit: c: Circuit = qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) c.remove_blank_wires() c.rename_units({q: Node(q.index[0]) for q in c.qubits}) + RebaseTket().apply(c) Transform.DecomposeCXDirected(architecture).apply(c) return c From 10f11427511db31513c0f5252df2ff54efbbe3f1 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:46:23 +0100 Subject: [PATCH 16/39] rebase to CX for noise model routing --- pytket/extensions/qiskit/backends/aer.py | 1 + pytket/extensions/qiskit/backends/ibm.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index cfdff475..5ce3f9f5 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -161,6 +161,7 @@ def _arch_dependent_default_compilation_pass( ) -> BasePass: assert optimisation_level in range(3) arch_specific_passes = [ + AutoRebase({OpType.CX, OpType.TK1}), CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)), NaivePlacementPass(arch), ] diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 1e395c7b..78a286a4 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -402,6 +402,7 @@ def default_compilation_pass_offline( arch = backend_info.architecture assert arch is not None if not isinstance(arch, FullyConnected): + passlist.append(AutoRebase(primitive_gates)) passlist.append( CustomPass(_gen_lightsabre_transformation(arch, optimisation_level)) ) From 09e9787a6676e8181010e06962ad1669e0eb2d79 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:18:44 +0000 Subject: [PATCH 17/39] Update pytket-docs-theming --- docs/pytket-docs-theming | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pytket-docs-theming b/docs/pytket-docs-theming index 45cc4e49..cfbe34c4 160000 --- a/docs/pytket-docs-theming +++ b/docs/pytket-docs-theming @@ -1 +1 @@ -Subproject commit 45cc4e49f473905984b99077e8739fe18e69595e +Subproject commit cfbe34c48f88c56085b8ef65f640d0108b8a9fa6 From 72d1edd19808f446dc661f40ff215ad6b52eae26 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:21:23 +0000 Subject: [PATCH 18/39] Update aer.py --- pytket/extensions/qiskit/backends/aer.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 60c09b39..9ca36f3f 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -35,16 +35,15 @@ AutoRebase, BasePass, CliffordSimp, + CXMappingPass, DecomposeBoxes, FullPeepholeOptimise, NaivePlacementPass, SequencePass, SynthesiseTket, - AutoRebase, - NaivePlacementPass, - CustomPass, ) from pytket.pauli import Pauli, QubitPauliString +from pytket.placement import NoiseAwarePlacement from pytket.predicates import ( ConnectivityPredicate, DefaultRegisterPredicate, @@ -64,7 +63,6 @@ SparsePauliOp, ) -from .ibm_utils import _STATUS_MAP, _batch_circuits, _gen_lightsabre_transformation from .._metadata import __extension_version__ from ..qiskit_convert import ( _gate_str_2_optype, @@ -84,6 +82,7 @@ ) + def _default_q_index(q: Qubit) -> int: if q.reg_name != "q" or len(q.index) != 1: raise ValueError("Non-default qubit register") From 4027b7597e46cb0964fc8cd02e90fc5451579288 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:21:40 +0000 Subject: [PATCH 19/39] Update ibm.py --- pytket/extensions/qiskit/backends/ibm.py | 49 +----------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 52739ba7..3a3e4148 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -86,54 +86,6 @@ # via job_monitor see-> https://github.com/CQCL/pytket-qiskit/issues/48 # therefore we can't use job_monitor until fixed # from qiskit.tools.monitor import job_monitor # type: ignore -from qiskit.result.distributions import QuasiDistribution # type: ignore -from qiskit_ibm_runtime import ( # type: ignore - QiskitRuntimeService, - Session, - SamplerOptions, - SamplerV2, - RuntimeJob, -) -from qiskit_ibm_runtime.models.backend_configuration import PulseBackendConfiguration # type: ignore -from qiskit_ibm_runtime.models.backend_properties import BackendProperties # type: ignore - -from pytket.circuit import Bit, Circuit, OpType -from pytket.backends import Backend, CircuitNotRunError, CircuitStatus, ResultHandle -from pytket.backends.backendinfo import BackendInfo -from pytket.backends.backendresult import BackendResult -from pytket.backends.resulthandle import _ResultIdTuple -from pytket.passes import ( - BasePass, - AutoRebase, - CustomPass, - KAKDecomposition, - RemoveRedundancies, - SequencePass, - SynthesiseTket, - DecomposeBoxes, - FullPeepholeOptimise, - CliffordSimp, - SimplifyInitial, - NaivePlacementPass, -) -from pytket.predicates import ( - NoMidMeasurePredicate, - NoSymbolsPredicate, - GateSetPredicate, - NoClassicalControlPredicate, - NoFastFeedforwardPredicate, - MaxNQubitsPredicate, - Predicate, - DirectednessPredicate, -) - -from pytket.architecture import FullyConnected, Architecture -from pytket.utils import prepare_circuit -from pytket.utils.outcomearray import OutcomeArray -from pytket.utils.results import KwargTypes -from .ibm_utils import _STATUS_MAP, _batch_circuits, _gen_lightsabre_transformation -from .config import QiskitConfig -from ..qiskit_convert import tk_to_qiskit, _tk_gate_set from .._metadata import __extension_version__ from ..qiskit_convert import ( _tk_gate_set, @@ -150,6 +102,7 @@ _DEBUG_HANDLE_PREFIX = "_MACHINE_DEBUG_" + def _gen_debug_results(n_bits: int, shots: int) -> PrimitiveResult: n_u8s = (n_bits - 1) // 8 + 1 arr = np.array([[0] * n_u8s for _ in range(shots)], dtype=np.uint8) From 6d98d2362566e03b9107e73a3293b2310df34c9f Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:22:00 +0000 Subject: [PATCH 20/39] Update ibmq_emulator.py --- pytket/extensions/qiskit/backends/ibmq_emulator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibmq_emulator.py b/pytket/extensions/qiskit/backends/ibmq_emulator.py index 2b1a92b0..4f01d949 100644 --- a/pytket/extensions/qiskit/backends/ibmq_emulator.py +++ b/pytket/extensions/qiskit/backends/ibmq_emulator.py @@ -14,10 +14,9 @@ from collections.abc import Sequence from typing import ( - Optional, - Sequence, TYPE_CHECKING, Any, + Optional, ) from qiskit_aer.noise.noise_model import NoiseModel # type: ignore @@ -40,7 +39,6 @@ from qiskit_ibm_runtime import QiskitRuntimeService # type: ignore - class IBMQEmulatorBackend(Backend): """A backend which uses the AerBackend to loaclly emulate the behaviour of IBMQBackend. Identical to :py:class:`IBMQBackend` except there is no `monitor` From ccb972af31d353f0e40d424b0a5f82951afb16e6 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:22:22 +0000 Subject: [PATCH 21/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 1f8465c8..ac17569b 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -16,23 +16,13 @@ """ import itertools -from typing import Collection, Optional, Sequence, TYPE_CHECKING, Tuple, List, Callable +from collections.abc import Collection, Sequence +from typing import TYPE_CHECKING, Optional import numpy as np -from qiskit.providers import JobStatus # type: ignore -from qiskit.transpiler import PassManager, CouplingMap # type: ignore -from qiskit.transpiler.preset_passmanagers.builtin_plugins import SabreLayoutPassManager # type: ignore -from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore - -from pytket.circuit import Circuit, Node -from pytket.architecture import Architecture from pytket.backends.status import StatusEnum -from pytket.transform import Transform -from pytket.passes import RebaseTket - -from ..qiskit_convert import tk_to_qiskit, qiskit_to_tk - +from qiskit.providers import JobStatus # type: ignore if TYPE_CHECKING: from pytket.circuit import Circuit @@ -54,7 +44,6 @@ "RUNNING": StatusEnum.RUNNING, } - def _batch_circuits( circuits: Sequence["Circuit"], n_shots: Sequence[Optional[int]], From d0d2f6fe6d763f05560a1eaaf39bcc411b285596 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:26:43 +0000 Subject: [PATCH 22/39] fix imports, reformat --- pytket/extensions/qiskit/backends/aer.py | 15 ++++++------- pytket/extensions/qiskit/backends/ibm.py | 6 ++--- .../extensions/qiskit/backends/ibm_utils.py | 22 +++++++++++++------ .../qiskit/backends/ibmq_emulator.py | 2 +- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 9ca36f3f..be61acdc 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -35,7 +35,7 @@ AutoRebase, BasePass, CliffordSimp, - CXMappingPass, + CustomPass, DecomposeBoxes, FullPeepholeOptimise, NaivePlacementPass, @@ -43,7 +43,6 @@ SynthesiseTket, ) from pytket.pauli import Pauli, QubitPauliString -from pytket.placement import NoiseAwarePlacement from pytket.predicates import ( ConnectivityPredicate, DefaultRegisterPredicate, @@ -73,7 +72,7 @@ CrosstalkParams, NoisyCircuitBuilder, ) -from .ibm_utils import _STATUS_MAP, _batch_circuits +from .ibm_utils import _STATUS_MAP, _batch_circuits, _gen_lightsabre_transformation if TYPE_CHECKING: from qiskit_aer import AerJob @@ -82,7 +81,6 @@ ) - def _default_q_index(q: Qubit) -> int: if q.reg_name != "q" or len(q.index) != 1: raise ValueError("Non-default qubit register") @@ -227,7 +225,8 @@ def default_compilation_pass( and self._backend_info.get_misc("characterisation") ): return self._arch_dependent_default_compilation_pass( - arch, optimisation_level # type: ignore + arch, + optimisation_level, # type: ignore ) return self._arch_independent_default_compilation_pass(optimisation_level) @@ -344,9 +343,9 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul include_density_matrix=self._supports_density_matrix, ) for circ_index, backres in enumerate(backresults): - self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)][ - "result" - ] = backres + self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)]["result"] = ( + backres + ) return cast(BackendResult, self._cache[handle]["result"]) diff --git a/pytket/extensions/qiskit/backends/ibm.py b/pytket/extensions/qiskit/backends/ibm.py index 3a3e4148..83b8f5e9 100644 --- a/pytket/extensions/qiskit/backends/ibm.py +++ b/pytket/extensions/qiskit/backends/ibm.py @@ -51,7 +51,7 @@ AutoRebase, BasePass, CliffordSimp, - CXMappingPass, + CustomPass, DecomposeBoxes, FullPeepholeOptimise, KAKDecomposition, @@ -61,7 +61,6 @@ SimplifyInitial, SynthesiseTket, ) -from pytket.placement import NoiseAwarePlacement from pytket.predicates import ( DirectednessPredicate, GateSetPredicate, @@ -94,7 +93,7 @@ tk_to_qiskit, ) from .config import QiskitConfig -from .ibm_utils import _STATUS_MAP, _batch_circuits +from .ibm_utils import _STATUS_MAP, _batch_circuits, _gen_lightsabre_transformation if TYPE_CHECKING: from qiskit_ibm_runtime.ibm_backend import IBMBackend # type: ignore @@ -102,7 +101,6 @@ _DEBUG_HANDLE_PREFIX = "_MACHINE_DEBUG_" - def _gen_debug_results(n_bits: int, shots: int) -> PrimitiveResult: n_u8s = (n_bits - 1) // 8 + 1 arr = np.array([[0] * n_u8s for _ in range(shots)], dtype=np.uint8) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index ac17569b..84cb2788 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -12,20 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Shared utility methods for ibm backends. -""" +"""Shared utility methods for ibm backends.""" import itertools from collections.abc import Collection, Sequence -from typing import TYPE_CHECKING, Optional +from typing import Optional, list, tuple, Callable import numpy as np -from pytket.backends.status import StatusEnum from qiskit.providers import JobStatus # type: ignore +from qiskit.transpiler import PassManager, CouplingMap # type: ignore +from qiskit.transpiler.preset_passmanagers.builtin_plugins import SabreLayoutPassManager # type: ignore +from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore + +from pytket.circuit import Circuit, Node +from pytket.architecture import Architecture +from pytket.backends.status import StatusEnum +from pytket.transform import Transform +from pytket.passes import RebaseTket + +from ..qiskit_convert import tk_to_qiskit, qiskit_to_tk -if TYPE_CHECKING: - from pytket.circuit import Circuit _STATUS_MAP = { JobStatus.CANCELLED: StatusEnum.CANCELLED, @@ -44,6 +51,7 @@ "RUNNING": StatusEnum.RUNNING, } + def _batch_circuits( circuits: Sequence["Circuit"], n_shots: Sequence[Optional[int]], @@ -83,7 +91,7 @@ def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: # 2) All nodes are default register # 3) Node with index "i" corresponds to integer "i" in the original coupling map # We confirm assumption 1) and 2) while producing the coupling map - coupling_map: List[Tuple[int, int]] = [] + coupling_map: list[tuple[int, int]] = [] for edge in architecture.coupling: assert len(edge[0].index) == 1 assert len(edge[1].index) == 1 diff --git a/pytket/extensions/qiskit/backends/ibmq_emulator.py b/pytket/extensions/qiskit/backends/ibmq_emulator.py index 4f01d949..1d213be6 100644 --- a/pytket/extensions/qiskit/backends/ibmq_emulator.py +++ b/pytket/extensions/qiskit/backends/ibmq_emulator.py @@ -15,7 +15,6 @@ from collections.abc import Sequence from typing import ( TYPE_CHECKING, - Any, Optional, ) @@ -39,6 +38,7 @@ from qiskit_ibm_runtime import QiskitRuntimeService # type: ignore + class IBMQEmulatorBackend(Backend): """A backend which uses the AerBackend to loaclly emulate the behaviour of IBMQBackend. Identical to :py:class:`IBMQBackend` except there is no `monitor` From 70ca5b046f7be66ab2d6b2fd2a9536ae1714d04a Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:29:11 +0000 Subject: [PATCH 23/39] Update ibm_utils.py --- .../extensions/qiskit/backends/ibm_utils.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 84cb2788..5af49cc1 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -16,23 +16,23 @@ import itertools from collections.abc import Collection, Sequence -from typing import Optional, list, tuple, Callable +from typing import Callable, Optional import numpy as np -from qiskit.providers import JobStatus # type: ignore -from qiskit.transpiler import PassManager, CouplingMap # type: ignore -from qiskit.transpiler.preset_passmanagers.builtin_plugins import SabreLayoutPassManager # type: ignore -from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore - -from pytket.circuit import Circuit, Node from pytket.architecture import Architecture from pytket.backends.status import StatusEnum -from pytket.transform import Transform +from pytket.circuit import Circuit, Node from pytket.passes import RebaseTket +from pytket.transform import Transform +from qiskit.providers import JobStatus # type: ignore +from qiskit.transpiler import CouplingMap, PassManager # type: ignore +from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore +from qiskit.transpiler.preset_passmanagers.builtin_plugins import ( + SabreLayoutPassManager, # type: ignore +) -from ..qiskit_convert import tk_to_qiskit, qiskit_to_tk - +from ..qiskit_convert import qiskit_to_tk, tk_to_qiskit _STATUS_MAP = { JobStatus.CANCELLED: StatusEnum.CANCELLED, From af6a3bd79cd6d5befe0702eead978675dfb6f4ea Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:31:50 +0000 Subject: [PATCH 24/39] Update aer.py --- pytket/extensions/qiskit/backends/aer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index be61acdc..1a465e22 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -343,9 +343,9 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul include_density_matrix=self._supports_density_matrix, ) for circ_index, backres in enumerate(backresults): - self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)]["result"] = ( - backres - ) + self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)][ + "result" + ] = backres return cast(BackendResult, self._cache[handle]["result"]) From e2472e3b563a70a0f13982197206460873413cd5 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:34:28 +0000 Subject: [PATCH 25/39] Update changelog.md --- docs/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0e06e304..692f6311 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,7 +6,8 @@ ## Unreleased -- Fix handling of non-default registers when selecting bits in results. +- Fix handling of non-default registers when selecting bits in results. +- Update default compilation to use `Qiskit` `SabreLayoutPassManager` as a `CustomPass`. ## 0.58.0 (October 2024) From 38a87126e50b709f2b196ecd67b5dc03cc38c268 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:58:06 +0000 Subject: [PATCH 26/39] Update ibm_utils.py --- .../extensions/qiskit/backends/ibm_utils.py | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 5af49cc1..fac920f7 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -27,10 +27,16 @@ from pytket.transform import Transform from qiskit.providers import JobStatus # type: ignore from qiskit.transpiler import CouplingMap, PassManager # type: ignore +from qiskit.transpiler.passes import SabreLayout, SetLayout # type: ignore from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore from qiskit.transpiler.preset_passmanagers.builtin_plugins import ( SabreLayoutPassManager, # type: ignore ) +from qiskit.passmanager.flow_controllers import ConditionalController # type: ignore + + +from qiskit.transpiler.preset_passmanagers import common + from ..qiskit_convert import qiskit_to_tk, tk_to_qiskit @@ -102,7 +108,7 @@ def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: def _gen_lightsabre_transformation( - architecture: Architecture, optimization_level: int = 2, seed=0 + architecture: Architecture, optimization_level: int = 2, seed=0, attempts=20 ) -> Callable[Circuit, Circuit]: """ Generates a function that can be passed to CustomPass for running @@ -111,17 +117,45 @@ def _gen_lightsabre_transformation( :param architecture: Architecture LightSABRE routes circuits to match :param optimization_level: Corresponds to qiskit optmization levels :param seed: LightSABRE routing is stochastic, with this parameter setting the seed + :param attempts: Number of generated random solutions to pick from. """ config: PassManagerConfig = PassManagerConfig( coupling_map=_architecture_to_couplingmap(architecture), routing_method="sabre", seed_transpiler=seed, ) + vf2_call_limit, vf2_max_trials = common.get_vf2_limits( + optimization_level, + config.layout_method, + config.initial_layout, + ) + + sabre_pass: PassManager = PassManager( + [ + SetLayout(config.initial_layout), + ConditionalController( + [ + SabreLayout( + config.coupling_map, + max_iterations=2, + seed=config.seed_transpiler, + swap_trials=attempts, + layout_trials=attempts, + skip_routing=False, + ) + ], + condition=lambda property_set: not property_set["layout"], + ), + ConditionalController( + common.generate_embed_passmanager( + config.coupling_map + ).to_flow_controller(), + condition=lambda property_set: property_set["final_layout"] is None, + ), + ] + ) def lightsabre(circuit: Circuit) -> Circuit: - sabre_pass: PassManager = SabreLayoutPassManager().pass_manager( - config, optimization_level=optimization_level - ) c: Circuit = qiskit_to_tk(sabre_pass.run(tk_to_qiskit(circuit))) c.remove_blank_wires() c.rename_units({q: Node(q.index[0]) for q in c.qubits}) From 481061a9da4f09e8fd9eda990912463fd6ff493b Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:58:37 +0000 Subject: [PATCH 27/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index fac920f7..3d12844d 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -25,19 +25,13 @@ from pytket.circuit import Circuit, Node from pytket.passes import RebaseTket from pytket.transform import Transform +from qiskit.passmanager.flow_controllers import ConditionalController # type: ignore from qiskit.providers import JobStatus # type: ignore from qiskit.transpiler import CouplingMap, PassManager # type: ignore from qiskit.transpiler.passes import SabreLayout, SetLayout # type: ignore from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore -from qiskit.transpiler.preset_passmanagers.builtin_plugins import ( - SabreLayoutPassManager, # type: ignore -) -from qiskit.passmanager.flow_controllers import ConditionalController # type: ignore - - from qiskit.transpiler.preset_passmanagers import common - from ..qiskit_convert import qiskit_to_tk, tk_to_qiskit _STATUS_MAP = { From e0a4cdd1c729fa2b2bcc29b876f80189b3b09606 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:59:31 +0000 Subject: [PATCH 28/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index 3d12844d..ff261cd4 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -118,12 +118,6 @@ def _gen_lightsabre_transformation( routing_method="sabre", seed_transpiler=seed, ) - vf2_call_limit, vf2_max_trials = common.get_vf2_limits( - optimization_level, - config.layout_method, - config.initial_layout, - ) - sabre_pass: PassManager = PassManager( [ SetLayout(config.initial_layout), From ad572217d0d216ed985927a5140c3b247c269b5c Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:31:28 +0000 Subject: [PATCH 29/39] Update backend_test.py --- tests/backend_test.py | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index 7c165329..fde6ec3c 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -988,46 +988,6 @@ def lift_perm(p: dict[int, int]) -> np.ndarray: return pm -@pytest.mark.skipif(skip_remote_tests, reason=REASON) -def test_compilation_correctness(brisbane_backend: IBMQBackend) -> None: - c = Circuit(7) - 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() - c.remove_blank_wires() - FlattenRelabelRegistersPass().apply(c) - u = u_backend.run_circuit(c).get_unitary() - ibm_backend = brisbane_backend - for ol in range(3): - p = ibm_backend.default_compilation_pass(optimisation_level=ol) - cu = CompilationUnit(c) - p.apply(cu) - FlattenRelabelRegistersPass().apply(cu) - c1 = cu.circuit - compiled_u = u_backend.run_circuit(c1).get_unitary() - - # 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()} # type: ignore - inv_fin = {c1_idx[node]: c_idx[qb] for qb, node in fmap.items()} # type: ignore - m_ini = lift_perm(ini) - m_inv_fin = lift_perm(inv_fin) - - assert compare_statevectors(u[:, 0], (m_inv_fin @ compiled_u @ m_ini)[:, 0]) - - # pytket-extensions issue #69 def test_symbolic_rebase() -> None: circ = QuantumCircuit(2) @@ -1469,7 +1429,6 @@ def test_noiseless_density_matrix_simulation() -> None: # https://github.com/CQCL/pytket-qiskit/issues/231 def test_noisy_density_matrix_simulation() -> None: - # Test that __init__ works with a very simple noise model noise_model = NoiseModel() noise_model.add_quantum_error(depolarizing_error(0.6, 2), ["cz"], [0, 1]) From 4fd3537d6d2f7da542a32f96c1146f214b851240 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:36:16 +0000 Subject: [PATCH 30/39] Update backend_test.py --- tests/backend_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index fde6ec3c..62b53c01 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -65,7 +65,7 @@ NoisyCircuitBuilder, ) from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod, MappingManager -from pytket.passes import CliffordSimp, FlattenRelabelRegistersPass +from pytket.passes import CliffordSimp from pytket.pauli import Pauli, QubitPauliString from pytket.predicates import CompilationUnit, NoMidMeasurePredicate from pytket.transform import Transform From 8c3a7586af39e141daf697b5f331ab0dde6b4bf5 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:47:15 +0000 Subject: [PATCH 31/39] reformat, fix mypy --- pytket/extensions/qiskit/backends/aer.py | 8 ++++---- pytket/extensions/qiskit/backends/ibm_utils.py | 4 ++-- pytket/extensions/qiskit/qiskit_convert.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 1a465e22..dbbf2df7 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -225,7 +225,7 @@ def default_compilation_pass( and self._backend_info.get_misc("characterisation") ): return self._arch_dependent_default_compilation_pass( - arch, + arch, # type: ignore optimisation_level, # type: ignore ) @@ -343,9 +343,9 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul include_density_matrix=self._supports_density_matrix, ) for circ_index, backres in enumerate(backresults): - self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)][ - "result" - ] = backres + self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)]["result"] = ( + backres + ) return cast(BackendResult, self._cache[handle]["result"]) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index ff261cd4..d7987bab 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -30,7 +30,7 @@ from qiskit.transpiler import CouplingMap, PassManager # type: ignore from qiskit.transpiler.passes import SabreLayout, SetLayout # type: ignore from qiskit.transpiler.passmanager_config import PassManagerConfig # type: ignore -from qiskit.transpiler.preset_passmanagers import common +from qiskit.transpiler.preset_passmanagers import common # type: ignore from ..qiskit_convert import qiskit_to_tk, tk_to_qiskit @@ -103,7 +103,7 @@ def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: def _gen_lightsabre_transformation( architecture: Architecture, optimization_level: int = 2, seed=0, attempts=20 -) -> Callable[Circuit, Circuit]: +) -> Callable[[Circuit], Circuit]: """ Generates a function that can be passed to CustomPass for running LightSABRE routing. diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 49dcb584..5283f312 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -29,7 +29,7 @@ from uuid import UUID import numpy as np -import sympy +import sympy # type: ignore from numpy.typing import NDArray from qiskit_ibm_runtime.models.backend_configuration import ( # type: ignore PulseBackendConfiguration, @@ -69,7 +69,7 @@ ClassicalRegister, QuantumCircuit, QuantumRegister, -) +) # type: ignore from qiskit.circuit import ( Barrier, Clbit, From 17939e0518dcf4f1ae5e0fec4b9c70ebcffeff64 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:50:01 +0000 Subject: [PATCH 32/39] reformat --- pytket/extensions/qiskit/backends/aer.py | 6 +++--- pytket/extensions/qiskit/qiskit_convert.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index dbbf2df7..57524ddf 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -343,9 +343,9 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul include_density_matrix=self._supports_density_matrix, ) for circ_index, backres in enumerate(backresults): - self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)]["result"] = ( - backres - ) + self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)][ + "result" + ] = backres return cast(BackendResult, self._cache[handle]["result"]) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 5283f312..10702517 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -29,7 +29,7 @@ from uuid import UUID import numpy as np -import sympy # type: ignore +import sympy # type: ignore from numpy.typing import NDArray from qiskit_ibm_runtime.models.backend_configuration import ( # type: ignore PulseBackendConfiguration, @@ -69,7 +69,7 @@ ClassicalRegister, QuantumCircuit, QuantumRegister, -) # type: ignore +) # type: ignore from qiskit.circuit import ( Barrier, Clbit, From b9be933e4485864a251b627130d9b2dccebbb927 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:58:36 +0000 Subject: [PATCH 33/39] fix mypy issues --- pytket/extensions/qiskit/backends/ibm_utils.py | 2 +- pytket/extensions/qiskit/qiskit_convert.py | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index d7987bab..ebb84654 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -101,7 +101,7 @@ def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: return CouplingMap(coupling_map) -def _gen_lightsabre_transformation( +def _gen_lightsabre_transformation( # type: ignore architecture: Architecture, optimization_level: int = 2, seed=0, attempts=20 ) -> Callable[[Circuit], Circuit]: """ diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 10702517..e6e0c221 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -13,8 +13,8 @@ # limitations under the License. -"""Methods to allow conversion between Qiskit and pytket circuit classes -""" +"""Methods to allow conversion between Qiskit and pytket circuit classes""" + from collections import defaultdict from collections.abc import Iterable from inspect import signature @@ -29,7 +29,7 @@ from uuid import UUID import numpy as np -import sympy # type: ignore +import sympy from numpy.typing import NDArray from qiskit_ibm_runtime.models.backend_configuration import ( # type: ignore PulseBackendConfiguration, @@ -69,7 +69,7 @@ ClassicalRegister, QuantumCircuit, QuantumRegister, -) # type: ignore +) from qiskit.circuit import ( Barrier, Clbit, @@ -755,7 +755,13 @@ def append_tk_command_to_qiskit( if a.reg_name != regname: raise NotImplementedError("Conditions can only use a single register") instruction = append_tk_command_to_qiskit( - op.op, args[width:], qcirc, qregmap, cregmap, symb_map, range_preds # type: ignore + op.op, + args[width:], + qcirc, + qregmap, + cregmap, + symb_map, + range_preds, # type: ignore ) if len(cregmap[regname]) == width: for i, a in enumerate(args[:width]): @@ -1037,7 +1043,7 @@ def return_value_if_found(iterator: Iterable["Nduv"], name: str) -> Optional[Any def get_avg_characterisation( - characterisation: dict[str, Any] + characterisation: dict[str, Any], ) -> dict[str, dict[Node, float]]: """ Convert gate-specific characterisation into readout, one- and two-qubit errors From c942e889cd2df43e61cb04155f90013c7a26dc22 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:05:06 +0000 Subject: [PATCH 34/39] fix mypy --- pytket/extensions/qiskit/backends/aer.py | 2 +- pytket/extensions/qiskit/qiskit_convert.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pytket/extensions/qiskit/backends/aer.py b/pytket/extensions/qiskit/backends/aer.py index 57524ddf..68807957 100644 --- a/pytket/extensions/qiskit/backends/aer.py +++ b/pytket/extensions/qiskit/backends/aer.py @@ -226,7 +226,7 @@ def default_compilation_pass( ): return self._arch_dependent_default_compilation_pass( arch, # type: ignore - optimisation_level, # type: ignore + optimisation_level, ) return self._arch_independent_default_compilation_pass(optimisation_level) diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index e6e0c221..e268388d 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -755,13 +755,13 @@ def append_tk_command_to_qiskit( if a.reg_name != regname: raise NotImplementedError("Conditions can only use a single register") instruction = append_tk_command_to_qiskit( - op.op, + op.op, # type: ignore args[width:], qcirc, qregmap, cregmap, symb_map, - range_preds, # type: ignore + range_preds, ) if len(cregmap[regname]) == width: for i, a in enumerate(args[:width]): From a574dd972745ef1d8517eae61e839e6bfb496dcb Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:24:34 +0000 Subject: [PATCH 35/39] Update index.md --- docs/index.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/index.md b/docs/index.md index 8911076e..846f6fb6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -205,12 +205,9 @@ Every {py:class}`~pytket.backends.backend.Backend` in pytket has its own {py:met * - [AutoRebase [2]](inv:#*.AutoRebase) - [SynthesiseTket](inv:#*.SynthesiseTket) - [FullPeepholeOptimise](inv:#*.passes.FullPeepholeOptimise) -* - [CXMappingPass [3]](inv:#*.passes.CXMappingPass) - - [CXMappingPass [3]](inv:#*.passes.CXMappingPass) - - [CXMappingPass [3]](inv:#*.passes.CXMappingPass) -* - [NaivePlacementPass](inv:#*.passes.NaivePlacementPass) - - [NaivePlacementPass](inv:#*.passes.NaivePlacementPass) - - [NaivePlacementPass](inv:#*.passes.NaivePlacementPass) +* - LightSabre [3] + - LightSabre [3] + - LightSabre [3] * - [AutoRebase [2]](inv:#*.AutoRebase) - [SynthesiseTket](inv:#*.SynthesiseTket) - [KAKDecomposition(allow_swaps=False)](inv:#*.passes.KAKDecomposition) @@ -231,7 +228,7 @@ Every {py:class}`~pytket.backends.backend.Backend` in pytket has its own {py:met - \[1\] If no value is specified then `optimisation_level` defaults to a value of 2. - \[2\] {py:class}`~pytket._tket.passes.AutoRebase` is a conversion to the gateset supported by the backend. For IBM quantum devices and emulators the supported gate set is either $\{X, SX, Rz, CX\}$, $\{X, SX, Rz, ECR\}$, or $\{X, SX, Rz, CZ\}$. The more idealised Aer simulators have a much broader range of supported gates. -- \[3\] Here [CXMappingPass](inv:#*.passes.CXMappingPass) maps program qubits to the architecture using a [NoiseAwarePlacement](inv:#*.NoiseAwarePlacement) +- \[3\] This is imported from qiskit and corresponds to the method in "LightSABRE: A Lightweight and Enhanced SABRE Algorithm", Henry Zou, Matthew Treinish, Kevin Hartman, Alexander Ivrii, Jake Lishman, arXiv:2409.08368. **Note:** The {py:meth}`~AerBackend.default_compilation_pass` for {py:class}`AerBackend` is the same as above. From 38078eb5cb9674b0355e42efa623165e47e56886 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:55:26 +0000 Subject: [PATCH 36/39] Update ibm_utils.py --- pytket/extensions/qiskit/backends/ibm_utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytket/extensions/qiskit/backends/ibm_utils.py b/pytket/extensions/qiskit/backends/ibm_utils.py index ebb84654..5d2850d8 100644 --- a/pytket/extensions/qiskit/backends/ibm_utils.py +++ b/pytket/extensions/qiskit/backends/ibm_utils.py @@ -84,6 +84,7 @@ def _architecture_to_couplingmap(architecture: Architecture) -> CouplingMap: Converts a pytket Architecture object to a Qiskit CouplingMap object. :param architecture: Architecture to be converted + :return: A Qiskit CouplingMap object corresponding to the same connectivity """ # we can make some assumptions from how the Architecture object is # originally constructed from the Qiskit CouplingMap: @@ -112,6 +113,8 @@ def _gen_lightsabre_transformation( # type: ignore :param optimization_level: Corresponds to qiskit optmization levels :param seed: LightSABRE routing is stochastic, with this parameter setting the seed :param attempts: Number of generated random solutions to pick from. + :return: A function that accepts a pytket Circuit and returns a new Circuit that + has been routed to the architecture using LightSABRE """ config: PassManagerConfig = PassManagerConfig( coupling_map=_architecture_to_couplingmap(architecture), From fddd31fb610fcb9953e404010f61379470ee25d0 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Fri, 8 Nov 2024 09:09:31 +0000 Subject: [PATCH 37/39] Update backend_test.py --- tests/backend_test.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index 62b53c01..13ff57c8 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -65,9 +65,13 @@ NoisyCircuitBuilder, ) from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod, MappingManager -from pytket.passes import CliffordSimp +from pytket.passes import CliffordSimp, FlattenRelabelRegistersPass from pytket.pauli import Pauli, QubitPauliString -from pytket.predicates import CompilationUnit, NoMidMeasurePredicate +from pytket.predicates import ( + CompilationUnit, + ConnectivityPredicate, + NoMidMeasurePredicate, +) from pytket.transform import Transform from pytket.utils.expectations import ( get_operator_expectation_value, @@ -988,6 +992,31 @@ def lift_perm(p: dict[int, int]) -> np.ndarray: return pm +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +def test_compilation_correctness(brisbane_backend: IBMQBackend) -> None: + # of routing + c = Circuit(7) + 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) + c.remove_blank_wires() + FlattenRelabelRegistersPass().apply(c) + c_pred = ConnectivityPredicate(brisbane_backend.backend_info.architecture) + for ol in range(3): + p = brisbane_backend.default_compilation_pass(optimisation_level=ol) + cu = CompilationUnit(c) + p.apply(cu) + assert c_pred.verify(cu.circuit) + + # pytket-extensions issue #69 def test_symbolic_rebase() -> None: circ = QuantumCircuit(2) From e3ffea57c12edf48c05f38b6f2ad80385790873d Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:06:26 +0000 Subject: [PATCH 38/39] Update backend_test.py --- tests/backend_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index 13ff57c8..51446bde 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -1009,7 +1009,7 @@ def test_compilation_correctness(brisbane_backend: IBMQBackend) -> None: c.CX(0, 3).CX(0, 4) c.remove_blank_wires() FlattenRelabelRegistersPass().apply(c) - c_pred = ConnectivityPredicate(brisbane_backend.backend_info.architecture) + c_pred = ConnectivityPredicate(cast(Architecture, brisbane_backend.backend_info.architecture)) for ol in range(3): p = brisbane_backend.default_compilation_pass(optimisation_level=ol) cu = CompilationUnit(c) From 7ead0a48a08791f8d5c00653d17193d3ce345389 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:08:20 +0000 Subject: [PATCH 39/39] Update backend_test.py --- tests/backend_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/backend_test.py b/tests/backend_test.py index 51446bde..8dc4104c 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -1009,7 +1009,9 @@ def test_compilation_correctness(brisbane_backend: IBMQBackend) -> None: c.CX(0, 3).CX(0, 4) c.remove_blank_wires() FlattenRelabelRegistersPass().apply(c) - c_pred = ConnectivityPredicate(cast(Architecture, brisbane_backend.backend_info.architecture)) + c_pred = ConnectivityPredicate( + cast(Architecture, brisbane_backend.backend_info.architecture) + ) for ol in range(3): p = brisbane_backend.default_compilation_pass(optimisation_level=ol) cu = CompilationUnit(c)