From 16c4ab6b7182ad5de0850290a30b37c51db1ce45 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 6 Jul 2022 12:47:25 -0400 Subject: [PATCH 01/31] Add stage plugin interface for transpile This commit adds a new plugin interface to qiskit for enabling external packages to write plugins that will replace a stage in the transpilation pipeline. For example, if an external package had a custom layout pass that they wanted to integrate into transpile() they could export a plugin using this new interface and then users would just need to run transpile(.., layout_method=foo) This adds long asked for extensibility to the transpiler so that to cleanly integrate new transpiler passes we're no longer required to merge the features into terra. This should hopefully make it easier for downstream pass authors to integrate their passes into terra and make it easier for the terra maintainers to evaluate new transpiler passes. --- docs/apidocs/transpiler_plugins.rst | 5 + qiskit/compiler/transpiler.py | 34 ++- qiskit/transpiler/passmanager_config.py | 19 +- .../preset_passmanagers/__init__.py | 35 ++- .../transpiler/preset_passmanagers/level1.py | 117 ++++++--- .../transpiler/preset_passmanagers/level2.py | 113 +++++--- .../transpiler/preset_passmanagers/plugin.py | 246 ++++++++++++++++++ 7 files changed, 489 insertions(+), 80 deletions(-) create mode 100644 qiskit/transpiler/preset_passmanagers/plugin.py diff --git a/docs/apidocs/transpiler_plugins.rst b/docs/apidocs/transpiler_plugins.rst index 33646fe2d58f..b01c4b775b98 100644 --- a/docs/apidocs/transpiler_plugins.rst +++ b/docs/apidocs/transpiler_plugins.rst @@ -4,3 +4,8 @@ :no-members: :no-inherited-members: :no-special-members: + +.. automodule:: qiskit.transpiler.preset_passmanagers.plugin + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index ac9ccc7c0db4..6df7d0f2bbef 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -80,6 +80,8 @@ def transpile( unitary_synthesis_method: str = "default", unitary_synthesis_plugin_config: dict = None, target: Target = None, + init_method: str = None, + optimization_method: str = None, ) -> Union[QuantumCircuit, List[QuantumCircuit]]: """Transpile one or more circuits, according to some desired transpilation targets. @@ -150,17 +152,29 @@ def transpile( [qr[0], None, None, qr[1], None, qr[2]] - layout_method: Name of layout selection pass ('trivial', 'dense', 'noise_adaptive', 'sabre') + layout_method: Name of layout selection pass ('trivial', 'dense', 'noise_adaptive', 'sabre'). + This can also be the external plugin name to use for the ``layout`` stage. + You can see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"layout"`` for the ``stage_name`` argument. routing_method: Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre', 'toqm', 'none'). Note that to use method 'toqm', package 'qiskit-toqm' must be installed. + This can also be the external plugin name to use for the ``routing`` stage. + You can see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"routing"`` for the ``stage_name`` argument. translation_method: Name of translation pass ('unroller', 'translator', 'synthesis') + This can also be the external plugin name to use for the ``translation`` stage. + You can see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"translation"`` for the ``stage_name`` argument. scheduling_method: Name of scheduling pass. * ``'as_soon_as_possible'``: Schedule instructions greedily, as early as possible on a qubit resource. (alias: ``'asap'``) * ``'as_late_as_possible'``: Schedule instructions late, i.e. keeping qubits in the ground state when possible. (alias: ``'alap'``) - If ``None``, no scheduling will be done. + If ``None``, no scheduling will be done. This can also be the external plugin name + to use for the ``scheduling`` stage. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"scheduling"`` for the ``stage_name`` + argument. instruction_durations: Durations of instructions. Applicable only if scheduling_method is specified. The gate lengths defined in ``backend.properties`` are used as default. @@ -246,6 +260,16 @@ def callback_func(**kwargs): the ``backend`` argument, but if you have manually constructed a :class:`~qiskit.transpiler.Target` object you can specify it manually here. This will override the target from ``backend``. + init_method: The plugin name to use for the ``init`` stage. By default an external + plugin is not used. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"init"`` for the stage + name argument. + optimization_method: The plugin name to use for the + ``optimization`` stage. By default an external + plugin is not used. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"optimization"`` for the + ``stage_name`` argument. + Returns: The transpiled circuit(s). @@ -310,6 +334,8 @@ def callback_func(**kwargs): unitary_synthesis_method, unitary_synthesis_plugin_config, target, + init_method, + optimization_method, ) # Get transpile_args to configure the circuit transpilation job(s) if coupling_map in unique_transpile_args: @@ -560,6 +586,8 @@ def _parse_transpile_args( unitary_synthesis_method, unitary_synthesis_plugin_config, target, + init_method, + optimization_method, ) -> Tuple[List[Dict], Dict]: """Resolve the various types of args allowed to the transpile() function through duck typing, overriding args, etc. Refer to the transpile() docstring for details on @@ -627,6 +655,8 @@ def _parse_transpile_args( shared_dict = { "optimization_level": optimization_level, "basis_gates": basis_gates, + "init_method": init_method, + "optimization_method": optimization_method, } list_transpile_args = [] diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index 929d1aa13366..cf0b3e521fb2 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -39,6 +39,8 @@ def __init__( unitary_synthesis_method="default", unitary_synthesis_plugin_config=None, target=None, + init_method=None, + optimization_method=None, ): """Initialize a PassManagerConfig object @@ -50,12 +52,16 @@ def __init__( coupling_map (CouplingMap): Directed graph represented a coupling map. layout_method (str): the pass to use for choosing initial qubit - placement. + placement. This will be the plugin name if an external layout stage + plugin is being used routing_method (str): the pass to use for routing qubits on the - architecture. + architecture. This will be a plugin name if an external routing stage + plugin is being used translation_method (str): the pass to use for translating gates to - basis_gates. - scheduling_method (str): the pass to use for scheduling instructions. + basis_gates. This will be a plugin name if an external translation stage + plugin is being used. + scheduling_method (str): the pass to use for scheduling instructions. This will + be a plugin name if an external scheduling stage plugin is being used. instruction_durations (InstructionDurations): Dictionary of duration (in dt) for each instruction. backend_properties (BackendProperties): Properties returned by a @@ -70,6 +76,9 @@ def __init__( :class:`~qiskit.transpiler.passes.UnitarySynthesis` pass. Will search installed plugins for a valid method. target (Target): The backend target + init_method (str): The plugin name for the init stage plugin to use + optimization_method (str): The plugin name for the optimization stage plugin + to use """ self.initial_layout = initial_layout self.basis_gates = basis_gates @@ -87,6 +96,8 @@ def __init__( self.unitary_synthesis_method = unitary_synthesis_method self.unitary_synthesis_plugin_config = unitary_synthesis_plugin_config self.target = target + self.init_method = init_method + self.optimization_method = optimization_method @classmethod def from_backend(cls, backend, **pass_manager_options): diff --git a/qiskit/transpiler/preset_passmanagers/__init__.py b/qiskit/transpiler/preset_passmanagers/__init__.py index eb2f0d2aed39..cb15d2ec372a 100644 --- a/qiskit/transpiler/preset_passmanagers/__init__.py +++ b/qiskit/transpiler/preset_passmanagers/__init__.py @@ -55,6 +55,8 @@ def generate_preset_pass_manager( seed_transpiler=None, unitary_synthesis_method="default", unitary_synthesis_plugin_config=None, + init_method=None, + optimization_method=None, ): """Generate a preset :class:`~.PassManager` @@ -103,18 +105,30 @@ def generate_preset_pass_manager( layout_method (str): The :class:`~.Pass` to use for choosing initial qubit placement. Valid choices are ``'trivial'``, ``'dense'``, ``'noise_adaptive'``, and, ``'sabre'`` repsenting :class:`~.TrivialLayout`, :class:`~DenseLayout`, - :class:`~.NoiseAdaptiveLayout`, :class:`~.SabreLayout` respectively. + :class:`~.NoiseAdaptiveLayout`, :class:`~.SabreLayout` respectively. This can also + be the external plugin name to use for the ``layout`` stage of the output + :class:`~.StagedPassManager`. You can see a list of installed plugins by using + :func:`~.list_stage_plugins` with ``"layout"`` for the ``stage_name`` argument. routing_method (str): The pass to use for routing qubits on the architecture. Valid choices are ``'basic'``, ``'lookahead'``, ``'stochastic'``, ``'sabre'``, and ``'none'`` representing :class:`~.BasicSwap`, :class:`~.LookaheadSwap`, :class:`~.StochasticSwap`, :class:`~.SabreSwap`, and - erroring if routing is required respectively. + erroring if routing is required respectively. This can also be the external plugin + name to use for the ``routing`` stage of the output :class:`~.StagedPassManager`. + You can see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"routing"`` for the ``stage_name`` argument. translation_method (str): The method to use for translating gates to basis gates. Valid choices ``'unroller'``, ``'translator'``, ``'synthesis'`` representing :class:`~.Unroller`, :class:`~.BasisTranslator`, and - :class:`~.UnitarySynthesis` respectively. + :class:`~.UnitarySynthesis` respectively. This can also be the external plugin + name to use for the ``translation`` stage of the output :class:`~.StagedPassManager`. + You can see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"translation"`` for the ``stage_name`` argument. scheduling_method (str): The pass to use for scheduling instructions. Valid choices - are ``'alap'`` and ``'asap'``. + are ``'alap'`` and ``'asap'``. This can also be the external plugin name to use + for the ``scheduling`` stage of the output :class:`~.StagedPassManager`. You can + see a list of installed plugins by using :func:`~.list_stage_plugins` with + ``"scheduling"`` for the ``stage_name`` argument. backend_properties (BackendProperties): Properties returned by a backend, including information on gate errors, readout errors, qubit coherence times, etc. @@ -134,6 +148,17 @@ def generate_preset_pass_manager( the ``unitary_synthesis`` argument. As this is custom for each unitary synthesis plugin refer to the plugin documentation for how to use this option. + init_method (str): The plugin name to use for the ``init`` stage of + the output :class:`~.StagedPassManager`. By default an external + plugin is not used. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"init"`` for the stage + name argument. + optimization_method (str): The plugin name to use for the + ``optimization`` stage of the output + :class:`~.StagedPassManager`. By default an external + plugin is not used. You can see a list of installed plugins by + using :func:`~.list_stage_plugins` with ``"optimization"`` for the + ``stage_name`` argument. Returns: StagedPassManager: The preset pass manager for the given options @@ -172,6 +197,8 @@ def generate_preset_pass_manager( unitary_synthesis_method=unitary_synthesis_method, unitary_synthesis_plugin_config=unitary_synthesis_plugin_config, initial_layout=initial_layout, + init_method=init_method, + optimization_method=optimization_method, ) if backend is not None: diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 5da52fb23052..6020eb76fc1c 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -42,6 +42,7 @@ from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM +from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassManager: @@ -65,6 +66,17 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa Raises: TranspilerError: if the passmanager config is invalid. """ + plugin_manager = None + # pylint: disable=too-many-boolean-expressions + if ( + pass_manager_config.layout_method is not None + or pass_manager_config.routing_method is not None + or pass_manager_config.translation_method is not None + or pass_manager_config.scheduling_method is not None + or pass_manager_config.init_method + or pass_manager_config.optimization_method is not None + ): + plugin_manager = PassManagerStagePluginManager() basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map @@ -73,6 +85,8 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method + init_method = pass_manager_config.init_method + optimization_method = pass_manager_config.optimization_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties @@ -147,6 +161,7 @@ def _vf2_match_not_found(property_set): raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False + routing_pm = None if routing_method == "basic": routing_pass = BasicSwap(coupling_map) elif routing_method == "stochastic": @@ -180,7 +195,9 @@ def _vf2_match_not_found(property_set): action="raise", ) else: - raise TranspilerError("Invalid routing method %s." % routing_method) + routing_pm = plugin_manager.get_passmanager_stage( + "routing", routing_method, pass_manager_config + ) # Build optimization loop: merge 1q rotations and cancel CNOT gates iteratively # until no more change in depth @@ -202,38 +219,52 @@ def _opt_control(property_set): unitary_synthesis_method, unitary_synthesis_plugin_config, ) - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout_0, condition=_choose_layout_condition) - layout.append(_choose_layout_1, condition=_trivial_not_perfect) - layout.append(_improve_layout, condition=_vf2_match_not_found) - layout += common.generate_embed_passmanager(coupling_map) + if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config + ) + else: + layout = PassManager() + layout.append(_given_layout) + layout.append(_choose_layout_0, condition=_choose_layout_condition) + layout.append(_choose_layout_1, condition=_trivial_not_perfect) + layout.append(_improve_layout, condition=_vf2_match_not_found) + layout += common.generate_embed_passmanager(coupling_map) + vf2_call_limit = None if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - routing = common.generate_routing_passmanager( - routing_pass, - target, - coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - check_trivial=True, - use_barrier_before_measurement=not toqm_pass, - ) + if routing_pm is not None: + routing = routing_pm + else: + routing = common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=not toqm_pass, + ) else: layout = None routing = None - translation = common.generate_translation_passmanager( - target, - basis_gates, - translation_method, - approximation_degree, - coupling_map, - backend_properties, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - ) + if translation_method not in {"translator", "synthesis", "unroller"}: + translation = plugin_manager.get_passmanager_stage( + "translation", translation_method, pass_manager_config + ) + else: + translation = common.generate_translation_passmanager( + target, + basis_gates, + translation_method, + approximation_degree, + coupling_map, + backend_properties, + unitary_synthesis_method, + unitary_synthesis_plugin_config, + ) pre_routing = None if toqm_pass: pre_routing = translation @@ -244,17 +275,31 @@ def _opt_control(property_set): pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) - optimization = PassManager() - unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] - optimization.append(_depth_check + _size_check) - opt_loop = _opt + unroll + _depth_check + _size_check - optimization.append(opt_loop, do_while=_opt_control) - sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map - ) + if optimization_method is None: + optimization = PassManager() + unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] + optimization.append(_depth_check + _size_check) + opt_loop = _opt + unroll + _depth_check + _size_check + optimization.append(opt_loop, do_while=_opt_control) + else: + optimization = plugin_manager.get_passmanager_stage( + "optimization", optimization_method, pass_manager_config + ) + if scheduling_method is None or scheduling_method in {"alap", "asap"}: + sched = common.generate_scheduling( + instruction_durations, scheduling_method, timing_constraints, inst_map + ) + else: + sched = plugin_manager.get_passmanager_stage( + "scheduling", scheduling_method, pass_manager_config + ) + if init_method is not None: + init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + else: + init = unroll_3q return StagedPassManager( - init=unroll_3q, + init=init, layout=layout, pre_routing=pre_routing, routing=routing, diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index c2ee13ad8500..aeac570076fe 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -42,6 +42,7 @@ from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM +from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassManager: @@ -67,6 +68,17 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa Raises: TranspilerError: if the passmanager config is invalid. """ + plugin_manager = None + # pylint: disable=too-many-boolean-expressions + if ( + pass_manager_config.layout_method is not None + or pass_manager_config.routing_method is not None + or pass_manager_config.translation_method is not None + or pass_manager_config.scheduling_method is not None + or pass_manager_config.init_method + or pass_manager_config.optimization_method is not None + ): + plugin_manager = PassManagerStagePluginManager() basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map @@ -75,6 +87,8 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method + init_method = pass_manager_config.init_method + optimization_method = pass_manager_config.optimization_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties @@ -130,6 +144,7 @@ def _vf2_match_not_found(property_set): raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False + routing_pm = None if routing_method == "basic": routing_pass = BasicSwap(coupling_map) elif routing_method == "stochastic": @@ -163,7 +178,9 @@ def _vf2_match_not_found(property_set): action="raise", ) else: - raise TranspilerError("Invalid routing method %s." % routing_method) + routing_pm = plugin_manager.get_passmanager_stage( + "routing", routing_method, pass_manager_config + ) # Build optimization loop: 1q rotation merge and commutative cancellation iteratively until # no more change in depth @@ -188,36 +205,49 @@ def _opt_control(property_set): unitary_synthesis_method, unitary_synthesis_plugin_config, ) - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout_0, condition=_choose_layout_condition) - layout.append(_choose_layout_1, condition=_vf2_match_not_found) - layout += common.generate_embed_passmanager(coupling_map) + if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config + ) + else: + layout = PassManager() + layout.append(_given_layout) + layout.append(_choose_layout_0, condition=_choose_layout_condition) + layout.append(_choose_layout_1, condition=_vf2_match_not_found) + layout += common.generate_embed_passmanager(coupling_map) vf2_call_limit = None if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 - routing = common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=not toqm_pass, - ) + if routing_pm is not None: + routing = routing_pm + else: + routing = common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=not toqm_pass, + ) else: layout = None routing = None - translation = common.generate_translation_passmanager( - target, - basis_gates, - translation_method, - approximation_degree, - coupling_map, - backend_properties, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - ) + if translation_method not in {"translator", "synthesis", "unroller"}: + translation = plugin_manager.get_passmanager_stage( + "translation", translation_method, pass_manager_config + ) + else: + translation = common.generate_translation_passmanager( + target, + basis_gates, + translation_method, + approximation_degree, + coupling_map, + backend_properties, + unitary_synthesis_method, + unitary_synthesis_plugin_config, + ) pre_routing = None if toqm_pass: pre_routing = translation @@ -227,16 +257,31 @@ def _opt_control(property_set): pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) - optimization = PassManager() - unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] - optimization.append(_depth_check + _size_check) - opt_loop = _opt + unroll + _depth_check + _size_check - optimization.append(opt_loop, do_while=_opt_control) - sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map - ) + if optimization_method is None: + optimization = PassManager() + unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] + optimization.append(_depth_check + _size_check) + opt_loop = _opt + unroll + _depth_check + _size_check + optimization.append(opt_loop, do_while=_opt_control) + else: + optimization = plugin_manager.get_passmanager_stage( + "optimization", optimization_method, pass_manager_config + ) + if scheduling_method is None or scheduling_method in {"alap", "asap"}: + sched = common.generate_scheduling( + instruction_durations, scheduling_method, timing_constraints, inst_map + ) + else: + sched = plugin_manager.get_passmanager_stage( + "scheduling", scheduling_method, pass_manager_config + ) + if init_method is not None: + init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + else: + init = unroll_3q + return StagedPassManager( - init=unroll_3q, + init=init, layout=layout, pre_routing=pre_routing, routing=routing, diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py new file mode 100644 index 000000000000..ee27ac48ee31 --- /dev/null +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -0,0 +1,246 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +======================================================================================= +Transpiler Stage Plugin Interface (:mod:`qiskit.transpiler.preset_passmanagers.plugin`) +======================================================================================= + +.. currentmodule:: qiskit.transpiler.preset_pass_managers.plugin + +This module defines the plugin interface for providing custom stage +implementations for the preset pass managers and the :func:`~.transpile` +function. This enables external Python packages to provide +:class:`~.PassManager` objects that can be used for each stage. + +.. _stage_table: + +Plugin Stages +============= + +Currently there are 6 stages in the preset pass managers used by and corresponding entrypoints. + +.. list-table:: Stages + :header-rows: 1 + + * - Stage Name + - Entry Point + - Reserved Names + - Description and expectations + * - ``init`` + - ``qiskit.transpiler.init`` + - No reserved names + - This stage runs first and is typically used for an initial logical optimization also + for most ``layout`` and ``routing`` stages this stage is used to translate any gates + that operate on more than 2 qubits into gates that operate on 1 or 2 + qubits only. This is because most layout and routing algorithms are + only designed to work with 1 and 2 qubit gates. + ``init`` + * - ``layout`` + - ``qiskit.transpiler.layout`` + - ``trivial``, ``dense``, ``noise_adaptive``, ``sabre`` + - The output from this staged is expected to have the ``layout`` property + set field set with a :class:`~.Layout` object. Additionally, the circuit is + is typically expected to be embedded so that it expanded to include all + qubits and the :class:`~.ApplyLayout` pass is expected to be run to apply the + layout. The embedding of the :class:`~.Layout` can be generated with + :func:`~.generate_embed_passmanager`. + * - ``routing`` + - ``qiskit.transpiler.routing`` + - ``basic``, ``stochastic``, ``lookahead``, ``sabre``, ``toqm`` + - The output from this stage is expected to have the circuit match the + connectivity constraints of the target backend. This does not necessarily + need to match the directionality of the edges in the target as a later + stage typically will adjust directional gates to match that constraint + (but there is no penalty for doing that in the ``routing`` stage). + * - ``translation`` + - ``qiskit.transpiler.translation`` + - ``translator``, ``synthesis``, ``unroller`` + - The output of this stage is expected to have every operation be a native + instruction on the target backend. + * - ``optimization`` + - ``qiskit.transpiler.optimization`` + - There are no reserve plugin names + - This stage is expected to perform and optimization and simplification. + The constraints from earlier stages still apply to the output of this + stage. After the ``optimization`` stage is run we expect the circuit + to still be executable on the target. + * - ``scheduling`` + - ``qiskit.transpiler.scheduling`` + - ``alap``, ``asap`` + - This is the last stage run and it is expected to output a scheduled + circuit such that all idle periods in the circuit are marked by explicit + :class:`~.Delay` instructions. + +Writing Plugins +=============== + +To write a pass manager stage plugin there are 2 main steps. The first step is +to create a subclass of the abstract plugin class +:class:`~.PassManagerStagePluginManager` which is used to define how the :class:`~.PassManager` +for the stage will be constructed. For example, to create a ``layout`` stage plugin that just +runs :class:`~.VF2Layout`:: + + from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin + from qiskit.transpiler.preset_passmanagers import common + from qiskit.transpiler import PassManager + from qiskit.transpiler.passes import VF2Layout + + class VF2LayoutPlugin(PassManagerStagePlugin): + + def pass_manager(self, pass_manager_config): + layout_pm = PassManager([VF2Layout( + coupling_map=pass_manager_config.coupling_map, + properties=pass_manager_config.backend_properties, + target=pass_manager_config.target + )]) + layout_pm += common.generate_embed_passmanager(pass_manager_config.coupling_map) + return layout_pm + +The second step is to expose the :class:`~.PassManagerStagePluginManager` +subclass as a setuptools entry point in the package metadata. This can be done +by simply adding an ``entry_points`` entry to the ``setuptools.setup`` call in +the ``setup.py`` or the plugin package with the necessary entry points under the +appropriate namespace for the stage your plugin is for. You can see the list +of stages, entrypoints, and expectations from the stage in :ref:`stage_table`. +For example, continuing from the example plugin above:: + + entry_points = { + 'qiskit.transpiler.layout': [ + 'vf2 = qiskit_plugin_pkg.module.plugin:VF2LayoutPlugin', + ] + }, + +(note that the entry point ``name = path`` is a single string not a Python +expression). There isn't a limit to the number of plugins a single package can +include as long as each plugin has a unique name. So a single package can +expose multiple plugins if necessary. Refer to :ref:`stage_table` for a list +of reserved names for each stage. + +Plugin API +========== + +.. autosummary:: + :toctree: ../stubs/ + + PassManagerStagePlugin + PassManagerStagePluginManager + list_stage_plugins +""" + +import abc +from typing import List + +import stevedore + +from qiskit.transpiler.passmanager import PassManager +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.passmanager_config import PassManagerConfig + + +class PassManagerStagePlugin(abc.ABC): + """A ``PassManagerStagePlugin`` is a plugin interface object for using custom + stages in :func:`transpile()`. + + A ``PassManagerStagePlugin`` object can be added to an external package and + + """ + + @abc.abstractmethod + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """This method is designed to return a :class:`~.PassManager` for the""" + pass + + +class PassManagerStagePluginManager: + """Manager class for preset pass manager stage plugins.""" + + def __init__(self): + super().__init__() + self.init_plugins = stevedore.ExtensionManager( + "qiskit.transpiler.init", invoke_on_load=True, propagate_map_exceptions=True + ) + self.layout_plugins = stevedore.ExtensionManager( + "qiskit.transpiler.layout", invoke_on_load=True, propagate_map_exceptions=True + ) + self.routing_plugins = stevedore.ExtensionManager( + "qiskit.transpiler.routing", invoke_on_load=True, propagate_map_exceptions=True + ) + self.translation_plugins = stevedore.ExtensionManager( + "qiskit.transpiler.translation", invoke_on_load=True, propagate_map_exceptions=True + ) + self.optimization_plugins = stevedore.ExtensionManager( + "qiskit.transpiler.optimization", invoke_on_load=True, propagate_map_exceptions=True + ) + self.scheduling_plugins = stevedore.ExtensionManager( + "qiskit.transpiler.scheduling", invoke_on_load=True, propagate_map_exceptions=True + ) + + def get_passmanager_stage( + self, stage_name: str, plugin_name: str, pm_config: PassManagerConfig + ) -> PassManager: + """Get a stage""" + if stage_name == "init": + return self._build_pm(self.init_plugins, stage_name, plugin_name, pm_config) + elif stage_name == "layout": + return self._build_pm(self.layout_plugins, stage_name, plugin_name, pm_config) + elif stage_name == "routing": + return self._build_pm(self.routing_plugins, stage_name, plugin_name, pm_config) + elif stage_name == "translation": + return self._build_pm(self.translation_plugins, stage_name, plugin_name, pm_config) + elif stage_name == "optimization_plugins": + return self._build_pm(self.optimization_plugins, stage_name, plugin_name, pm_config) + elif stage_name == "scheduling": + return self._build_pm(self.scheduling_plugins, stage_name, plugin_name, pm_config) + else: + raise TranspilerError(f"Invalid stage name: {stage_name}") + + def _build_pm( + self, + stage_obj: stevedore.ExtensionManager, + stage_name: str, + plugin_name: str, + pm_config: PassManagerConfig, + ): + plugin_obj = stage_obj.get(plugin_name) + if plugin_obj is None: + raise TranspilerError(f"Invalid plugin name {plugin_name} for stage {stage_name}") + return plugin_obj.pass_manager(pm_config) + + +def list_stage_plugins(stage_name: str) -> List[str]: + """Get a list of installed plugins for a stage. + + Args: + stage_name: The stage name to get the plugin names for + + Returns: + plugins: The list of installed plugin names for the specified stages + + Raises: + TranspilerError: If an invalid stage name is specified. + """ + plugin_mgr = PassManagerStagePluginManager() + if stage_name == "init": + return plugin_mgr.init_plugins.names() + elif stage_name == "layout": + return plugin_mgr.layout_plugins.names() + elif stage_name == "routing": + return plugin_mgr.routing_plugins.names() + elif stage_name == "translation": + return plugin_mgr.translation_plugins.names() + elif stage_name == "optimization_plugins": + return plugin_mgr.optimization_plugins.names() + elif stage_name == "scheduling": + return plugin_mgr.scheduling_plugins.names() + else: + raise TranspilerError(f"Invalid stage name: {stage_name}") From 5ec9324be644245491315ec94614e452166cab91 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 7 Jul 2022 10:20:38 -0400 Subject: [PATCH 02/31] Fix docs builds --- docs/apidocs/terra.rst | 1 + docs/apidocs/transpiler_plugins.rst | 5 ----- docs/apidocs/transpiler_synthesis_plugins.rst | 6 ++++++ qiskit/transpiler/passes/synthesis/plugin.py | 4 ++++ qiskit/transpiler/preset_passmanagers/plugin.py | 9 ++++++++- 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 docs/apidocs/transpiler_synthesis_plugins.rst diff --git a/docs/apidocs/terra.rst b/docs/apidocs/terra.rst index 5b765c7c95e7..9154a9420fc6 100644 --- a/docs/apidocs/terra.rst +++ b/docs/apidocs/terra.rst @@ -36,6 +36,7 @@ Qiskit Terra API Reference transpiler_passes transpiler_preset transpiler_plugins + transpiler_synthesis_plugins transpiler_builtin_plugins utils utils_mitigation diff --git a/docs/apidocs/transpiler_plugins.rst b/docs/apidocs/transpiler_plugins.rst index b01c4b775b98..b5de3efc8ff6 100644 --- a/docs/apidocs/transpiler_plugins.rst +++ b/docs/apidocs/transpiler_plugins.rst @@ -1,10 +1,5 @@ .. _qiskit-transpiler-plugins: -.. automodule:: qiskit.transpiler.passes.synthesis.plugin - :no-members: - :no-inherited-members: - :no-special-members: - .. automodule:: qiskit.transpiler.preset_passmanagers.plugin :no-members: :no-inherited-members: diff --git a/docs/apidocs/transpiler_synthesis_plugins.rst b/docs/apidocs/transpiler_synthesis_plugins.rst new file mode 100644 index 000000000000..70bef1190f40 --- /dev/null +++ b/docs/apidocs/transpiler_synthesis_plugins.rst @@ -0,0 +1,6 @@ +.. _qiskit-transpiler-synthesis-plugins: + +.. automodule:: qiskit.transpiler.passes.synthesis.plugin + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/qiskit/transpiler/passes/synthesis/plugin.py b/qiskit/transpiler/passes/synthesis/plugin.py index 33f168319ced..b199e0ffdbe8 100644 --- a/qiskit/transpiler/passes/synthesis/plugin.py +++ b/qiskit/transpiler/passes/synthesis/plugin.py @@ -27,6 +27,10 @@ which enable packages external to qiskit to advertise they include a synthesis plugin. +See :mod:`qiskit.transpiler.preset_passmanagers.plugin` for details on how +to write plugins for transpiler stages. + + Writing Plugins =============== diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index ee27ac48ee31..09276d8945f7 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -15,13 +15,20 @@ Transpiler Stage Plugin Interface (:mod:`qiskit.transpiler.preset_passmanagers.plugin`) ======================================================================================= -.. currentmodule:: qiskit.transpiler.preset_pass_managers.plugin +.. currentmodule:: qiskit.transpiler.preset_passmanagers.plugin This module defines the plugin interface for providing custom stage implementations for the preset pass managers and the :func:`~.transpile` function. This enables external Python packages to provide :class:`~.PassManager` objects that can be used for each stage. +The plugin interfaces are built using setuptools +`entry points `__ +which enable packages external to Qiskit to advertise they include a transpiler stage. + +See :mod:`qiskit.transpiler.passes.synthesis.plugin` for details on how to +write plugins for synthesis methods which are used by the transpiler. + .. _stage_table: Plugin Stages From dfeb06466592211b4a440655bfd0d5616e899980 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 7 Jul 2022 14:38:35 -0400 Subject: [PATCH 03/31] Fix doc warning --- qiskit/transpiler/preset_passmanagers/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 09276d8945f7..4058cf890e5d 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -86,7 +86,7 @@ - ``alap``, ``asap`` - This is the last stage run and it is expected to output a scheduled circuit such that all idle periods in the circuit are marked by explicit - :class:`~.Delay` instructions. + :class:`~qiskit.circuit.Delay` instructions. Writing Plugins =============== From 150c838867807a56c62cd8b31371b1c4860143e9 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 20 Jul 2022 09:55:11 -0400 Subject: [PATCH 04/31] Make routing methods all plugins This commit converts all the built-in routing method options into separate plugins and also adds a default plugin for the default behavior at each optimization level. To support using plugins for routing method adding the optimization_level to the passmanager config was necessary so that the plugin has sufficient context on how to construct the routing pass used for the routing stage. As depending on the optimization level the settings for each pass differs. For example on stochastic swap the number of stochastic trials increases for level 3 to try and find a better solution at the cost of more runtime. --- qiskit/compiler/transpiler.py | 2 +- qiskit/transpiler/passmanager_config.py | 3 + .../preset_passmanagers/__init__.py | 1 + .../preset_passmanagers/builtin_plugins.py | 421 ++++++++++++++++++ .../transpiler/preset_passmanagers/level0.py | 7 + .../transpiler/preset_passmanagers/level1.py | 72 ++- .../transpiler/preset_passmanagers/level2.py | 70 +-- .../transpiler/preset_passmanagers/level3.py | 8 + .../transpiler/preset_passmanagers/plugin.py | 13 +- setup.py | 10 +- 10 files changed, 507 insertions(+), 100 deletions(-) create mode 100644 qiskit/transpiler/preset_passmanagers/builtin_plugins.py diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index 6df7d0f2bbef..d5545e9fdda6 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -418,7 +418,7 @@ def _log_transpile_time(start_time, end_time): def _combine_args(shared_transpiler_args, unique_config): # Pop optimization_level to exclude it from the kwargs when building a # PassManagerConfig - level = shared_transpiler_args.pop("optimization_level") + level = shared_transpiler_args.get("optimization_level") pass_manager_config = shared_transpiler_args pass_manager_config.update(unique_config.pop("pass_manager_config")) pass_manager_config = PassManagerConfig(**pass_manager_config) diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index cf0b3e521fb2..18c85ff8a57e 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -41,6 +41,7 @@ def __init__( target=None, init_method=None, optimization_method=None, + optimization_level=None, ): """Initialize a PassManagerConfig object @@ -79,6 +80,7 @@ def __init__( init_method (str): The plugin name for the init stage plugin to use optimization_method (str): The plugin name for the optimization stage plugin to use + optimization_level (int): The optimization level being used for compilation """ self.initial_layout = initial_layout self.basis_gates = basis_gates @@ -98,6 +100,7 @@ def __init__( self.target = target self.init_method = init_method self.optimization_method = optimization_method + self.optimization_level = optimization_level @classmethod def from_backend(cls, backend, **pass_manager_options): diff --git a/qiskit/transpiler/preset_passmanagers/__init__.py b/qiskit/transpiler/preset_passmanagers/__init__.py index cb15d2ec372a..0af4e40dd031 100644 --- a/qiskit/transpiler/preset_passmanagers/__init__.py +++ b/qiskit/transpiler/preset_passmanagers/__init__.py @@ -199,6 +199,7 @@ def generate_preset_pass_manager( initial_layout=initial_layout, init_method=init_method, optimization_method=optimization_method, + optimization_level=optimization_level, ) if backend is not None: diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py new file mode 100644 index 000000000000..668f6fe7bd33 --- /dev/null +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -0,0 +1,421 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Built-in transpiler stage plugins for preset pass managers.""" + +from qiskit.transpiler.passmanager_config import PassManagerConfig +from qiskit.transpiler.passmanager import PassManager +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.passes import BasicSwap +from qiskit.transpiler.passes import LookaheadSwap +from qiskit.transpiler.passes import StochasticSwap +from qiskit.transpiler.passes import SabreSwap +from qiskit.transpiler.passes import Error +from qiskit.transpiler.preset_passmanagers import common +from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin + + +class DefaultRoutingPassManager(PassManagerStagePlugin): + """Plugin class for default routing stage.""" + + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """Build routing stage PassManager.""" + opt_level = pass_manager_config.optimization_level + seed_transpiler = pass_manager_config.seed_transpiler + target = pass_manager_config.target + coupling_map = pass_manager_config.coupling_map + backend_properties = pass_manager_config.backend_properties + if opt_level == 0: + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 1: + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=True, + ) + elif opt_level == 2: + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 3: + routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + else: + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + + +class BasicSwapPassManager(PassManagerStagePlugin): + """Plugin class for routing stage with :class:`~.BasicSwap`""" + + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """Build routing stage PassManager.""" + opt_level = pass_manager_config.optimization_level + seed_transpiler = pass_manager_config.seed_transpiler + target = pass_manager_config.target + coupling_map = pass_manager_config.coupling_map + backend_properties = pass_manager_config.backend_properties + routing_pass = BasicSwap(coupling_map) + if opt_level == 0: + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 1: + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=True, + ) + elif opt_level == 2: + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 3: + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + else: + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + + +class StochasticSwapPassManager(PassManagerStagePlugin): + """Plugin class for routing stage with :class:`~.StochasticSwap`""" + + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """Build routing stage PassManager.""" + opt_level = pass_manager_config.optimization_level + seed_transpiler = pass_manager_config.seed_transpiler + target = pass_manager_config.target + coupling_map = pass_manager_config.coupling_map + backend_properties = pass_manager_config.backend_properties + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) + if opt_level == 0: + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 1: + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=True, + ) + elif opt_level == 2: + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 3: + routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + else: + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + + +class LookaheadSwapPassManager(PassManagerStagePlugin): + """Plugin class for routing stage with :class:`~.LookaheadSwap`""" + + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """Build routing stage PassManager.""" + opt_level = pass_manager_config.optimization_level + seed_transpiler = pass_manager_config.seed_transpiler + target = pass_manager_config.target + coupling_map = pass_manager_config.coupling_map + backend_properties = pass_manager_config.backend_properties + if opt_level == 0: + routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 1: + routing_pass = LookaheadSwap(coupling_map, search_depth=4, search_width=4) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=True, + ) + elif opt_level == 2: + routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 3: + routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + else: + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + + +class SabreSwapPassManager(PassManagerStagePlugin): + """Plugin class for routing stage with :class:`~.SabreSwap`""" + + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """Build routing stage PassManager.""" + opt_level = pass_manager_config.optimization_level + seed_transpiler = pass_manager_config.seed_transpiler + target = pass_manager_config.target + coupling_map = pass_manager_config.coupling_map + backend_properties = pass_manager_config.backend_properties + if opt_level == 0: + routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 1: + routing_pass = SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=True, + ) + elif opt_level == 2: + routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + elif opt_level == 3: + routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) + vf2_call_limit = None + if ( + pass_manager_config.layout_method is None + and pass_manager_config.initial_layout is None + ): + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) + else: + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + + +class NoneRoutingPassManager(PassManagerStagePlugin): + """Plugin class for routing stage with error on routing.""" + + def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + """Build routing stage PassManager.""" + seed_transpiler = pass_manager_config.seed_transpiler + target = pass_manager_config.target + coupling_map = pass_manager_config.coupling_map + routing_pass = Error( + msg="No routing method selected, but circuit is not routed to device. " + "CheckMap Error: {check_map_msg}", + action="raise", + ) + return common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=True, + ) diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 37aadc1c4452..44bae104ed82 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -70,6 +70,11 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target + # Override an unset optimization_level for stage plugin use. + # it will be restored to None before this is returned + optimization_level = pass_manager_config.optimization_level + if optimization_level is None: + pass_manager_config.optimization_level = 0 # Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) @@ -173,6 +178,8 @@ def _choose_layout_condition(property_set): sched = common.generate_scheduling( instruction_durations, scheduling_method, timing_constraints, inst_map ) + # Restore PassManagerConfig optimization_level override + pass_manager_config.optimization_level = optimization_level return StagedPassManager( init=unroll_3q, diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 6020eb76fc1c..8ca7c93f372c 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -27,16 +27,11 @@ from qiskit.transpiler.passes import DenseLayout from qiskit.transpiler.passes import NoiseAdaptiveLayout from qiskit.transpiler.passes import SabreLayout -from qiskit.transpiler.passes import BasicSwap -from qiskit.transpiler.passes import LookaheadSwap -from qiskit.transpiler.passes import StochasticSwap -from qiskit.transpiler.passes import SabreSwap from qiskit.transpiler.passes import FixedPoint from qiskit.transpiler.passes import Depth from qiskit.transpiler.passes import Size from qiskit.transpiler.passes import Optimize1qGatesDecomposition from qiskit.transpiler.passes import Layout2qDistance -from qiskit.transpiler.passes import Error from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason @@ -66,23 +61,13 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa Raises: TranspilerError: if the passmanager config is invalid. """ - plugin_manager = None - # pylint: disable=too-many-boolean-expressions - if ( - pass_manager_config.layout_method is not None - or pass_manager_config.routing_method is not None - or pass_manager_config.translation_method is not None - or pass_manager_config.scheduling_method is not None - or pass_manager_config.init_method - or pass_manager_config.optimization_method is not None - ): - plugin_manager = PassManagerStagePluginManager() + plugin_manager = PassManagerStagePluginManager() basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" - routing_method = pass_manager_config.routing_method or "stochastic" + routing_method = pass_manager_config.routing_method or "default" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method init_method = pass_manager_config.init_method @@ -95,6 +80,11 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() target = pass_manager_config.target + # Override an unset optimization_level for stage plugin use. + # it will be restored to None before this is returned + optimization_level = pass_manager_config.optimization_level + if optimization_level is None: + pass_manager_config.optimization_level = 1 # Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) @@ -162,15 +152,8 @@ def _vf2_match_not_found(property_set): toqm_pass = False routing_pm = None - if routing_method == "basic": - routing_pass = BasicSwap(coupling_map) - elif routing_method == "stochastic": - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - elif routing_method == "lookahead": - routing_pass = LookaheadSwap(coupling_map, search_depth=4, search_width=4) - elif routing_method == "sabre": - routing_pass = SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) - elif routing_method == "toqm": + # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface + if routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO1, latencies_from_target @@ -188,11 +171,18 @@ def _vf2_match_not_found(property_set): ) ), ) - elif routing_method == "none": - routing_pass = Error( - msg="No routing method selected, but circuit is not routed to device. " - "CheckMap Error: {check_map_msg}", - action="raise", + vf2_call_limit = None + if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + routing_pm = common.generate_routing_passmanager( + routing_pass, + target, + coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + check_trivial=True, + use_barrier_before_measurement=not toqm_pass, ) else: routing_pm = plugin_manager.get_passmanager_stage( @@ -231,22 +221,8 @@ def _opt_control(property_set): layout.append(_improve_layout, condition=_vf2_match_not_found) layout += common.generate_embed_passmanager(coupling_map) - vf2_call_limit = None - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - if routing_pm is not None: - routing = routing_pm - else: - routing = common.generate_routing_passmanager( - routing_pass, - target, - coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - check_trivial=True, - use_barrier_before_measurement=not toqm_pass, - ) + routing = routing_pm + else: layout = None routing = None @@ -297,6 +273,8 @@ def _opt_control(property_set): init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) else: init = unroll_3q + # Restore PassManagerConfig optimization_level override + pass_manager_config.optimization_level = optimization_level return StagedPassManager( init=init, diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index aeac570076fe..2382ee22df06 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -27,16 +27,11 @@ from qiskit.transpiler.passes import DenseLayout from qiskit.transpiler.passes import NoiseAdaptiveLayout from qiskit.transpiler.passes import SabreLayout -from qiskit.transpiler.passes import BasicSwap -from qiskit.transpiler.passes import LookaheadSwap -from qiskit.transpiler.passes import StochasticSwap -from qiskit.transpiler.passes import SabreSwap from qiskit.transpiler.passes import FixedPoint from qiskit.transpiler.passes import Depth from qiskit.transpiler.passes import Size from qiskit.transpiler.passes import Optimize1qGatesDecomposition from qiskit.transpiler.passes import CommutativeCancellation -from qiskit.transpiler.passes import Error from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason @@ -68,23 +63,13 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa Raises: TranspilerError: if the passmanager config is invalid. """ - plugin_manager = None - # pylint: disable=too-many-boolean-expressions - if ( - pass_manager_config.layout_method is not None - or pass_manager_config.routing_method is not None - or pass_manager_config.translation_method is not None - or pass_manager_config.scheduling_method is not None - or pass_manager_config.init_method - or pass_manager_config.optimization_method is not None - ): - plugin_manager = PassManagerStagePluginManager() + plugin_manager = PassManagerStagePluginManager() basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" - routing_method = pass_manager_config.routing_method or "stochastic" + routing_method = pass_manager_config.routing_method or "default" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method init_method = pass_manager_config.init_method @@ -97,6 +82,11 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target + # Override an unset optimization_level for stage plugin use. + # it will be restored to None before this is returned + optimization_level = pass_manager_config.optimization_level + if optimization_level is None: + pass_manager_config.optimization_level = 2 # Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) @@ -145,15 +135,8 @@ def _vf2_match_not_found(property_set): toqm_pass = False routing_pm = None - if routing_method == "basic": - routing_pass = BasicSwap(coupling_map) - elif routing_method == "stochastic": - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - elif routing_method == "lookahead": - routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=5) - elif routing_method == "sabre": - routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) - elif routing_method == "toqm": + # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface + if routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO2, latencies_from_target @@ -171,11 +154,17 @@ def _vf2_match_not_found(property_set): ) ), ) - elif routing_method == "none": - routing_pass = Error( - msg="No routing method selected, but circuit is not routed to device. " - "CheckMap Error: {check_map_msg}", - action="raise", + vf2_call_limit = None + if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + routing_pm = common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=not toqm_pass, ) else: routing_pm = plugin_manager.get_passmanager_stage( @@ -215,21 +204,7 @@ def _opt_control(property_set): layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append(_choose_layout_1, condition=_vf2_match_not_found) layout += common.generate_embed_passmanager(coupling_map) - vf2_call_limit = None - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 - if routing_pm is not None: - routing = routing_pm - else: - routing = common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=not toqm_pass, - ) + routing = routing_pm else: layout = None routing = None @@ -280,6 +255,9 @@ def _opt_control(property_set): else: init = unroll_3q + # Restore PassManagerConfig optimization_level override + pass_manager_config.optimization_level = optimization_level + return StagedPassManager( init=init, layout=layout, diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 7a0f22319ecb..d0c9e5e1ade4 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -90,6 +90,11 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target + # Override an unset optimization_level for stage plugin use. + # it will be restored to None before this is returned + optimization_level = pass_manager_config.optimization_level + if optimization_level is None: + pass_manager_config.optimization_level = 3 # Layout on good qubits if calibration info available, otherwise on dense links _given_layout = SetLayout(initial_layout) @@ -271,6 +276,9 @@ def _opt_control(property_set): sched = common.generate_scheduling( instruction_durations, scheduling_method, timing_constraints, inst_map ) + # Restore PassManagerConfig optimization_level override + pass_manager_config.optimization_level = optimization_level + return StagedPassManager( init=init, layout=layout, diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 4058cf890e5d..bded9b0e420f 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -218,10 +218,13 @@ def _build_pm( plugin_name: str, pm_config: PassManagerConfig, ): - plugin_obj = stage_obj.get(plugin_name) - if plugin_obj is None: - raise TranspilerError(f"Invalid plugin name {plugin_name} for stage {stage_name}") - return plugin_obj.pass_manager(pm_config) + try: + plugin_obj = stage_obj[plugin_name] + except KeyError as err: + raise TranspilerError( + f"Invalid plugin name {plugin_name} for stage {stage_name}" + ) from err + return plugin_obj.obj.pass_manager(pm_config) def list_stage_plugins(stage_name: str) -> List[str]: diff --git a/setup.py b/setup.py index 5cdccc12ceea..9498df0c9dab 100755 --- a/setup.py +++ b/setup.py @@ -100,6 +100,14 @@ "qiskit.unitary_synthesis": [ "default = qiskit.transpiler.passes.synthesis.unitary_synthesis:DefaultUnitarySynthesis", "aqc = qiskit.transpiler.synthesis.aqc.aqc_plugin:AQCSynthesisPlugin", - ] + ], + "qiskit.transpiler.routing": [ + "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultRoutingPassManager", + "basic = qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager", + "stochastic = qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager", + "lookahead = qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager", + "sabre = qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager", + "none = qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager", + ], }, ) From 19df4a0d90c5f6706d211be9dc921ae11a23b58f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 22 Jul 2022 09:11:29 -0400 Subject: [PATCH 05/31] Add plugin usage to level 3 --- .../transpiler/preset_passmanagers/level3.py | 148 ++++++++++-------- 1 file changed, 81 insertions(+), 67 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index d0c9e5e1ade4..ff438edd28d0 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -28,10 +28,6 @@ from qiskit.transpiler.passes import DenseLayout from qiskit.transpiler.passes import NoiseAdaptiveLayout from qiskit.transpiler.passes import SabreLayout -from qiskit.transpiler.passes import BasicSwap -from qiskit.transpiler.passes import LookaheadSwap -from qiskit.transpiler.passes import StochasticSwap -from qiskit.transpiler.passes import SabreSwap from qiskit.transpiler.passes import FixedPoint from qiskit.transpiler.passes import Depth from qiskit.transpiler.passes import Size @@ -43,10 +39,9 @@ from qiskit.transpiler.passes import Collect2qBlocks from qiskit.transpiler.passes import ConsolidateBlocks from qiskit.transpiler.passes import UnitarySynthesis -from qiskit.transpiler.passes import Error from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason - +from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM @@ -74,14 +69,17 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa Raises: TranspilerError: if the passmanager config is invalid. """ + plugin_manager = PassManagerStagePluginManager() basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "sabre" - routing_method = pass_manager_config.routing_method or "sabre" + routing_method = pass_manager_config.routing_method or "default" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method + init_method = pass_manager_config.init_method + optimization_method = pass_manager_config.optimization_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties @@ -142,15 +140,7 @@ def _vf2_match_not_found(property_set): raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False - if routing_method == "basic": - routing_pass = BasicSwap(coupling_map) - elif routing_method == "stochastic": - routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) - elif routing_method == "lookahead": - routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) - elif routing_method == "sabre": - routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) - elif routing_method == "toqm": + if routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO3, latencies_from_target @@ -168,14 +158,22 @@ def _vf2_match_not_found(property_set): ) ), ) - elif routing_method == "none": - routing_pass = Error( - msg="No routing method selected, but circuit is not routed to device. " - "CheckMap Error: {check_map_msg}", - action="raise", + vf2_call_limit = None + if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + routing_pm = common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + vf2_call_limit=vf2_call_limit, + backend_properties=backend_properties, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=not toqm_pass, ) else: - raise TranspilerError("Invalid routing method %s." % routing_method) + routing_pm = plugin_manager.get_passmanager_stage( + "routing", routing_method, pass_manager_config + ) # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. @@ -202,53 +200,57 @@ def _opt_control(property_set): ] # Build pass manager - init = common.generate_unroll_3q( - target, - basis_gates, - approximation_degree, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - ) + if init_method is not None: + init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + else: + init = common.generate_unroll_3q( + target, + basis_gates, + approximation_degree, + unitary_synthesis_method, + unitary_synthesis_plugin_config, + ) init.append(RemoveResetInZeroState()) init.append(OptimizeSwapBeforeMeasure()) init.append(RemoveDiagonalGatesBeforeMeasure()) if coupling_map or initial_layout: - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout_0, condition=_choose_layout_condition) - layout.append(_choose_layout_1, condition=_vf2_match_not_found) - layout += common.generate_embed_passmanager(coupling_map) - vf2_call_limit = None - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 - routing = common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=not toqm_pass, - ) + if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config + ) + else: + layout = PassManager() + layout.append(_given_layout) + layout.append(_choose_layout_0, condition=_choose_layout_condition) + layout.append(_choose_layout_1, condition=_vf2_match_not_found) + layout += common.generate_embed_passmanager(coupling_map) + routing = routing_pm else: layout = None routing = None - translation = common.generate_translation_passmanager( - target, - basis_gates, - translation_method, - approximation_degree, - coupling_map, - backend_properties, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - ) + if translation_method not in {"translator", "synthesis", "unroller"}: + translation = plugin_manager.get_passmanager_stage( + "translation", translation_method, pass_manager_config + ) + else: + translation = common.generate_translation_passmanager( + target, + basis_gates, + translation_method, + approximation_degree, + coupling_map, + backend_properties, + unitary_synthesis_method, + unitary_synthesis_plugin_config, + ) pre_routing = None if toqm_pass: pre_routing = translation - optimization = PassManager() - unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] - optimization.append(_depth_check + _size_check) + optimization = None + if optimization_method is None: + optimization = PassManager() + unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] + optimization.append(_depth_check + _size_check) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) ): @@ -262,20 +264,32 @@ def _opt_control(property_set): # optimization loop to correct for incorrect directions that might be # inserted by UnitarySynthesis which is direction aware but only via # the coupling map which with a target doesn't give a full picture - if target is not None: + if target is not None and optimization is not None: optimization.append( _opt + unroll + _depth_check + _size_check + _direction, do_while=_opt_control ) - else: + elif optimization is not None: optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) - optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) - opt_loop = _depth_check + _opt + unroll - optimization.append(opt_loop, do_while=_opt_control) - sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map - ) + if optimization is not None: + optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) + if optimization is None: + optimization = plugin_manager.get_passmanager_stage( + "optimization", optimization_method, pass_manager_config + ) + else: + opt_loop = _depth_check + _opt + unroll + optimization.append(opt_loop, do_while=_opt_control) + if scheduling_method is None or scheduling_method in {"alap", "asap"}: + sched = common.generate_scheduling( + instruction_durations, scheduling_method, timing_constraints, inst_map + ) + else: + sched = plugin_manager.get_passmanager_stage( + "scheduling", scheduling_method, pass_manager_config + ) + # Restore PassManagerConfig optimization_level override pass_manager_config.optimization_level = optimization_level From 0e1172352ffb2adf32654e8483d7f868e05eeb51 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 22 Jul 2022 09:21:01 -0400 Subject: [PATCH 06/31] Add plugin support to level 0 preset pass manager --- .../transpiler/preset_passmanagers/level0.py | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 44bae104ed82..d2bfc8638144 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -25,12 +25,8 @@ from qiskit.transpiler.passes import DenseLayout from qiskit.transpiler.passes import NoiseAdaptiveLayout from qiskit.transpiler.passes import SabreLayout -from qiskit.transpiler.passes import BasicSwap -from qiskit.transpiler.passes import LookaheadSwap -from qiskit.transpiler.passes import StochasticSwap -from qiskit.transpiler.passes import SabreSwap -from qiskit.transpiler.passes import Error from qiskit.transpiler.preset_passmanagers import common +from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM @@ -54,14 +50,17 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa Raises: TranspilerError: if the passmanager config is invalid. """ + plugin_manager = PassManagerStagePluginManager() basis_gates = pass_manager_config.basis_gates inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "trivial" - routing_method = pass_manager_config.routing_method or "stochastic" + routing_method = pass_manager_config.routing_method or "default" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method + init_method = pass_manager_config.init_method + optimization_method = pass_manager_config.optimization_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties @@ -95,15 +94,7 @@ def _choose_layout_condition(property_set): toqm_pass = False # Choose routing pass - if routing_method == "basic": - routing_pass = BasicSwap(coupling_map) - elif routing_method == "stochastic": - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - elif routing_method == "lookahead": - routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) - elif routing_method == "sabre": - routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) - elif routing_method == "toqm": + if routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO0, latencies_from_target @@ -121,14 +112,17 @@ def _choose_layout_condition(property_set): ) ), ) - elif routing_method == "none": - routing_pass = Error( - msg="No routing method selected, but circuit is not routed to device. " - "CheckMap Error: {check_map_msg}", - action="raise", + routing_pm = common.generate_routing_passmanager( + routing_pass, + target, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + use_barrier_before_measurement=not toqm_pass, ) else: - raise TranspilerError("Invalid routing method %s." % routing_method) + routing_pm = plugin_manager.get_passmanager_stage( + "routing", routing_method, pass_manager_config + ) unroll_3q = None # Build pass manager @@ -140,30 +134,34 @@ def _choose_layout_condition(property_set): unitary_synthesis_method, unitary_synthesis_plugin_config, ) - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout, condition=_choose_layout_condition) - layout += common.generate_embed_passmanager(coupling_map) - routing = common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=not toqm_pass, - ) + if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config + ) + else: + layout = PassManager() + layout.append(_given_layout) + layout.append(_choose_layout, condition=_choose_layout_condition) + layout += common.generate_embed_passmanager(coupling_map) + routing = routing_pm else: layout = None routing = None - translation = common.generate_translation_passmanager( - target, - basis_gates, - translation_method, - approximation_degree, - coupling_map, - backend_properties, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - ) + if translation_method not in {"translator", "synthesis", "unroller"}: + translation = plugin_manager.get_passmanager_stage( + "translation", translation_method, pass_manager_config + ) + else: + translation = common.generate_translation_passmanager( + target, + basis_gates, + translation_method, + approximation_degree, + coupling_map, + backend_properties, + unitary_synthesis_method, + unitary_synthesis_plugin_config, + ) pre_routing = None if toqm_pass: pre_routing = translation @@ -175,18 +173,34 @@ def _choose_layout_condition(property_set): pre_opt += translation else: pre_opt = None - sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map - ) + if scheduling_method is None or scheduling_method in {"alap", "asap"}: + sched = common.generate_scheduling( + instruction_durations, scheduling_method, timing_constraints, inst_map + ) + else: + sched = plugin_manager.get_passmanager_stage( + "scheduling", scheduling_method, pass_manager_config + ) + if init_method is not None: + init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + else: + init = unroll_3q + optimization = None + if optimization_method is not None: + optimization = plugin_manager.get_passmanager_stage( + "optimization", optimization_method, pass_manager_config + ) + # Restore PassManagerConfig optimization_level override pass_manager_config.optimization_level = optimization_level return StagedPassManager( - init=unroll_3q, + init=init, layout=layout, pre_routing=pre_routing, routing=routing, translation=translation, pre_optimization=pre_opt, + optimization=optimization, scheduling=sched, ) From fb72d1df15bef47cde531c1532a476980276fb56 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 22 Jul 2022 09:22:34 -0400 Subject: [PATCH 07/31] Add default plugin to reserved routing methods list --- qiskit/transpiler/preset_passmanagers/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index bded9b0e420f..cedae9e4e68e 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -63,7 +63,7 @@ :func:`~.generate_embed_passmanager`. * - ``routing`` - ``qiskit.transpiler.routing`` - - ``basic``, ``stochastic``, ``lookahead``, ``sabre``, ``toqm`` + - ``basic``, ``stochastic``, ``lookahead``, ``sabre``, ``toqm``, ``default`` - The output from this stage is expected to have the circuit match the connectivity constraints of the target backend. This does not necessarily need to match the directionality of the edges in the target as a later From 4ea994962540a9dbe2d637770dd37ccb0a456bd2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 22 Jul 2022 09:50:43 -0400 Subject: [PATCH 08/31] Add release notes --- ...age-plugin-interface-47daae40f7d0ad3c.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml diff --git a/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml b/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml new file mode 100644 index 000000000000..6abd84dfa113 --- /dev/null +++ b/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml @@ -0,0 +1,32 @@ +--- +features: + - | + Introduced a new plugin interface for transpiler stages which is used to + enable alternative :class:`~.PassManager` objects from an external package + in a particular stage as part of :func:`~.transpile` or the + :class:`~.StagedPassManager` output from + :func:`~.generate_preset_pass_manager`, :func:`~.level_0_pass_manager`, + :func:`~.level_1_pass_manager`, :func:`~.level_2_pass_manager`, and + :func:`~.level_3_pass_manager`. Users can select a plugin to use for a + transpiler stage with the ``init_method``, ``layout_method``, + ``routing_method``, ``translation_method``, ``optimization_method``, and + ``scheduling_method`` keyword arguments on :func:`~.transpile` and + :func:`~.generate_preset_pass_manager`. A full list of plugin names + currently installed this can be found with the :func:`.list_stage_plugins` + function. For creating plugins refer to the + :mod:`qiskit.transpiler.preset_passmanagers.plugin` module documentation + which includes a guide for writing stage plugins. + - | + The :func:`~.transpile` has two new keyword arguments, ``init_method`` and + ``optimization_method`` which are used to specify alternative plugins to + use for the ``init`` stage and ``optimization`` stages respectively. + - | + The :class:`~.PassManagerConfig` class has 3 new attributes, + :attr:`~.PassManagerConfig.init_method`, + :attr:`~.PassManagerConfig.optimization_method`, and + :attr:`~.PassManagerConfig.optimization_level` along with matching keyword + arguments on the constructor methods. The first two attributes repreesent + the user specified ``init`` and ``optimization`` plugins to use for + compilation. The :attr:`~.PassManagerConfig.optimization_level` attribute + represents the compilations optimization level if specified which can + be used to inform stage plugin behavior. From e96b12078ac4f6afa28df4606ea1715577550fb0 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 22 Jul 2022 15:01:21 -0400 Subject: [PATCH 09/31] Add tests --- .../transpiler/preset_passmanagers/plugin.py | 4 +- test/python/transpiler/test_stage_plugin.py | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 test/python/transpiler/test_stage_plugin.py diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index cedae9e4e68e..bc942f1a5c2c 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -204,7 +204,7 @@ def get_passmanager_stage( return self._build_pm(self.routing_plugins, stage_name, plugin_name, pm_config) elif stage_name == "translation": return self._build_pm(self.translation_plugins, stage_name, plugin_name, pm_config) - elif stage_name == "optimization_plugins": + elif stage_name == "optimization": return self._build_pm(self.optimization_plugins, stage_name, plugin_name, pm_config) elif stage_name == "scheduling": return self._build_pm(self.scheduling_plugins, stage_name, plugin_name, pm_config) @@ -248,7 +248,7 @@ def list_stage_plugins(stage_name: str) -> List[str]: return plugin_mgr.routing_plugins.names() elif stage_name == "translation": return plugin_mgr.translation_plugins.names() - elif stage_name == "optimization_plugins": + elif stage_name == "optimization": return plugin_mgr.optimization_plugins.names() elif stage_name == "scheduling": return plugin_mgr.scheduling_plugins.names() diff --git a/test/python/transpiler/test_stage_plugin.py b/test/python/transpiler/test_stage_plugin.py new file mode 100644 index 000000000000..f4464829a6e0 --- /dev/null +++ b/test/python/transpiler/test_stage_plugin.py @@ -0,0 +1,66 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Tests for the UnitarySynthesis transpiler pass. +""" + +from qiskit.test import QiskitTestCase +from qiskit.transpiler import PassManager, PassManagerConfig +from qiskit.transpiler.preset_passmanagers.plugin import ( + PassManagerStagePluginManager, + list_stage_plugins, +) +from qiskit.transpiler.exceptions import TranspilerError + + +class TestStagePassManagerPlugin(QiskitTestCase): + """Tests for the transpiler stage plugin interface.""" + + def test_list_stage_plugins(self): + """Test list stage plugin function.""" + routing_passes = list_stage_plugins("routing") + self.assertIn("default", routing_passes) + self.assertIn("sabre", routing_passes) + self.assertIsInstance(list_stage_plugins("init"), list) + self.assertIsInstance(list_stage_plugins("layout"), list) + self.assertIsInstance(list_stage_plugins("translation"), list) + self.assertIsInstance(list_stage_plugins("optimization"), list) + self.assertIsInstance(list_stage_plugins("scheduling"), list) + + def test_list_stage_plugins_invalid_stage_name(self): + """Test list stage plugin function with invalid stage name.""" + with self.assertRaises(TranspilerError): + list_stage_plugins("not_a_stage") + + def test_build_pm_invalid_plugin_name_valid_stage(self): + """Test get pm from plugin with invalid plugin name and valid stage.""" + plugin_manager = PassManagerStagePluginManager() + with self.assertRaises(TranspilerError): + plugin_manager.get_passmanager_stage( + "init", "fake_plugin_not_real", PassManagerConfig() + ) + + def test_build_pm_invalid_stage(self): + """Test get pm from plugin with invalid stage.""" + plugin_manager = PassManagerStagePluginManager() + with self.assertRaises(TranspilerError): + plugin_manager.get_passmanager_stage( + "not_a_sage", "fake_plugin_not_real", PassManagerConfig() + ) + + def test_build_pm(self): + """Test get pm from plugin.""" + plugin_manager = PassManagerStagePluginManager() + pm_config = PassManagerConfig(optimization_level=3) + pm = plugin_manager.get_passmanager_stage("routing", "sabre", pm_config) + self.assertIsInstance(pm, PassManager) From a0054ed832f4e1b03b5a529723be37f6587aa995 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 27 Jul 2022 07:40:33 -0400 Subject: [PATCH 10/31] Apply suggestions from code review Co-authored-by: Alexander Ivrii --- .../notes/stage-plugin-interface-47daae40f7d0ad3c.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml b/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml index 6abd84dfa113..ea4c88c097df 100644 --- a/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml +++ b/releasenotes/notes/stage-plugin-interface-47daae40f7d0ad3c.yaml @@ -12,7 +12,7 @@ features: ``routing_method``, ``translation_method``, ``optimization_method``, and ``scheduling_method`` keyword arguments on :func:`~.transpile` and :func:`~.generate_preset_pass_manager`. A full list of plugin names - currently installed this can be found with the :func:`.list_stage_plugins` + currently installed can be found with the :func:`.list_stage_plugins` function. For creating plugins refer to the :mod:`qiskit.transpiler.preset_passmanagers.plugin` module documentation which includes a guide for writing stage plugins. @@ -25,7 +25,7 @@ features: :attr:`~.PassManagerConfig.init_method`, :attr:`~.PassManagerConfig.optimization_method`, and :attr:`~.PassManagerConfig.optimization_level` along with matching keyword - arguments on the constructor methods. The first two attributes repreesent + arguments on the constructor methods. The first two attributes represent the user specified ``init`` and ``optimization`` plugins to use for compilation. The :attr:`~.PassManagerConfig.optimization_level` attribute represents the compilations optimization level if specified which can From 0599830c3a6f2e5270af424f15b016473fe35766 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 27 Jul 2022 08:23:40 -0400 Subject: [PATCH 11/31] Apply suggestions from code review Co-authored-by: Alexander Ivrii --- qiskit/transpiler/preset_passmanagers/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index bc942f1a5c2c..ad3cf83a780b 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -57,7 +57,7 @@ - ``trivial``, ``dense``, ``noise_adaptive``, ``sabre`` - The output from this staged is expected to have the ``layout`` property set field set with a :class:`~.Layout` object. Additionally, the circuit is - is typically expected to be embedded so that it expanded to include all + typically expected to be embedded so that it expanded to include all qubits and the :class:`~.ApplyLayout` pass is expected to be run to apply the layout. The embedding of the :class:`~.Layout` can be generated with :func:`~.generate_embed_passmanager`. @@ -156,7 +156,7 @@ def pass_manager(self, pass_manager_config): class PassManagerStagePlugin(abc.ABC): """A ``PassManagerStagePlugin`` is a plugin interface object for using custom - stages in :func:`transpile()`. + stages in :func:`~. transpile`. A ``PassManagerStagePlugin`` object can be added to an external package and From 62b4f3f50718e0d28c9d8d5c23c1e9aaff5f9783 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 27 Jul 2022 08:57:11 -0400 Subject: [PATCH 12/31] Remove raise on non-builtin layout method argument --- qiskit/transpiler/preset_passmanagers/level0.py | 2 -- qiskit/transpiler/preset_passmanagers/level1.py | 2 -- qiskit/transpiler/preset_passmanagers/level2.py | 2 -- qiskit/transpiler/preset_passmanagers/level3.py | 2 -- 4 files changed, 8 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index d2bfc8638144..52c50e5f9e24 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -89,8 +89,6 @@ def _choose_layout_condition(property_set): _choose_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout = SabreLayout(coupling_map, max_iterations=1, seed=seed_transpiler) - else: - raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False # Choose routing pass diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 8ca7c93f372c..5934842ced97 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -147,8 +147,6 @@ def _vf2_match_not_found(property_set): _improve_layout = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _improve_layout = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) - else: - raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False routing_pm = None diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 2382ee22df06..be8cc784f01c 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -130,8 +130,6 @@ def _vf2_match_not_found(property_set): _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_1 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler) - else: - raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False routing_pm = None diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index ff438edd28d0..13ac13fc3432 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -136,8 +136,6 @@ def _vf2_match_not_found(property_set): _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) elif layout_method == "sabre": _choose_layout_1 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) - else: - raise TranspilerError("Invalid layout method %s." % layout_method) toqm_pass = False if routing_method == "toqm": From a52f7ed868c810c63f03b353fcbb0f809cf80150 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 27 Jul 2022 08:58:32 -0400 Subject: [PATCH 13/31] Fix typo --- qiskit/transpiler/preset_passmanagers/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index ad3cf83a780b..efa3e4dbf3e7 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -156,7 +156,7 @@ def pass_manager(self, pass_manager_config): class PassManagerStagePlugin(abc.ABC): """A ``PassManagerStagePlugin`` is a plugin interface object for using custom - stages in :func:`~. transpile`. + stages in :func:`~.transpile`. A ``PassManagerStagePlugin`` object can be added to an external package and From 5dec6c81e7afaba95b0bf1ee150a297030002a26 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 27 Jul 2022 09:13:49 -0400 Subject: [PATCH 14/31] Deduplicate code in built-in routing plugins This commit deduplicates the code in the built-in routing stage plugins. First it removes the default plugin which was duplicated with the stochastic and sabre plugins. There was no functional difference between just setting the implicit default method name and using a per method plugin and having a standalone default plugin. Secondly all the vf2 call limit code is abstracted into a helper function which reduces code duplication. --- .../preset_passmanagers/builtin_plugins.py | 167 ++---------------- .../transpiler/preset_passmanagers/level0.py | 2 +- .../transpiler/preset_passmanagers/level1.py | 2 +- .../transpiler/preset_passmanagers/level2.py | 2 +- .../transpiler/preset_passmanagers/level3.py | 2 +- .../transpiler/preset_passmanagers/plugin.py | 2 +- test/python/transpiler/test_stage_plugin.py | 4 +- 7 files changed, 25 insertions(+), 156 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 668f6fe7bd33..561913b15ef5 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -24,80 +24,18 @@ from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin -class DefaultRoutingPassManager(PassManagerStagePlugin): - """Plugin class for default routing stage.""" - - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: - """Build routing stage PassManager.""" - opt_level = pass_manager_config.optimization_level - seed_transpiler = pass_manager_config.seed_transpiler - target = pass_manager_config.target - coupling_map = pass_manager_config.coupling_map - backend_properties = pass_manager_config.backend_properties - if opt_level == 0: - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - return common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=True, - ) - elif opt_level == 1: - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - - return common.generate_routing_passmanager( - routing_pass, - target, - coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - check_trivial=True, - use_barrier_before_measurement=True, - ) +def get_vf2_limit(pass_manager_config): + """Get the vf2 call limit for vf2 based layout passes.""" + vf2_call_limit = None + opt_level = pass_manager_config.optimization_level + if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: + if opt_level == 1: + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 elif opt_level == 2: - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 - return common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=True, - ) + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 elif opt_level == 3: - routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 - return common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=True, - ) - else: - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return vf2_call_limit class BasicSwapPassManager(PassManagerStagePlugin): @@ -111,6 +49,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties routing_pass = BasicSwap(coupling_map) + vf2_call_limit = get_vf2_limit(pass_manager_config) if opt_level == 0: return common.generate_routing_passmanager( routing_pass, @@ -120,13 +59,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 1: - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - return common.generate_routing_passmanager( routing_pass, target, @@ -138,12 +70,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 2: - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, @@ -154,12 +80,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 3: - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, @@ -183,8 +103,9 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) + vf2_call_limit = get_vf2_limit(pass_manager_config) if opt_level == 0: + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, target, @@ -193,13 +114,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 1: - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, target, @@ -211,12 +126,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 2: - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, target, @@ -227,13 +137,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 3: - routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, @@ -257,6 +160,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties + vf2_call_limit = get_vf2_limit(pass_manager_config) if opt_level == 0: routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) return common.generate_routing_passmanager( @@ -268,13 +172,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: ) elif opt_level == 1: routing_pass = LookaheadSwap(coupling_map, search_depth=4, search_width=4) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - return common.generate_routing_passmanager( routing_pass, target, @@ -287,12 +184,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: ) elif opt_level == 2: routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, @@ -304,12 +195,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: ) elif opt_level == 3: routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, @@ -333,6 +218,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties + vf2_call_limit = get_vf2_limit(pass_manager_config) if opt_level == 0: routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) return common.generate_routing_passmanager( @@ -344,13 +230,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: ) elif opt_level == 1: routing_pass = SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - return common.generate_routing_passmanager( routing_pass, target, @@ -363,12 +242,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: ) elif opt_level == 2: routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, @@ -380,12 +253,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: ) elif opt_level == 3: routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) - vf2_call_limit = None - if ( - pass_manager_config.layout_method is None - and pass_manager_config.initial_layout is None - ): - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 return common.generate_routing_passmanager( routing_pass, target, diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 52c50e5f9e24..cbc268b6d283 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -56,7 +56,7 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "trivial" - routing_method = pass_manager_config.routing_method or "default" + routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method init_method = pass_manager_config.init_method diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 5934842ced97..c4249cccd974 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -67,7 +67,7 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" - routing_method = pass_manager_config.routing_method or "default" + routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method init_method = pass_manager_config.init_method diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index be8cc784f01c..96c210aa63fc 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -69,7 +69,7 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" - routing_method = pass_manager_config.routing_method or "default" + routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method init_method = pass_manager_config.init_method diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 13ac13fc3432..b18a5ddf8605 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -75,7 +75,7 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "sabre" - routing_method = pass_manager_config.routing_method or "default" + routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" scheduling_method = pass_manager_config.scheduling_method init_method = pass_manager_config.init_method diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index efa3e4dbf3e7..390425fd72f7 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -63,7 +63,7 @@ :func:`~.generate_embed_passmanager`. * - ``routing`` - ``qiskit.transpiler.routing`` - - ``basic``, ``stochastic``, ``lookahead``, ``sabre``, ``toqm``, ``default`` + - ``basic``, ``stochastic``, ``lookahead``, ``sabre``, ``toqm`` - The output from this stage is expected to have the circuit match the connectivity constraints of the target backend. This does not necessarily need to match the directionality of the edges in the target as a later diff --git a/test/python/transpiler/test_stage_plugin.py b/test/python/transpiler/test_stage_plugin.py index f4464829a6e0..451239e357cf 100644 --- a/test/python/transpiler/test_stage_plugin.py +++ b/test/python/transpiler/test_stage_plugin.py @@ -29,8 +29,10 @@ class TestStagePassManagerPlugin(QiskitTestCase): def test_list_stage_plugins(self): """Test list stage plugin function.""" routing_passes = list_stage_plugins("routing") - self.assertIn("default", routing_passes) + self.assertIn("basic", routing_passes) self.assertIn("sabre", routing_passes) + self.assertIn("lookahead", routing_passes) + self.assertIn("stochastic", routing_passes) self.assertIsInstance(list_stage_plugins("init"), list) self.assertIsInstance(list_stage_plugins("layout"), list) self.assertIsInstance(list_stage_plugins("translation"), list) From adaa1697bf79b0749c757d762baf1003f894ed1f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 29 Jul 2022 06:49:43 -0400 Subject: [PATCH 15/31] Make vf2 call limit function private --- .../transpiler/preset_passmanagers/builtin_plugins.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 561913b15ef5..5fd179c47cb9 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -24,7 +24,7 @@ from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin -def get_vf2_limit(pass_manager_config): +def _get_vf2_call_limit(pass_manager_config): """Get the vf2 call limit for vf2 based layout passes.""" vf2_call_limit = None opt_level = pass_manager_config.optimization_level @@ -49,7 +49,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties routing_pass = BasicSwap(coupling_map) - vf2_call_limit = get_vf2_limit(pass_manager_config) + vf2_call_limit = _get_vf2_call_limit(pass_manager_config) if opt_level == 0: return common.generate_routing_passmanager( routing_pass, @@ -103,7 +103,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - vf2_call_limit = get_vf2_limit(pass_manager_config) + vf2_call_limit = _get_vf2_call_limit(pass_manager_config) if opt_level == 0: routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) return common.generate_routing_passmanager( @@ -160,7 +160,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - vf2_call_limit = get_vf2_limit(pass_manager_config) + vf2_call_limit = _get_vf2_call_limit(pass_manager_config) if opt_level == 0: routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) return common.generate_routing_passmanager( @@ -218,7 +218,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - vf2_call_limit = get_vf2_limit(pass_manager_config) + vf2_call_limit = _get_vf2_call_limit(pass_manager_config) if opt_level == 0: routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) return common.generate_routing_passmanager( From 58f63065ad5ba16c556048de9bb71ce9f6866b15 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 29 Jul 2022 06:55:11 -0400 Subject: [PATCH 16/31] Deduplicate code in stochastic swap plugin --- .../preset_passmanagers/builtin_plugins.py | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 5fd179c47cb9..ddfb70daef77 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -104,8 +104,12 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties vf2_call_limit = _get_vf2_call_limit(pass_manager_config) - if opt_level == 0: + if opt_level == 3: + routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) + else: routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) + + if opt_level == 0: return common.generate_routing_passmanager( routing_pass, target, @@ -114,7 +118,6 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: use_barrier_before_measurement=True, ) elif opt_level == 1: - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, target, @@ -125,18 +128,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - elif opt_level == 2: - routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - return common.generate_routing_passmanager( - routing_pass, - target, - coupling_map=coupling_map, - vf2_call_limit=vf2_call_limit, - backend_properties=backend_properties, - seed_transpiler=seed_transpiler, - use_barrier_before_measurement=True, - ) - elif opt_level == 3: + elif opt_level in {2, 3}: return common.generate_routing_passmanager( routing_pass, target, From 7963e84fffa8cc7114cdefe9b1dba1fa83cadc3a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 29 Jul 2022 07:04:11 -0400 Subject: [PATCH 17/31] Expand example plugin documentation to show more complete use case --- .../transpiler/preset_passmanagers/plugin.py | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 390425fd72f7..2652944f8fbc 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -95,21 +95,38 @@ to create a subclass of the abstract plugin class :class:`~.PassManagerStagePluginManager` which is used to define how the :class:`~.PassManager` for the stage will be constructed. For example, to create a ``layout`` stage plugin that just -runs :class:`~.VF2Layout`:: +runs :class:`~.VF2Layout` and will fallback to use :class:`~.TrivialLayout` if +:class:`~VF2Layout` is unable to find a perfect layout:: from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import VF2Layout + from qiskit.transpiler.passes import VF2Layout, TrivialLayout + from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason + + + def _vf2_match_not_found(property_set): + return property_set["layout"] is None or ( + property_set["VF2Layout_stop_reason"] is not None + and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND + class VF2LayoutPlugin(PassManagerStagePlugin): def pass_manager(self, pass_manager_config): - layout_pm = PassManager([VF2Layout( - coupling_map=pass_manager_config.coupling_map, - properties=pass_manager_config.backend_properties, - target=pass_manager_config.target - )]) + layout_pm = PassManager( + [ + VF2Layout( + coupling_map=pass_manager_config.coupling_map, + properties=pass_manager_config.backend_properties, + target=pass_manager_config.target + ) + ] + ) + layout_pm.append( + TrivialLayout(pass_manager_config.coupling_map), + condition=_vf2_match_not_found, + ) layout_pm += common.generate_embed_passmanager(pass_manager_config.coupling_map) return layout_pm From 3c19b4a98d75eb7a6f497a0a4a0b5b8d965061fe Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 29 Jul 2022 15:03:41 -0400 Subject: [PATCH 18/31] Update qiskit/transpiler/preset_passmanagers/level1.py Co-authored-by: Luciano Bello --- qiskit/transpiler/preset_passmanagers/level1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index c4249cccd974..9532867bf01e 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -246,7 +246,7 @@ def _opt_control(property_set): if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) ): - pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) + pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, remove_reset_in_zero=True) else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) if optimization_method is None: From 2c03969c4f6ef5acf7d2e8068630c8fa235e449f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 29 Jul 2022 15:10:26 -0400 Subject: [PATCH 19/31] Add missing TODO comment --- qiskit/transpiler/preset_passmanagers/level3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index b18a5ddf8605..2f9061c8eb46 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -138,6 +138,7 @@ def _vf2_match_not_found(property_set): _choose_layout_1 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler) toqm_pass = False + # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface if routing_method == "toqm": HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO3, latencies_from_target From e0a7d38df712402fb696b1041c298eff97f07c63 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Sat, 30 Jul 2022 06:13:05 -0400 Subject: [PATCH 20/31] Unify vf2 call limit handling --- .../preset_passmanagers/builtin_plugins.py | 30 ++++++++----------- .../transpiler/preset_passmanagers/common.py | 20 +++++++++++++ .../transpiler/preset_passmanagers/level1.py | 10 ++++--- .../transpiler/preset_passmanagers/level2.py | 6 ++-- .../transpiler/preset_passmanagers/level3.py | 6 ++-- 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index ddfb70daef77..8aed46e940d0 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -24,20 +24,6 @@ from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin -def _get_vf2_call_limit(pass_manager_config): - """Get the vf2 call limit for vf2 based layout passes.""" - vf2_call_limit = None - opt_level = pass_manager_config.optimization_level - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - if opt_level == 1: - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 - elif opt_level == 2: - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 - elif opt_level == 3: - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 - return vf2_call_limit - - class BasicSwapPassManager(PassManagerStagePlugin): """Plugin class for routing stage with :class:`~.BasicSwap`""" @@ -49,7 +35,9 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties routing_pass = BasicSwap(coupling_map) - vf2_call_limit = _get_vf2_call_limit(pass_manager_config) + vf2_call_limit = common.get_vf2_call_limit( + opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) if opt_level == 0: return common.generate_routing_passmanager( routing_pass, @@ -103,7 +91,9 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - vf2_call_limit = _get_vf2_call_limit(pass_manager_config) + vf2_call_limit = common.get_vf2_call_limit( + opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) if opt_level == 3: routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) else: @@ -152,7 +142,9 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - vf2_call_limit = _get_vf2_call_limit(pass_manager_config) + vf2_call_limit = common.get_vf2_call_limit( + opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) if opt_level == 0: routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) return common.generate_routing_passmanager( @@ -210,7 +202,9 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties - vf2_call_limit = _get_vf2_call_limit(pass_manager_config) + vf2_call_limit = common.get_vf2_call_limit( + opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) if opt_level == 0: routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) return common.generate_routing_passmanager( diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index ce01dce6a26e..12b2309c1293 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -14,6 +14,8 @@ """Common preset passmanager generators.""" +from typing import Optional + from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel from qiskit.transpiler.passmanager import PassManager @@ -46,6 +48,7 @@ from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason from qiskit.transpiler.passes.layout.vf2_post_layout import VF2PostLayoutStopReason from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.layout import Layout def generate_unroll_3q( @@ -386,3 +389,20 @@ def _require_alignment(property_set): scheduling.append(PadDelay()) return scheduling + + +def get_vf2_call_limit( + optimization_level: int, + layout_method: Optional[str] = None, + initial_layout: Optional[Layout] = None, +) -> Optional[int]: + """Get the vf2 call limit for vf2 based layout passes.""" + vf2_call_limit = None + if layout_method is None and initial_layout is None: + if optimization_level == 1: + vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + elif optimization_level == 2: + vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + elif optimization_level == 3: + vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + return vf2_call_limit diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 9532867bf01e..8ed09d2f3f87 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -169,9 +169,9 @@ def _vf2_match_not_found(property_set): ) ), ) - vf2_call_limit = None - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - vf2_call_limit = int(5e4) # Set call limit to ~100ms with retworkx 0.10.2 + vf2_call_limit = common.get_vf2_call_limit( + 1, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) routing_pm = common.generate_routing_passmanager( routing_pass, target, @@ -246,7 +246,9 @@ def _opt_control(property_set): if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) ): - pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, remove_reset_in_zero=True) + pre_optimization = common.generate_pre_op_passmanager( + target, coupling_map, remove_reset_in_zero=True + ) else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) if optimization_method is None: diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 96c210aa63fc..e45404f3c710 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -152,9 +152,9 @@ def _vf2_match_not_found(property_set): ) ), ) - vf2_call_limit = None - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - vf2_call_limit = int(5e6) # Set call limit to ~10 sec with retworkx 0.10.2 + vf2_call_limit = common.get_vf2_call_limit( + 2, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) routing_pm = common.generate_routing_passmanager( routing_pass, target, diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 2f9061c8eb46..a725bef4be51 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -157,9 +157,9 @@ def _vf2_match_not_found(property_set): ) ), ) - vf2_call_limit = None - if pass_manager_config.layout_method is None and pass_manager_config.initial_layout is None: - vf2_call_limit = int(3e7) # Set call limit to ~60 sec with retworkx 0.10.2 + vf2_call_limit = common.get_vf2_call_limit( + 3, pass_manager_config.layout_method, pass_manager_config.initial_layout + ) routing_pm = common.generate_routing_passmanager( routing_pass, target, From 08a497282492ab8e4e282d05f3ebc07e70ec0439 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 2 Aug 2022 17:15:41 -0400 Subject: [PATCH 21/31] Remove default plugin from entry points --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 9498df0c9dab..ed7d0489f8d3 100755 --- a/setup.py +++ b/setup.py @@ -102,7 +102,6 @@ "aqc = qiskit.transpiler.synthesis.aqc.aqc_plugin:AQCSynthesisPlugin", ], "qiskit.transpiler.routing": [ - "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultRoutingPassManager", "basic = qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager", "stochastic = qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager", "lookahead = qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager", From 4190774c3b50a1a9b7832437dd4492f8ddea4bfc Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 2 Aug 2022 17:19:44 -0400 Subject: [PATCH 22/31] Simplify level 3 optimization stage logic --- .../transpiler/preset_passmanagers/level3.py | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index a725bef4be51..55c892b2339d 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -245,41 +245,47 @@ def _opt_control(property_set): pre_routing = None if toqm_pass: pre_routing = translation - optimization = None if optimization_method is None: optimization = PassManager() unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] optimization.append(_depth_check + _size_check) - if (coupling_map and not coupling_map.is_symmetric) or ( - target is not None and target.get_non_global_operation_names(strict_direction=True) - ): - pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) - _direction = [ - pass_ - for x in common.generate_pre_op_passmanager(target, coupling_map).passes() - for pass_ in x["passes"] - ] - # For transpiling to a target we need to run GateDirection in the - # optimization loop to correct for incorrect directions that might be - # inserted by UnitarySynthesis which is direction aware but only via - # the coupling map which with a target doesn't give a full picture - if target is not None and optimization is not None: - optimization.append( - _opt + unroll + _depth_check + _size_check + _direction, do_while=_opt_control - ) - elif optimization is not None: + if (coupling_map and not coupling_map.is_symmetric) or ( + target is not None and target.get_non_global_operation_names(strict_direction=True) + ): + pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) + _direction = [ + pass_ + for x in common.generate_pre_op_passmanager(target, coupling_map).passes() + for pass_ in x["passes"] + ] + # For transpiling to a target we need to run GateDirection in the + # optimization loop to correct for incorrect directions that might be + # inserted by UnitarySynthesis which is direction aware but only via + # the coupling map which with a target doesn't give a full picture + if target is not None and optimization is not None: + optimization.append( + _opt + unroll + _depth_check + _size_check + _direction, do_while=_opt_control + ) + elif optimization is not None: + optimization.append( + _opt + unroll + _depth_check + _size_check, do_while=_opt_control + ) + else: + pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) + opt_loop = _depth_check + _opt + unroll + optimization.append(opt_loop, do_while=_opt_control) else: - pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) - if optimization is not None: - optimization.append(_opt + unroll + _depth_check + _size_check, do_while=_opt_control) - if optimization is None: optimization = plugin_manager.get_passmanager_stage( "optimization", optimization_method, pass_manager_config ) - else: - opt_loop = _depth_check + _opt + unroll - optimization.append(opt_loop, do_while=_opt_control) + if (coupling_map and not coupling_map.is_symmetric) or ( + target is not None and target.get_non_global_operation_names(strict_direction=True) + ): + pre_optimization = common.generate_pre_op_passmanager(target, coupling_map, True) + else: + pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) + if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( instruction_durations, scheduling_method, timing_constraints, inst_map From 38931c8e2f81b7a43c9c0ffd3034320e9489b1f4 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 2 Aug 2022 17:22:46 -0400 Subject: [PATCH 23/31] Update qiskit/transpiler/preset_passmanagers/plugin.py Co-authored-by: Luciano Bello --- qiskit/transpiler/preset_passmanagers/plugin.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 2652944f8fbc..d335bf0b9043 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -46,11 +46,9 @@ * - ``init`` - ``qiskit.transpiler.init`` - No reserved names - - This stage runs first and is typically used for an initial logical optimization also - for most ``layout`` and ``routing`` stages this stage is used to translate any gates - that operate on more than 2 qubits into gates that operate on 1 or 2 - qubits only. This is because most layout and routing algorithms are - only designed to work with 1 and 2 qubit gates. + - This stage runs first and is typically used for any initial logical optimization. Because most layout and routing algorithms are + only designed to work with 1 and 2 qubit gates, this stage is also used to translate any gates + that operate on more than 2 qubits into gates that only operate on 1 or 2 qubits. ``init`` * - ``layout`` - ``qiskit.transpiler.layout`` From 9b928a0bfdaf65ad3c2b6c5c3694c6efa2227ca2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 2 Aug 2022 17:34:14 -0400 Subject: [PATCH 24/31] Prefer toqm plugin if one is available The qiskit-toqm project will be one of the first users of this plugin interface. Once this is released in qiskit, qiskit-toqm will likely publish their own plugin soon after and if they do we want that plugin to be used instead of the hardcoded stage in terra. This commit updates the logic for toqm handling to only use the built-in toqm if a version of qiskit-toqm is installed without a plugin present. --- qiskit/transpiler/preset_passmanagers/level0.py | 8 ++++++-- qiskit/transpiler/preset_passmanagers/level1.py | 7 +++++-- qiskit/transpiler/preset_passmanagers/level2.py | 7 +++++-- qiskit/transpiler/preset_passmanagers/level3.py | 7 +++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index cbc268b6d283..31219e20cc19 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -26,7 +26,10 @@ from qiskit.transpiler.passes import NoiseAdaptiveLayout from qiskit.transpiler.passes import SabreLayout from qiskit.transpiler.preset_passmanagers import common -from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager +from qiskit.transpiler.preset_passmanagers.plugin import ( + PassManagerStagePluginManager, + list_stage_plugins, +) from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM @@ -92,7 +95,8 @@ def _choose_layout_condition(property_set): toqm_pass = False # Choose routing pass - if routing_method == "toqm": + # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interfac + if routing_method == "toqm" and "toqm" not in list_stage_plugins("routing"): HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO0, latencies_from_target diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 8ed09d2f3f87..fc1c0a5c820c 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -37,7 +37,10 @@ from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM -from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager +from qiskit.transpiler.preset_passmanagers.plugin import ( + PassManagerStagePluginManager, + list_stage_plugins, +) def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassManager: @@ -151,7 +154,7 @@ def _vf2_match_not_found(property_set): toqm_pass = False routing_pm = None # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface - if routing_method == "toqm": + if routing_method == "toqm" and "toqm" not in list_stage_plugins("routing"): HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO1, latencies_from_target diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index e45404f3c710..20082b4e85e2 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -37,7 +37,10 @@ from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM -from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager +from qiskit.transpiler.preset_passmanagers.plugin import ( + PassManagerStagePluginManager, + list_stage_plugins, +) def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassManager: @@ -134,7 +137,7 @@ def _vf2_match_not_found(property_set): toqm_pass = False routing_pm = None # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface - if routing_method == "toqm": + if routing_method == "toqm" and "toqm" not in list_stage_plugins("routing"): HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO2, latencies_from_target diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 55c892b2339d..c579cc5038b6 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -41,7 +41,10 @@ from qiskit.transpiler.passes import UnitarySynthesis from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason -from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePluginManager +from qiskit.transpiler.preset_passmanagers.plugin import ( + PassManagerStagePluginManager, + list_stage_plugins, +) from qiskit.transpiler import TranspilerError from qiskit.utils.optionals import HAS_TOQM @@ -139,7 +142,7 @@ def _vf2_match_not_found(property_set): toqm_pass = False # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface - if routing_method == "toqm": + if routing_method == "toqm" and "toqm" not in list_stage_plugins("routing"): HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO3, latencies_from_target From 7b67905beb07237fc39e459d6cd56218df3fa237 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 4 Aug 2022 09:40:03 -0400 Subject: [PATCH 25/31] Apply suggestions from code review Co-authored-by: Kevin Hartman Co-authored-by: Toshinari Itoko <15028342+itoko@users.noreply.github.com> --- qiskit/transpiler/passmanager_config.py | 8 ++++---- qiskit/transpiler/preset_passmanagers/level0.py | 2 +- qiskit/transpiler/preset_passmanagers/plugin.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index 18c85ff8a57e..ae73cd75e1eb 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -54,10 +54,10 @@ def __init__( map. layout_method (str): the pass to use for choosing initial qubit placement. This will be the plugin name if an external layout stage - plugin is being used + plugin is being used. routing_method (str): the pass to use for routing qubits on the architecture. This will be a plugin name if an external routing stage - plugin is being used + plugin is being used. translation_method (str): the pass to use for translating gates to basis_gates. This will be a plugin name if an external translation stage plugin is being used. @@ -79,8 +79,8 @@ def __init__( target (Target): The backend target init_method (str): The plugin name for the init stage plugin to use optimization_method (str): The plugin name for the optimization stage plugin - to use - optimization_level (int): The optimization level being used for compilation + to use. + optimization_level (int): The optimization level being used for compilation. """ self.initial_layout = initial_layout self.basis_gates = basis_gates diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 31219e20cc19..84dd2d810aaf 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -95,7 +95,7 @@ def _choose_layout_condition(property_set): toqm_pass = False # Choose routing pass - # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interfac + # TODO: Remove when qiskit-toqm has it's own plugin and we can rely on just the plugin interface if routing_method == "toqm" and "toqm" not in list_stage_plugins("routing"): HAS_TOQM.require_now("TOQM-based routing") from qiskit_toqm import ToqmSwap, ToqmStrategyO0, latencies_from_target diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index d335bf0b9043..777380bae8da 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -53,9 +53,9 @@ * - ``layout`` - ``qiskit.transpiler.layout`` - ``trivial``, ``dense``, ``noise_adaptive``, ``sabre`` - - The output from this staged is expected to have the ``layout`` property + - The output from this stage is expected to have the ``layout`` property set field set with a :class:`~.Layout` object. Additionally, the circuit is - typically expected to be embedded so that it expanded to include all + typically expected to be embedded so that it is expanded to include all qubits and the :class:`~.ApplyLayout` pass is expected to be run to apply the layout. The embedding of the :class:`~.Layout` can be generated with :func:`~.generate_embed_passmanager`. @@ -74,8 +74,8 @@ instruction on the target backend. * - ``optimization`` - ``qiskit.transpiler.optimization`` - - There are no reserve plugin names - - This stage is expected to perform and optimization and simplification. + - There are no reserved plugin names + - This stage is expected to perform optimization and simplification. The constraints from earlier stages still apply to the output of this stage. After the ``optimization`` stage is run we expect the circuit to still be executable on the target. From 23a2b36519977d5f0b7692cf074dbe694ba59567 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 17 Aug 2022 16:20:39 -0400 Subject: [PATCH 26/31] Fix lint --- qiskit/transpiler/preset_passmanagers/plugin.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 777380bae8da..9c290a0d7a58 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -46,9 +46,10 @@ * - ``init`` - ``qiskit.transpiler.init`` - No reserved names - - This stage runs first and is typically used for any initial logical optimization. Because most layout and routing algorithms are - only designed to work with 1 and 2 qubit gates, this stage is also used to translate any gates - that operate on more than 2 qubits into gates that only operate on 1 or 2 qubits. + - This stage runs first and is typically used for any initial logical optimization. Because most + layout and routing algorithms are only designed to work with 1 and 2 qubit gates, this stage + is also used to translate any gates that operate on more than 2 qubits into gates that only + operate on 1 or 2 qubits. ``init`` * - ``layout`` - ``qiskit.transpiler.layout`` From 6cb6b2011a6d72bd837bfb94f994e6f97a3ab8a7 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 17 Aug 2022 16:22:18 -0400 Subject: [PATCH 27/31] Remove unnecessary elses in builtin plugins --- .../preset_passmanagers/builtin_plugins.py | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 8aed46e940d0..c123ebab60c6 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -46,7 +46,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 1: + if opt_level == 1: return common.generate_routing_passmanager( routing_pass, target, @@ -57,7 +57,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - elif opt_level == 2: + if opt_level == 2: return common.generate_routing_passmanager( routing_pass, target, @@ -67,7 +67,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 3: + if opt_level == 3: return common.generate_routing_passmanager( routing_pass, target, @@ -77,8 +77,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - else: - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") class StochasticSwapPassManager(PassManagerStagePlugin): @@ -107,7 +106,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 1: + if opt_level == 1: return common.generate_routing_passmanager( routing_pass, target, @@ -118,7 +117,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - elif opt_level in {2, 3}: + if opt_level in {2, 3}: return common.generate_routing_passmanager( routing_pass, target, @@ -128,8 +127,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - else: - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") class LookaheadSwapPassManager(PassManagerStagePlugin): @@ -154,7 +152,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 1: + if opt_level == 1: routing_pass = LookaheadSwap(coupling_map, search_depth=4, search_width=4) return common.generate_routing_passmanager( routing_pass, @@ -166,7 +164,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - elif opt_level == 2: + if opt_level == 2: routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) return common.generate_routing_passmanager( routing_pass, @@ -177,7 +175,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 3: + if opt_level == 3: routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) return common.generate_routing_passmanager( routing_pass, @@ -188,8 +186,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - else: - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") class SabreSwapPassManager(PassManagerStagePlugin): @@ -214,7 +211,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 1: + if opt_level == 1: routing_pass = SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -226,7 +223,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - elif opt_level == 2: + if opt_level == 2: routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -237,7 +234,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - elif opt_level == 3: + if opt_level == 3: routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -248,8 +245,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - else: - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {opt_level}") class NoneRoutingPassManager(PassManagerStagePlugin): From d75659795cd546f5cc06e515cbe8b8a530cebfa2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 25 Aug 2022 11:13:44 -0400 Subject: [PATCH 28/31] Apply suggestions from code review Co-authored-by: Luciano Bello --- qiskit/transpiler/preset_passmanagers/plugin.py | 9 +++------ test/python/transpiler/test_stage_plugin.py | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 9c290a0d7a58..607ef0567187 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -50,7 +50,6 @@ layout and routing algorithms are only designed to work with 1 and 2 qubit gates, this stage is also used to translate any gates that operate on more than 2 qubits into gates that only operate on 1 or 2 qubits. - ``init`` * - ``layout`` - ``qiskit.transpiler.layout`` - ``trivial``, ``dense``, ``noise_adaptive``, ``sabre`` @@ -234,13 +233,11 @@ def _build_pm( plugin_name: str, pm_config: PassManagerConfig, ): - try: - plugin_obj = stage_obj[plugin_name] - except KeyError as err: + if plugin_name not in stage_obj: raise TranspilerError( f"Invalid plugin name {plugin_name} for stage {stage_name}" - ) from err - return plugin_obj.obj.pass_manager(pm_config) + ) + return plugin_obj.obj.pass_manager(stage_obj[plugin_name]) def list_stage_plugins(stage_name: str) -> List[str]: diff --git a/test/python/transpiler/test_stage_plugin.py b/test/python/transpiler/test_stage_plugin.py index 451239e357cf..56a33758b1fa 100644 --- a/test/python/transpiler/test_stage_plugin.py +++ b/test/python/transpiler/test_stage_plugin.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """ -Tests for the UnitarySynthesis transpiler pass. +Tests for the staged transpiler plugins. """ from qiskit.test import QiskitTestCase @@ -49,7 +49,7 @@ def test_build_pm_invalid_plugin_name_valid_stage(self): plugin_manager = PassManagerStagePluginManager() with self.assertRaises(TranspilerError): plugin_manager.get_passmanager_stage( - "init", "fake_plugin_not_real", PassManagerConfig() + "init", "empty_plugin", PassManagerConfig() ) def test_build_pm_invalid_stage(self): From d731d0601229db7772d933d496bed78ec5c2eb01 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 25 Aug 2022 13:12:09 -0400 Subject: [PATCH 29/31] Make optimization level a plugin argument --- .../preset_passmanagers/builtin_plugins.py | 71 ++++++++++--------- .../transpiler/preset_passmanagers/level0.py | 22 +++--- .../transpiler/preset_passmanagers/level1.py | 24 +++---- .../transpiler/preset_passmanagers/level2.py | 22 +++--- .../transpiler/preset_passmanagers/level3.py | 14 ++-- .../transpiler/preset_passmanagers/plugin.py | 60 ++++++++++++---- test/python/transpiler/test_stage_plugin.py | 10 +-- 7 files changed, 122 insertions(+), 101 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index c123ebab60c6..d1fc2b5e67c7 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -12,7 +12,6 @@ """Built-in transpiler stage plugins for preset pass managers.""" -from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes import BasicSwap @@ -27,18 +26,19 @@ class BasicSwapPassManager(PassManagerStagePlugin): """Plugin class for routing stage with :class:`~.BasicSwap`""" - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - opt_level = pass_manager_config.optimization_level seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties routing_pass = BasicSwap(coupling_map) vf2_call_limit = common.get_vf2_call_limit( - opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + optimization_level, + pass_manager_config.layout_method, + pass_manager_config.initial_layout, ) - if opt_level == 0: + if optimization_level == 0: return common.generate_routing_passmanager( routing_pass, target, @@ -46,7 +46,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 1: + if optimization_level == 1: return common.generate_routing_passmanager( routing_pass, target, @@ -57,7 +57,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - if opt_level == 2: + if optimization_level == 2: return common.generate_routing_passmanager( routing_pass, target, @@ -67,7 +67,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 3: + if optimization_level == 3: return common.generate_routing_passmanager( routing_pass, target, @@ -77,28 +77,29 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") class StochasticSwapPassManager(PassManagerStagePlugin): """Plugin class for routing stage with :class:`~.StochasticSwap`""" - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - opt_level = pass_manager_config.optimization_level seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties vf2_call_limit = common.get_vf2_call_limit( - opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + optimization_level, + pass_manager_config.layout_method, + pass_manager_config.initial_layout, ) - if opt_level == 3: + if optimization_level == 3: routing_pass = StochasticSwap(coupling_map, trials=200, seed=seed_transpiler) else: routing_pass = StochasticSwap(coupling_map, trials=20, seed=seed_transpiler) - if opt_level == 0: + if optimization_level == 0: return common.generate_routing_passmanager( routing_pass, target, @@ -106,7 +107,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 1: + if optimization_level == 1: return common.generate_routing_passmanager( routing_pass, target, @@ -117,7 +118,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - if opt_level in {2, 3}: + if optimization_level in {2, 3}: return common.generate_routing_passmanager( routing_pass, target, @@ -127,23 +128,24 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") class LookaheadSwapPassManager(PassManagerStagePlugin): """Plugin class for routing stage with :class:`~.LookaheadSwap`""" - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - opt_level = pass_manager_config.optimization_level seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties vf2_call_limit = common.get_vf2_call_limit( - opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + optimization_level, + pass_manager_config.layout_method, + pass_manager_config.initial_layout, ) - if opt_level == 0: + if optimization_level == 0: routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2) return common.generate_routing_passmanager( routing_pass, @@ -152,7 +154,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 1: + if optimization_level == 1: routing_pass = LookaheadSwap(coupling_map, search_depth=4, search_width=4) return common.generate_routing_passmanager( routing_pass, @@ -164,7 +166,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - if opt_level == 2: + if optimization_level == 2: routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) return common.generate_routing_passmanager( routing_pass, @@ -175,7 +177,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 3: + if optimization_level == 3: routing_pass = LookaheadSwap(coupling_map, search_depth=5, search_width=6) return common.generate_routing_passmanager( routing_pass, @@ -186,23 +188,24 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") class SabreSwapPassManager(PassManagerStagePlugin): """Plugin class for routing stage with :class:`~.SabreSwap`""" - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - opt_level = pass_manager_config.optimization_level seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties vf2_call_limit = common.get_vf2_call_limit( - opt_level, pass_manager_config.layout_method, pass_manager_config.initial_layout + optimization_level, + pass_manager_config.layout_method, + pass_manager_config.initial_layout, ) - if opt_level == 0: + if optimization_level == 0: routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -211,7 +214,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 1: + if optimization_level == 1: routing_pass = SabreSwap(coupling_map, heuristic="lookahead", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -223,7 +226,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: check_trivial=True, use_barrier_before_measurement=True, ) - if opt_level == 2: + if optimization_level == 2: routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -234,7 +237,7 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - if opt_level == 3: + if optimization_level == 3: routing_pass = SabreSwap(coupling_map, heuristic="decay", seed=seed_transpiler) return common.generate_routing_passmanager( routing_pass, @@ -245,13 +248,13 @@ def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: seed_transpiler=seed_transpiler, use_barrier_before_measurement=True, ) - raise TranspilerError(f"Invalid optimization level specified: {opt_level}") + raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") class NoneRoutingPassManager(PassManagerStagePlugin): """Plugin class for routing stage with error on routing.""" - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 84dd2d810aaf..bd52ae54d429 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -72,11 +72,6 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa unitary_synthesis_method = pass_manager_config.unitary_synthesis_method unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target - # Override an unset optimization_level for stage plugin use. - # it will be restored to None before this is returned - optimization_level = pass_manager_config.optimization_level - if optimization_level is None: - pass_manager_config.optimization_level = 0 # Choose an initial layout if not set by user (default: trivial layout) _given_layout = SetLayout(initial_layout) @@ -123,7 +118,7 @@ def _choose_layout_condition(property_set): ) else: routing_pm = plugin_manager.get_passmanager_stage( - "routing", routing_method, pass_manager_config + "routing", routing_method, pass_manager_config, optimization_level=0 ) unroll_3q = None @@ -138,7 +133,7 @@ def _choose_layout_condition(property_set): ) if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config + "layout", layout_method, pass_manager_config, optimization_level=0 ) else: layout = PassManager() @@ -151,7 +146,7 @@ def _choose_layout_condition(property_set): routing = None if translation_method not in {"translator", "synthesis", "unroller"}: translation = plugin_manager.get_passmanager_stage( - "translation", translation_method, pass_manager_config + "translation", translation_method, pass_manager_config, optimization_level=0 ) else: translation = common.generate_translation_passmanager( @@ -181,21 +176,20 @@ def _choose_layout_condition(property_set): ) else: sched = plugin_manager.get_passmanager_stage( - "scheduling", scheduling_method, pass_manager_config + "scheduling", scheduling_method, pass_manager_config, optimization_level=0 ) if init_method is not None: - init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=0 + ) else: init = unroll_3q optimization = None if optimization_method is not None: optimization = plugin_manager.get_passmanager_stage( - "optimization", optimization_method, pass_manager_config + "optimization", optimization_method, pass_manager_config, optimization_level=0 ) - # Restore PassManagerConfig optimization_level override - pass_manager_config.optimization_level = optimization_level - return StagedPassManager( init=init, layout=layout, diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index fc1c0a5c820c..cd3151b2a229 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -83,11 +83,6 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() target = pass_manager_config.target - # Override an unset optimization_level for stage plugin use. - # it will be restored to None before this is returned - optimization_level = pass_manager_config.optimization_level - if optimization_level is None: - pass_manager_config.optimization_level = 1 # Use trivial layout if no layout given _given_layout = SetLayout(initial_layout) @@ -187,7 +182,10 @@ def _vf2_match_not_found(property_set): ) else: routing_pm = plugin_manager.get_passmanager_stage( - "routing", routing_method, pass_manager_config + "routing", + routing_method, + pass_manager_config, + optimization_level=1, ) # Build optimization loop: merge 1q rotations and cancel CNOT gates iteratively @@ -212,7 +210,7 @@ def _opt_control(property_set): ) if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config + "layout", layout_method, pass_manager_config, optimization_level=1 ) else: layout = PassManager() @@ -229,7 +227,7 @@ def _opt_control(property_set): routing = None if translation_method not in {"translator", "synthesis", "unroller"}: translation = plugin_manager.get_passmanager_stage( - "translation", translation_method, pass_manager_config + "translation", translation_method, pass_manager_config, optimization_level=1 ) else: translation = common.generate_translation_passmanager( @@ -262,7 +260,7 @@ def _opt_control(property_set): optimization.append(opt_loop, do_while=_opt_control) else: optimization = plugin_manager.get_passmanager_stage( - "optimization", optimization_method, pass_manager_config + "optimization", optimization_method, pass_manager_config, optimization_level=1 ) if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( @@ -270,14 +268,14 @@ def _opt_control(property_set): ) else: sched = plugin_manager.get_passmanager_stage( - "scheduling", scheduling_method, pass_manager_config + "scheduling", scheduling_method, pass_manager_config, optimization_level=1 ) if init_method is not None: - init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=1 + ) else: init = unroll_3q - # Restore PassManagerConfig optimization_level override - pass_manager_config.optimization_level = optimization_level return StagedPassManager( init=init, diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 20082b4e85e2..dad5b75cde5b 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -85,11 +85,6 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa timing_constraints = pass_manager_config.timing_constraints or TimingConstraints() unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config target = pass_manager_config.target - # Override an unset optimization_level for stage plugin use. - # it will be restored to None before this is returned - optimization_level = pass_manager_config.optimization_level - if optimization_level is None: - pass_manager_config.optimization_level = 2 # Search for a perfect layout, or choose a dense layout, if no layout given _given_layout = SetLayout(initial_layout) @@ -169,7 +164,7 @@ def _vf2_match_not_found(property_set): ) else: routing_pm = plugin_manager.get_passmanager_stage( - "routing", routing_method, pass_manager_config + "routing", routing_method, pass_manager_config, optimization_level=2 ) # Build optimization loop: 1q rotation merge and commutative cancellation iteratively until @@ -197,7 +192,7 @@ def _opt_control(property_set): ) if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config + "layout", layout_method, pass_manager_config, optimization_level=2 ) else: layout = PassManager() @@ -211,7 +206,7 @@ def _opt_control(property_set): routing = None if translation_method not in {"translator", "synthesis", "unroller"}: translation = plugin_manager.get_passmanager_stage( - "translation", translation_method, pass_manager_config + "translation", translation_method, pass_manager_config, optimization_level=2 ) else: translation = common.generate_translation_passmanager( @@ -241,7 +236,7 @@ def _opt_control(property_set): optimization.append(opt_loop, do_while=_opt_control) else: optimization = plugin_manager.get_passmanager_stage( - "optimization", optimization_method, pass_manager_config + "optimization", optimization_method, pass_manager_config, optimization_level=2 ) if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( @@ -249,16 +244,15 @@ def _opt_control(property_set): ) else: sched = plugin_manager.get_passmanager_stage( - "scheduling", scheduling_method, pass_manager_config + "scheduling", scheduling_method, pass_manager_config, optimization_level=2 ) if init_method is not None: - init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=2 + ) else: init = unroll_3q - # Restore PassManagerConfig optimization_level override - pass_manager_config.optimization_level = optimization_level - return StagedPassManager( init=init, layout=layout, diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index c579cc5038b6..2bc560282f4f 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -174,7 +174,7 @@ def _vf2_match_not_found(property_set): ) else: routing_pm = plugin_manager.get_passmanager_stage( - "routing", routing_method, pass_manager_config + "routing", routing_method, pass_manager_config, optimization_level=3 ) # 8. Optimize iteratively until no more change in depth. Removes useless gates @@ -203,7 +203,9 @@ def _opt_control(property_set): # Build pass manager if init_method is not None: - init = plugin_manager.get_passmanager_stage("init", init_method, pass_manager_config) + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=3 + ) else: init = common.generate_unroll_3q( target, @@ -218,7 +220,7 @@ def _opt_control(property_set): if coupling_map or initial_layout: if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config + "layout", layout_method, pass_manager_config, optimization_level=3 ) else: layout = PassManager() @@ -232,7 +234,7 @@ def _opt_control(property_set): routing = None if translation_method not in {"translator", "synthesis", "unroller"}: translation = plugin_manager.get_passmanager_stage( - "translation", translation_method, pass_manager_config + "translation", translation_method, pass_manager_config, optimization_level=3 ) else: translation = common.generate_translation_passmanager( @@ -280,7 +282,7 @@ def _opt_control(property_set): optimization.append(opt_loop, do_while=_opt_control) else: optimization = plugin_manager.get_passmanager_stage( - "optimization", optimization_method, pass_manager_config + "optimization", optimization_method, pass_manager_config, optimization_level=3 ) if (coupling_map and not coupling_map.is_symmetric) or ( target is not None and target.get_non_global_operation_names(strict_direction=True) @@ -295,7 +297,7 @@ def _opt_control(property_set): ) else: sched = plugin_manager.get_passmanager_stage( - "scheduling", scheduling_method, pass_manager_config + "scheduling", scheduling_method, pass_manager_config, optimization_level=3 ) # Restore PassManagerConfig optimization_level override diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 607ef0567187..a442bbb49383 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -160,7 +160,7 @@ def pass_manager(self, pass_manager_config): """ import abc -from typing import List +from typing import List, Optional import stevedore @@ -174,12 +174,26 @@ class PassManagerStagePlugin(abc.ABC): stages in :func:`~.transpile`. A ``PassManagerStagePlugin`` object can be added to an external package and - + integrated into the :func:`~.transpile` function with an entrypoint. This + will enable users to use the output of :meth:`.pass_manager` to implement + a stage in the compilation process. """ @abc.abstractmethod - def pass_manager(self, pass_manager_config: PassManagerConfig) -> PassManager: - """This method is designed to return a :class:`~.PassManager` for the""" + def pass_manager( + self, pass_manager_config: PassManagerConfig, optimization_level: Optional[int] = None + ) -> PassManager: + """This method is designed to return a :class:`~.PassManager` for the stage this implements + + Args: + pass_manager_config: A configuration object that defines all the target device + specifications and any user specified options to :func:`~.transpile` or + :func:`~.generate_preset_pass_manager` + optimization_level: The optimization level of the transpilation, if set this + should be used to set values for any tunable parameters to trade off runtime + for potential optimization. Valid values should be ``0``, ``1``, ``2``, or ``3`` + and the higher the number the more optimization is expected. + """ pass @@ -208,21 +222,37 @@ def __init__(self): ) def get_passmanager_stage( - self, stage_name: str, plugin_name: str, pm_config: PassManagerConfig + self, + stage_name: str, + plugin_name: str, + pm_config: PassManagerConfig, + optimization_level=None, ) -> PassManager: """Get a stage""" if stage_name == "init": - return self._build_pm(self.init_plugins, stage_name, plugin_name, pm_config) + return self._build_pm( + self.init_plugins, stage_name, plugin_name, pm_config, optimization_level + ) elif stage_name == "layout": - return self._build_pm(self.layout_plugins, stage_name, plugin_name, pm_config) + return self._build_pm( + self.layout_plugins, stage_name, plugin_name, pm_config, optimization_level + ) elif stage_name == "routing": - return self._build_pm(self.routing_plugins, stage_name, plugin_name, pm_config) + return self._build_pm( + self.routing_plugins, stage_name, plugin_name, pm_config, optimization_level + ) elif stage_name == "translation": - return self._build_pm(self.translation_plugins, stage_name, plugin_name, pm_config) + return self._build_pm( + self.translation_plugins, stage_name, plugin_name, pm_config, optimization_level + ) elif stage_name == "optimization": - return self._build_pm(self.optimization_plugins, stage_name, plugin_name, pm_config) + return self._build_pm( + self.optimization_plugins, stage_name, plugin_name, pm_config, optimization_level + ) elif stage_name == "scheduling": - return self._build_pm(self.scheduling_plugins, stage_name, plugin_name, pm_config) + return self._build_pm( + self.scheduling_plugins, stage_name, plugin_name, pm_config, optimization_level + ) else: raise TranspilerError(f"Invalid stage name: {stage_name}") @@ -232,12 +262,12 @@ def _build_pm( stage_name: str, plugin_name: str, pm_config: PassManagerConfig, + optimization_level: Optional[int] = None, ): if plugin_name not in stage_obj: - raise TranspilerError( - f"Invalid plugin name {plugin_name} for stage {stage_name}" - ) - return plugin_obj.obj.pass_manager(stage_obj[plugin_name]) + raise TranspilerError(f"Invalid plugin name {plugin_name} for stage {stage_name}") + plugin_obj = stage_obj[plugin_name] + return plugin_obj.obj.pass_manager(pm_config, optimization_level) def list_stage_plugins(stage_name: str) -> List[str]: diff --git a/test/python/transpiler/test_stage_plugin.py b/test/python/transpiler/test_stage_plugin.py index 56a33758b1fa..807d6b2d5da0 100644 --- a/test/python/transpiler/test_stage_plugin.py +++ b/test/python/transpiler/test_stage_plugin.py @@ -48,9 +48,7 @@ def test_build_pm_invalid_plugin_name_valid_stage(self): """Test get pm from plugin with invalid plugin name and valid stage.""" plugin_manager = PassManagerStagePluginManager() with self.assertRaises(TranspilerError): - plugin_manager.get_passmanager_stage( - "init", "empty_plugin", PassManagerConfig() - ) + plugin_manager.get_passmanager_stage("init", "empty_plugin", PassManagerConfig()) def test_build_pm_invalid_stage(self): """Test get pm from plugin with invalid stage.""" @@ -63,6 +61,8 @@ def test_build_pm_invalid_stage(self): def test_build_pm(self): """Test get pm from plugin.""" plugin_manager = PassManagerStagePluginManager() - pm_config = PassManagerConfig(optimization_level=3) - pm = plugin_manager.get_passmanager_stage("routing", "sabre", pm_config) + pm_config = PassManagerConfig() + pm = plugin_manager.get_passmanager_stage( + "routing", "sabre", pm_config, optimization_level=3 + ) self.assertIsInstance(pm, PassManager) From f185c598ad6b521237336181eb4d52927f9314ef Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 25 Aug 2022 13:29:32 -0400 Subject: [PATCH 30/31] Add test coverage for all built-in routing plugins --- test/python/transpiler/test_stage_plugin.py | 37 ++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/python/transpiler/test_stage_plugin.py b/test/python/transpiler/test_stage_plugin.py index 807d6b2d5da0..1242905e9f4e 100644 --- a/test/python/transpiler/test_stage_plugin.py +++ b/test/python/transpiler/test_stage_plugin.py @@ -14,13 +14,20 @@ Tests for the staged transpiler plugins. """ +from test import combine + +import ddt + +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.compiler.transpiler import transpile from qiskit.test import QiskitTestCase -from qiskit.transpiler import PassManager, PassManagerConfig +from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePluginManager, list_stage_plugins, ) from qiskit.transpiler.exceptions import TranspilerError +from qiskit.providers.basicaer import QasmSimulatorPy class TestStagePassManagerPlugin(QiskitTestCase): @@ -66,3 +73,31 @@ def test_build_pm(self): "routing", "sabre", pm_config, optimization_level=3 ) self.assertIsInstance(pm, PassManager) + + +@ddt.ddt +class TestBuiltinPlugins(QiskitTestCase): + """Test that all built-in plugins work in transpile().""" + + @combine( + optimization_level=list(range(4)), + routing_method=["basic", "lookahead", "sabre", "stochastic"], + ) + def test_routing_plugins(self, optimization_level, routing_method): + """Test all routing plugins (excluding error).""" + qc = QuantumCircuit(4) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.cx(0, 3) + qc.measure_all() + tqc = transpile( + qc, + basis_gates=["cx", "sx", "x", "rz"], + coupling_map=CouplingMap.from_line(4), + optimization_level=optimization_level, + routing_method=routing_method, + ) + backend = QasmSimulatorPy() + counts = backend.run(tqc, shots=1000).result().get_counts() + self.assertDictAlmostEqual(counts, {"0000": 500, "1111": 500}, delta=100) From 6d344c068c7e3605d735f3efa654b9f1e406a0cb Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 25 Aug 2022 13:49:28 -0400 Subject: [PATCH 31/31] Reorder stage variables to execution order --- qiskit/transpiler/passmanager_config.py | 4 ++-- qiskit/transpiler/preset_passmanagers/level0.py | 4 ++-- qiskit/transpiler/preset_passmanagers/level1.py | 4 ++-- qiskit/transpiler/preset_passmanagers/level2.py | 4 ++-- qiskit/transpiler/preset_passmanagers/level3.py | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qiskit/transpiler/passmanager_config.py b/qiskit/transpiler/passmanager_config.py index ae73cd75e1eb..fc412dc4f286 100644 --- a/qiskit/transpiler/passmanager_config.py +++ b/qiskit/transpiler/passmanager_config.py @@ -86,9 +86,11 @@ def __init__( self.basis_gates = basis_gates self.inst_map = inst_map self.coupling_map = coupling_map + self.init_method = init_method self.layout_method = layout_method self.routing_method = routing_method self.translation_method = translation_method + self.optimization_method = optimization_method self.scheduling_method = scheduling_method self.instruction_durations = instruction_durations self.backend_properties = backend_properties @@ -98,8 +100,6 @@ def __init__( self.unitary_synthesis_method = unitary_synthesis_method self.unitary_synthesis_plugin_config = unitary_synthesis_plugin_config self.target = target - self.init_method = init_method - self.optimization_method = optimization_method self.optimization_level = optimization_level @classmethod diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index bd52ae54d429..c02c517a23e1 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -58,12 +58,12 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout + init_method = pass_manager_config.init_method layout_method = pass_manager_config.layout_method or "trivial" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" - scheduling_method = pass_manager_config.scheduling_method - init_method = pass_manager_config.init_method optimization_method = pass_manager_config.optimization_method + scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index cd3151b2a229..05e7d56a736e 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -70,11 +70,11 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout layout_method = pass_manager_config.layout_method or "dense" + init_method = pass_manager_config.init_method routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" - scheduling_method = pass_manager_config.scheduling_method - init_method = pass_manager_config.init_method optimization_method = pass_manager_config.optimization_method + scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index dad5b75cde5b..5a8dcde691e8 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -71,12 +71,12 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout + init_method = pass_manager_config.init_method layout_method = pass_manager_config.layout_method or "dense" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" - scheduling_method = pass_manager_config.scheduling_method - init_method = pass_manager_config.init_method optimization_method = pass_manager_config.optimization_method + scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 2bc560282f4f..0d664f2172a0 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -77,12 +77,12 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout + init_method = pass_manager_config.init_method layout_method = pass_manager_config.layout_method or "sabre" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" - scheduling_method = pass_manager_config.scheduling_method - init_method = pass_manager_config.init_method optimization_method = pass_manager_config.optimization_method + scheduling_method = pass_manager_config.scheduling_method instruction_durations = pass_manager_config.instruction_durations seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties