From b0396870e2b2f064d97da369d954599ec4e24d73 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 22 Aug 2023 07:25:08 -0400 Subject: [PATCH] Migrate layout stage to plugins (#10622) * Migrate layout stage to plugins This commit updates the preset pass manager construction to only use plugins for the layout stage. To accomplish this the previously hard coded built-in layout methods, trivial, dense, noise adpative, and sabre are migrated to be exposed as built-in plugins. Additionally, the special case of layout_method=None has been centralized into a standard default method plugin, as the pass construction in this case involved extra steps for each optimization level. This simplifies the preset pass manager construction as now the layout stage is solely built via plugins. Fixes #9455 * Remove unnecessary if statement * Handle invalid optimization levels in plugins * Remove unused variable post-rebase --- .../preset_passmanagers/builtin_plugins.py | 278 ++++++++++++++++++ .../transpiler/preset_passmanagers/level0.py | 62 +--- .../transpiler/preset_passmanagers/level1.py | 109 +------ .../transpiler/preset_passmanagers/level2.py | 96 +----- .../transpiler/preset_passmanagers/level3.py | 95 +----- .../transpiler/preset_passmanagers/plugin.py | 4 +- ...reserved-plugin-name-3825c2000a579e38.yaml | 9 + setup.py | 7 + .../transpiler/test_preset_passmanagers.py | 2 + 9 files changed, 314 insertions(+), 348 deletions(-) create mode 100644 releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 7da641c2d5fb..81475813e29c 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -19,9 +19,18 @@ from qiskit.transpiler.passes import StochasticSwap from qiskit.transpiler.passes import SabreSwap from qiskit.transpiler.passes import Error +from qiskit.transpiler.passes import SetLayout +from qiskit.transpiler.passes import VF2Layout +from qiskit.transpiler.passes import SabreLayout +from qiskit.transpiler.passes import DenseLayout +from qiskit.transpiler.passes import TrivialLayout +from qiskit.transpiler.passes import NoiseAdaptiveLayout +from qiskit.transpiler.passes import CheckMap +from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin from qiskit.transpiler.timing_constraints import TimingConstraints +from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason class BasisTranslatorPassManager(PassManagerStagePlugin): @@ -418,3 +427,272 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana return common.generate_scheduling( instruction_durations, scheduling_method, timing_constraints, inst_map, target ) + + +class DefaultLayoutPassManager(PassManagerStagePlugin): + """Plugin class for default layout stage.""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + _given_layout = SetLayout(pass_manager_config.initial_layout) + + def _choose_layout_condition(property_set): + return not property_set["layout"] + + def _layout_not_perfect(property_set): + """Return ``True`` if the first attempt at layout has been checked and found to be + imperfect. In this case, perfection means "does not require any swap routing".""" + return property_set["is_swap_mapped"] is not None and not property_set["is_swap_mapped"] + + def _vf2_match_not_found(property_set): + # If a layout hasn't been set by the time we run vf2 layout we need to + # run layout + if property_set["layout"] is None: + return True + # if VF2 layout stopped for any reason other than solution found we need + # to run layout since VF2 didn't converge. + return ( + property_set["VF2Layout_stop_reason"] is not None + and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND + ) + + def _swap_mapped(property_set): + return property_set["final_layout"] is None + + if pass_manager_config.target is None: + coupling_map = pass_manager_config.coupling_map + else: + coupling_map = pass_manager_config.target + + layout = PassManager() + layout.append(_given_layout) + if optimization_level == 0: + layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout += common.generate_embed_passmanager(coupling_map) + return layout + elif optimization_level == 1: + layout.append( + [TrivialLayout(coupling_map), CheckMap(coupling_map)], + condition=_choose_layout_condition, + ) + choose_layout_1 = VF2Layout( + coupling_map=pass_manager_config.coupling_map, + seed=pass_manager_config.seed_transpiler, + call_limit=int(5e4), # Set call limit to ~100ms with rustworkx 0.10.2 + properties=pass_manager_config.backend_properties, + target=pass_manager_config.target, + max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices + ) + layout.append(choose_layout_1, condition=_layout_not_perfect) + choose_layout_2 = SabreLayout( + coupling_map, + max_iterations=2, + seed=pass_manager_config.seed_transpiler, + swap_trials=5, + layout_trials=5, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + layout.append( + [BarrierBeforeFinalMeasurements(), choose_layout_2], condition=_vf2_match_not_found + ) + elif optimization_level == 2: + choose_layout_0 = VF2Layout( + coupling_map=pass_manager_config.coupling_map, + seed=pass_manager_config.seed_transpiler, + call_limit=int(5e6), # Set call limit to ~10s with rustworkx 0.10.2 + properties=pass_manager_config.backend_properties, + target=pass_manager_config.target, + max_trials=25000, # Limits layout scoring to < 10s on ~400 qubit devices + ) + layout.append(choose_layout_0, condition=_choose_layout_condition) + choose_layout_1 = SabreLayout( + coupling_map, + max_iterations=2, + seed=pass_manager_config.seed_transpiler, + swap_trials=10, + layout_trials=10, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + layout.append( + [BarrierBeforeFinalMeasurements(), choose_layout_1], condition=_vf2_match_not_found + ) + elif optimization_level == 3: + choose_layout_0 = VF2Layout( + coupling_map=pass_manager_config.coupling_map, + seed=pass_manager_config.seed_transpiler, + call_limit=int(3e7), # Set call limit to ~60s with rustworkx 0.10.2 + properties=pass_manager_config.backend_properties, + target=pass_manager_config.target, + max_trials=250000, # Limits layout scoring to < 60s on ~400 qubit devices + ) + layout.append(choose_layout_0, condition=_choose_layout_condition) + choose_layout_1 = SabreLayout( + coupling_map, + max_iterations=4, + seed=pass_manager_config.seed_transpiler, + swap_trials=20, + layout_trials=20, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + layout.append( + [BarrierBeforeFinalMeasurements(), choose_layout_1], condition=_vf2_match_not_found + ) + else: + raise TranspilerError(f"Invalid optimization level: {optimization_level}") + + embed = common.generate_embed_passmanager(coupling_map) + layout.append( + [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped + ) + return layout + + +class TrivialLayoutPassManager(PassManagerStagePlugin): + """Plugin class for trivial layout stage.""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + _given_layout = SetLayout(pass_manager_config.initial_layout) + + def _choose_layout_condition(property_set): + return not property_set["layout"] + + if pass_manager_config.target is None: + coupling_map = pass_manager_config.coupling_map + else: + coupling_map = pass_manager_config.target + + layout = PassManager() + layout.append(_given_layout) + layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout += common.generate_embed_passmanager(coupling_map) + return layout + + +class DenseLayoutPassManager(PassManagerStagePlugin): + """Plugin class for dense layout stage.""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + _given_layout = SetLayout(pass_manager_config.initial_layout) + + def _choose_layout_condition(property_set): + return not property_set["layout"] + + if pass_manager_config.target is None: + coupling_map = pass_manager_config.coupling_map + else: + coupling_map = pass_manager_config.target + + layout = PassManager() + layout.append(_given_layout) + layout.append( + DenseLayout( + coupling_map=pass_manager_config.coupling_map, + backend_prop=pass_manager_config.backend_properties, + target=pass_manager_config.target, + ), + condition=_choose_layout_condition, + ) + layout += common.generate_embed_passmanager(coupling_map) + return layout + + +class NoiseAdaptiveLayoutPassManager(PassManagerStagePlugin): + """Plugin class for noise adaptive layout stage.""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + _given_layout = SetLayout(pass_manager_config.initial_layout) + + def _choose_layout_condition(property_set): + return not property_set["layout"] + + if pass_manager_config.target is None: + coupling_map = pass_manager_config.coupling_map + else: + coupling_map = pass_manager_config.target + + layout = PassManager() + layout.append(_given_layout) + if pass_manager_config.target is None: + layout.append( + NoiseAdaptiveLayout(pass_manager_config.backend_properties), + condition=_choose_layout_condition, + ) + else: + layout.append( + NoiseAdaptiveLayout(pass_manager_config.target), condition=_choose_layout_condition + ) + layout += common.generate_embed_passmanager(coupling_map) + return layout + + +class SabreLayoutPassManager(PassManagerStagePlugin): + """Plugin class for sabre layout stage.""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + _given_layout = SetLayout(pass_manager_config.initial_layout) + + def _choose_layout_condition(property_set): + return not property_set["layout"] + + def _swap_mapped(property_set): + return property_set["final_layout"] is None + + if pass_manager_config.target is None: + coupling_map = pass_manager_config.coupling_map + else: + coupling_map = pass_manager_config.target + + layout = PassManager() + layout.append(_given_layout) + if optimization_level == 0: + layout_pass = SabreLayout( + coupling_map, + max_iterations=1, + seed=pass_manager_config.seed_transpiler, + swap_trials=5, + layout_trials=5, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + elif optimization_level == 1: + layout_pass = SabreLayout( + coupling_map, + max_iterations=2, + seed=pass_manager_config.seed_transpiler, + swap_trials=5, + layout_trials=5, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + elif optimization_level == 2: + layout_pass = SabreLayout( + coupling_map, + max_iterations=2, + seed=pass_manager_config.seed_transpiler, + swap_trials=10, + layout_trials=10, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + elif optimization_level == 3: + layout_pass = SabreLayout( + coupling_map, + max_iterations=4, + seed=pass_manager_config.seed_transpiler, + swap_trials=20, + layout_trials=20, + skip_routing=pass_manager_config.routing_method is not None + and pass_manager_config.routing_method != "sabre", + ) + else: + raise TranspilerError(f"Invalid optimization level: {optimization_level}") + layout.append( + [BarrierBeforeFinalMeasurements(), layout_pass], condition=_choose_layout_condition + ) + embed = common.generate_embed_passmanager(coupling_map) + layout.append( + [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped + ) + return layout diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index caa5cbc2846c..ca1a40e3d823 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -14,17 +14,9 @@ Level 0 pass manager: no explicit optimization other than mapping to backend. """ -from qiskit.transpiler.basepasses import BasePass from qiskit.transpiler.passmanager_config import PassManagerConfig -from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passmanager import StagedPassManager - -from qiskit.transpiler.passes import SetLayout -from qiskit.transpiler.passes import TrivialLayout -from qiskit.transpiler.passes import DenseLayout -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, @@ -55,50 +47,17 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa 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" + layout_method = pass_manager_config.layout_method or "default" routing_method = pass_manager_config.routing_method or "stochastic" translation_method = pass_manager_config.translation_method or "translator" optimization_method = pass_manager_config.optimization_method scheduling_method = pass_manager_config.scheduling_method or "default" - seed_transpiler = pass_manager_config.seed_transpiler - backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree 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 hls_config = pass_manager_config.hls_config - # Choose an initial layout if not set by user (default: trivial layout) - _given_layout = SetLayout(initial_layout) - - def _choose_layout_condition(property_set): - return not property_set["layout"] - - if target is None: - coupling_map_layout = coupling_map - else: - coupling_map_layout = target - - if layout_method == "trivial": - _choose_layout: BasePass = TrivialLayout(coupling_map_layout) - elif layout_method == "dense": - _choose_layout = DenseLayout(coupling_map, backend_properties, target=target) - elif layout_method == "noise_adaptive": - if target is None: - _choose_layout = NoiseAdaptiveLayout(backend_properties) - else: - _choose_layout = NoiseAdaptiveLayout(target) - elif layout_method == "sabre": - skip_routing = pass_manager_config.routing_method is not None and routing_method != "sabre" - _choose_layout = SabreLayout( - coupling_map_layout, - max_iterations=1, - seed=seed_transpiler, - swap_trials=5, - layout_trials=5, - skip_routing=skip_routing, - ) - # Choose routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=0 @@ -115,22 +74,9 @@ def _choose_layout_condition(property_set): unitary_synthesis_plugin_config, hls_config, ) - if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: - layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config, optimization_level=0 - ) - else: - - def _swap_mapped(property_set): - return property_set["final_layout"] is None - - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout, condition=_choose_layout_condition) - embed = common.generate_embed_passmanager(coupling_map_layout) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config, optimization_level=0 + ) routing = routing_pm else: layout = None diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 97cd5b319841..f364c2577edd 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -20,24 +20,13 @@ from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passmanager import StagedPassManager from qiskit.transpiler import ConditionalController, FlowController - from qiskit.transpiler.passes import CXCancellation -from qiskit.transpiler.passes import SetLayout -from qiskit.transpiler.passes import VF2Layout -from qiskit.transpiler.passes import TrivialLayout -from qiskit.transpiler.passes import DenseLayout -from qiskit.transpiler.passes import NoiseAdaptiveLayout -from qiskit.transpiler.passes import SabreLayout 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 CheckMap from qiskit.transpiler.passes import GatesInBasis -from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements from qiskit.transpiler.preset_passmanagers import common -from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason - from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePluginManager, ) @@ -71,89 +60,17 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa init_method = pass_manager_config.init_method # Unlike other presets, the layout and routing defaults aren't set here because they change # based on whether the input circuit has control flow. - layout_method = pass_manager_config.layout_method or "sabre" + layout_method = pass_manager_config.layout_method or "default" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" optimization_method = pass_manager_config.optimization_method scheduling_method = pass_manager_config.scheduling_method or "default" - seed_transpiler = pass_manager_config.seed_transpiler - backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree 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 hls_config = pass_manager_config.hls_config - # Use trivial layout if no layout given - _given_layout = SetLayout(initial_layout) - - def _choose_layout_condition(property_set): - return not property_set["layout"] - - def _layout_not_perfect(property_set): - """Return ``True`` if the first attempt at layout has been checked and found to be - imperfect. In this case, perfection means "does not require any swap routing".""" - return property_set["is_swap_mapped"] is not None and not property_set["is_swap_mapped"] - - # Use a better layout on densely connected qubits, if circuit needs swaps - def _vf2_match_not_found(property_set): - # If a layout hasn't been set by the time we run vf2 layout we need to - # run layout - if property_set["layout"] is None: - return True - # if VF2 layout stopped for any reason other than solution found we need - # to run layout since VF2 didn't converge. - if ( - property_set["VF2Layout_stop_reason"] is not None - and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND - ): - return True - return False - - if target is None: - coupling_map_layout = coupling_map - else: - coupling_map_layout = target - - _choose_layout_0: list[BasePass] = ( - [] - if pass_manager_config.layout_method - else [TrivialLayout(coupling_map_layout), CheckMap(coupling_map_layout)] - ) - - _choose_layout_1: list[BasePass] | BasePass = ( - [] - if pass_manager_config.layout_method - else VF2Layout( - coupling_map, - seed=seed_transpiler, - call_limit=int(5e4), # Set call limit to ~100ms with rustworkx 0.10.2 - properties=backend_properties, - target=target, - max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices - ) - ) - - if layout_method == "trivial": - _improve_layout: BasePass = TrivialLayout(coupling_map_layout) - elif layout_method == "dense": - _improve_layout = DenseLayout(coupling_map, backend_properties, target=target) - elif layout_method == "noise_adaptive": - if target is None: - _improve_layout = NoiseAdaptiveLayout(backend_properties) - else: - _improve_layout = NoiseAdaptiveLayout(target) - elif layout_method == "sabre": - _improve_layout = SabreLayout( - coupling_map_layout, - max_iterations=2, - seed=seed_transpiler, - swap_trials=5, - layout_trials=5, - skip_routing=pass_manager_config.routing_method is not None - and routing_method != "sabre", - ) - # Choose routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=1 @@ -180,27 +97,9 @@ def _opt_control(property_set): unitary_synthesis_plugin_config, hls_config, ) - if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre", None}: - layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config, optimization_level=1 - ) - else: - - def _swap_mapped(property_set): - return property_set["final_layout"] is None - - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout_0, condition=_choose_layout_condition) - layout.append(_choose_layout_1, condition=_layout_not_perfect) - layout.append( - [BarrierBeforeFinalMeasurements(), _improve_layout], condition=_vf2_match_not_found - ) - embed = common.generate_embed_passmanager(coupling_map_layout) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) - + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config, optimization_level=1 + ) routing = routing_pm else: diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index cf4f66e1e67b..42ccd9e0c4f7 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -21,23 +21,13 @@ from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passmanager import StagedPassManager from qiskit.transpiler import ConditionalController, FlowController - -from qiskit.transpiler.passes import SetLayout -from qiskit.transpiler.passes import VF2Layout -from qiskit.transpiler.passes import TrivialLayout -from qiskit.transpiler.passes import DenseLayout -from qiskit.transpiler.passes import NoiseAdaptiveLayout -from qiskit.transpiler.passes import SabreLayout 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 GatesInBasis -from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements from qiskit.transpiler.preset_passmanagers import common -from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason - from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePluginManager, ) @@ -71,79 +61,17 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa 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" + layout_method = pass_manager_config.layout_method or "default" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" optimization_method = pass_manager_config.optimization_method scheduling_method = pass_manager_config.scheduling_method or "default" - seed_transpiler = pass_manager_config.seed_transpiler - backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree 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 hls_config = pass_manager_config.hls_config - # Search for a perfect layout, or choose a dense layout, if no layout given - _given_layout = SetLayout(initial_layout) - - def _choose_layout_condition(property_set): - # layout hasn't been set yet - return not property_set["layout"] - - def _vf2_match_not_found(property_set): - # If a layout hasn't been set by the time we run vf2 layout we need to - # run layout - if property_set["layout"] is None: - return True - # if VF2 layout stopped for any reason other than solution found we need - # to run layout since VF2 didn't converge. - if ( - property_set["VF2Layout_stop_reason"] is not None - and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND - ): - return True - return False - - # Try using VF2 layout to find a perfect layout - _choose_layout_0: list[BasePass] | BasePass = ( - [] - if pass_manager_config.layout_method - else VF2Layout( - coupling_map, - seed=seed_transpiler, - call_limit=int(5e6), # Set call limit to ~10 sec with rustworkx 0.10.2 - properties=backend_properties, - target=target, - max_trials=25000, # Limits layout scoring to < 6 sec on ~400 qubit devices - ) - ) - - if target is None: - coupling_map_layout = coupling_map - else: - coupling_map_layout = target - - if layout_method == "trivial": - _choose_layout_1: BasePass = TrivialLayout(coupling_map_layout) - elif layout_method == "dense": - _choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target) - elif layout_method == "noise_adaptive": - if target is None: - _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) - else: - _choose_layout_1 = NoiseAdaptiveLayout(target) - elif layout_method == "sabre": - _choose_layout_1 = SabreLayout( - coupling_map_layout, - max_iterations=2, - seed=seed_transpiler, - swap_trials=10, - layout_trials=10, - skip_routing=pass_manager_config.routing_method is not None - and routing_method != "sabre", - ) - # Choose routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=2 @@ -173,25 +101,9 @@ def _opt_control(property_set): unitary_synthesis_plugin_config, hls_config, ) - if layout_method not in {"trivial", "dense", "noise_adaptive", "sabre"}: - layout = plugin_manager.get_passmanager_stage( - "layout", layout_method, pass_manager_config, optimization_level=2 - ) - else: - - def _swap_mapped(property_set): - return property_set["final_layout"] is None - - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout_0, condition=_choose_layout_condition) - layout.append( - [BarrierBeforeFinalMeasurements(), _choose_layout_1], condition=_vf2_match_not_found - ) - embed = common.generate_embed_passmanager(coupling_map_layout) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config, optimization_level=2 + ) routing = routing_pm else: layout = None diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 8b5e0ef36472..8b6f28e58137 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -20,13 +20,6 @@ from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passmanager import StagedPassManager - -from qiskit.transpiler.passes import SetLayout -from qiskit.transpiler.passes import VF2Layout -from qiskit.transpiler.passes import TrivialLayout -from qiskit.transpiler.passes import DenseLayout -from qiskit.transpiler.passes import NoiseAdaptiveLayout -from qiskit.transpiler.passes import SabreLayout from qiskit.transpiler.passes import MinimumPoint from qiskit.transpiler.passes import Depth from qiskit.transpiler.passes import Size @@ -39,10 +32,8 @@ from qiskit.transpiler.passes import ConsolidateBlocks from qiskit.transpiler.passes import UnitarySynthesis from qiskit.transpiler.passes import GatesInBasis -from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements from qiskit.transpiler.runningpassmanager import ConditionalController, FlowController from qiskit.transpiler.preset_passmanagers import common -from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePluginManager, ) @@ -76,12 +67,11 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa 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" + layout_method = pass_manager_config.layout_method or "default" routing_method = pass_manager_config.routing_method or "sabre" translation_method = pass_manager_config.translation_method or "translator" optimization_method = pass_manager_config.optimization_method scheduling_method = pass_manager_config.scheduling_method or "default" - seed_transpiler = pass_manager_config.seed_transpiler backend_properties = pass_manager_config.backend_properties approximation_degree = pass_manager_config.approximation_degree unitary_synthesis_method = pass_manager_config.unitary_synthesis_method @@ -89,67 +79,6 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa target = pass_manager_config.target hls_config = pass_manager_config.hls_config - # Layout on good qubits if calibration info available, otherwise on dense links - _given_layout = SetLayout(initial_layout) - - def _choose_layout_condition(property_set): - # layout hasn't been set yet - return not property_set["layout"] - - def _vf2_match_not_found(property_set): - # If a layout hasn't been set by the time we run vf2 layout we need to - # run layout - if property_set["layout"] is None: - return True - # if VF2 layout stopped for any reason other than solution found we need - # to run layout since VF2 didn't converge. - if ( - property_set["VF2Layout_stop_reason"] is not None - and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND - ): - return True - return False - - # 2a. If layout method is not set, first try VF2Layout - _choose_layout_0: list[BasePass] | BasePass = ( - [] - if pass_manager_config.layout_method - else VF2Layout( - coupling_map, - seed=seed_transpiler, - call_limit=int(3e7), # Set call limit to ~60 sec with rustworkx 0.10.2 - properties=backend_properties, - target=target, - max_trials=250000, # Limits layout scoring to < 60 sec on ~400 qubit devices - ) - ) - - if target is None: - coupling_map_layout = coupling_map - else: - coupling_map_layout = target - - # 2b. if VF2 didn't converge on a solution use layout_method (dense). - if layout_method == "trivial": - _choose_layout_1: BasePass = TrivialLayout(coupling_map_layout) - elif layout_method == "dense": - _choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target) - elif layout_method == "noise_adaptive": - if target is None: - _choose_layout_1 = NoiseAdaptiveLayout(backend_properties) - else: - _choose_layout_1 = NoiseAdaptiveLayout(target) - elif layout_method == "sabre": - _choose_layout_1 = SabreLayout( - coupling_map_layout, - max_iterations=4, - seed=seed_transpiler, - swap_trials=20, - layout_trials=20, - skip_routing=pass_manager_config.routing_method is not None - and routing_method != "sabre", - ) - # Choose routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=3 @@ -211,25 +140,9 @@ def _opt_control(property_set): init.append(OptimizeSwapBeforeMeasure()) init.append(RemoveDiagonalGatesBeforeMeasure()) 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, optimization_level=3 - ) - else: - - def _swap_mapped(property_set): - return property_set["final_layout"] is None - - layout = PassManager() - layout.append(_given_layout) - layout.append(_choose_layout_0, condition=_choose_layout_condition) - layout.append( - [BarrierBeforeFinalMeasurements(), _choose_layout_1], condition=_vf2_match_not_found - ) - embed = common.generate_embed_passmanager(coupling_map_layout) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout = plugin_manager.get_passmanager_stage( + "layout", layout_method, pass_manager_config, optimization_level=3 + ) routing = routing_pm else: layout = None diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 53fd7b8389bf..aa935fd7bb77 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -54,7 +54,7 @@ operate on 1 or 2 qubits. * - ``layout`` - ``qiskit.transpiler.layout`` - - ``trivial``, ``dense``, ``noise_adaptive``, ``sabre`` + - ``trivial``, ``dense``, ``noise_adaptive``, ``sabre``, ``default`` - 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 is expanded to include all @@ -83,7 +83,7 @@ to still be executable on the target. * - ``scheduling`` - ``qiskit.transpiler.scheduling`` - - ``alap``, ``asap`` + - ``alap``, ``asap``, ``default`` - 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:`~qiskit.circuit.Delay` instructions. diff --git a/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml b/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml new file mode 100644 index 000000000000..904ad9b4b926 --- /dev/null +++ b/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml @@ -0,0 +1,9 @@ +--- +upgrade: + - | + The plugin name ``default`` is reserved for the :ref:`stage_table` + ``layout`` and ``scheduling``. These stages previously did not reserve this + plugin name, but the ``default`` name is now used to represent Qiskit's + built-in default method for these stages. If you were using these names + for plugins on these stages these will conflict with Qiskit's usage and + you should rename your plugin. diff --git a/setup.py b/setup.py index e7ee6f893318..6cb88200eff2 100644 --- a/setup.py +++ b/setup.py @@ -146,6 +146,13 @@ "sabre = qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager", "none = qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager", ], + "qiskit.transpiler.layout": [ + "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager", + "trivial = qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager", + "dense = qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager", + "noise_adaptive = qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager", + "sabre = qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager", + ], "qiskit.transpiler.scheduling": [ "alap = qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager", "asap = qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager", diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index b7c496fabb62..268bf4ea1f72 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -71,6 +71,8 @@ def mock_get_passmanager_stage( return pm elif stage_name == "routing": return PassManager([]) + elif stage_name == "layout": + return PassManager([]) else: raise Exception("Failure, unexpected stage plugin combo for test")