From 4a8c0ca38060ff6434c5fdbb42cdf0c741d4cd8a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 22 Aug 2023 15:02:45 -0400 Subject: [PATCH] Migrate init stage to plugins (#10689) This commit updates the preset pass manager construction to only use plugins for the init stage. To accomplish this the previously hard coded built-in pass manager used for each optimization level are refactored to be in a plugin named "default". One thing that is changed in this PR is that the use of `generate_control_flow_options_check()` is moved to the `pre_init` stage. The reason for this is because the way the init stage was being constructed in the preset pass managers was to do initial checking of any methods, and this was unconditionally being run. This is a more logical fit for pre_init stage because it should run before any specified plugin. Fixes: #10687 Fixes: #8661 --- .../preset_passmanagers/builtin_plugins.py | 42 +++++++++++++++++++ .../transpiler/preset_passmanagers/level0.py | 27 +++--------- .../transpiler/preset_passmanagers/level1.py | 27 +++--------- .../transpiler/preset_passmanagers/level2.py | 27 +++--------- .../transpiler/preset_passmanagers/level3.py | 36 +++------------- .../transpiler/preset_passmanagers/plugin.py | 2 +- ...reserved-plugin-name-3825c2000a579e38.yaml | 2 +- setup.py | 3 ++ .../transpiler/test_preset_passmanagers.py | 2 + 9 files changed, 73 insertions(+), 95 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 2ecd4653707b..b6cf031f3f3a 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -27,6 +27,9 @@ from qiskit.transpiler.passes import NoiseAdaptiveLayout from qiskit.transpiler.passes import CheckMap from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements +from qiskit.transpiler.passes import RemoveResetInZeroState +from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure +from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePlugin, @@ -47,6 +50,45 @@ from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason +class DefaultInitPassManager(PassManagerStagePlugin): + """Plugin class for default init stage.""" + + def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: + if optimization_level in {1, 2, 0}: + init = None + if ( + pass_manager_config.initial_layout + or pass_manager_config.coupling_map + or ( + pass_manager_config.target is not None + and pass_manager_config.target.build_coupling_map() is not None + ) + ): + init = common.generate_unroll_3q( + pass_manager_config.target, + pass_manager_config.basis_gates, + pass_manager_config.approximation_degree, + pass_manager_config.unitary_synthesis_method, + pass_manager_config.unitary_synthesis_plugin_config, + pass_manager_config.hls_config, + ) + elif optimization_level == 3: + init = common.generate_unroll_3q( + pass_manager_config.target, + pass_manager_config.basis_gates, + pass_manager_config.approximation_degree, + pass_manager_config.unitary_synthesis_method, + pass_manager_config.unitary_synthesis_plugin_config, + pass_manager_config.hls_config, + ) + init.append(RemoveResetInZeroState()) + init.append(OptimizeSwapBeforeMeasure()) + init.append(RemoveDiagonalGatesBeforeMeasure()) + else: + return TranspilerError(f"Invalid optimization level {optimization_level}") + return init + + class BasisTranslatorPassManager(PassManagerStagePlugin): """Plugin class for translation stage with :class:`~.BasisTranslator`""" diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 471a9d91fab1..7289a8635c55 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -46,34 +46,21 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout - init_method = pass_manager_config.init_method + init_method = pass_manager_config.init_method or "default" 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 or "default" scheduling_method = pass_manager_config.scheduling_method or "default" - 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 routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=0 ) - unroll_3q = None # Build pass manager if coupling_map or initial_layout: - unroll_3q = common.generate_unroll_3q( - target, - basis_gates, - approximation_degree, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - hls_config, - ) layout = plugin_manager.get_passmanager_stage( "layout", layout_method, pass_manager_config, optimization_level=0 ) @@ -98,7 +85,7 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa "scheduling", scheduling_method, pass_manager_config, optimization_level=0 ) - init = common.generate_control_flow_options_check( + pre_init = common.generate_control_flow_options_check( layout_method=layout_method, routing_method=routing_method, translation_method=translation_method, @@ -107,17 +94,15 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates=basis_gates, target=target, ) - if init_method is not None: - init += plugin_manager.get_passmanager_stage( - "init", init_method, pass_manager_config, optimization_level=0 - ) - elif unroll_3q is not None: - init += unroll_3q + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=0 + ) optimization = plugin_manager.get_passmanager_stage( "optimization", optimization_method, pass_manager_config, optimization_level=0 ) return StagedPassManager( + pre_init=pre_init, init=init, layout=layout, routing=routing, diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 22be66c4370c..4e585148c080 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -48,7 +48,7 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout - init_method = pass_manager_config.init_method + init_method = pass_manager_config.init_method or "default" # 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 "default" @@ -56,28 +56,15 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa translation_method = pass_manager_config.translation_method or "translator" optimization_method = pass_manager_config.optimization_method or "default" scheduling_method = pass_manager_config.scheduling_method or "default" - 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 routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=1 ) - unroll_3q = None # Build full pass manager if coupling_map or initial_layout: - unroll_3q = common.generate_unroll_3q( - target, - basis_gates, - approximation_degree, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - hls_config, - ) layout = plugin_manager.get_passmanager_stage( "layout", layout_method, pass_manager_config, optimization_level=1 ) @@ -108,7 +95,7 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa "scheduling", scheduling_method, pass_manager_config, optimization_level=1 ) - init = common.generate_control_flow_options_check( + pre_init = common.generate_control_flow_options_check( layout_method=layout_method, routing_method=routing_method, translation_method=translation_method, @@ -117,14 +104,12 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates=basis_gates, target=target, ) - if init_method is not None: - init += plugin_manager.get_passmanager_stage( - "init", init_method, pass_manager_config, optimization_level=1 - ) - elif unroll_3q is not None: - init += unroll_3q + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=1 + ) return StagedPassManager( + pre_init=pre_init, init=init, layout=layout, routing=routing, diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 8815a983ca61..941bf2fcf2ac 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -51,34 +51,21 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout - init_method = pass_manager_config.init_method + init_method = pass_manager_config.init_method or "default" 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 or "default" scheduling_method = pass_manager_config.scheduling_method or "default" - 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 routing pass routing_pm = plugin_manager.get_passmanager_stage( "routing", routing_method, pass_manager_config, optimization_level=2 ) - unroll_3q = None # Build pass manager if coupling_map or initial_layout: - unroll_3q = common.generate_unroll_3q( - target, - basis_gates, - approximation_degree, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - hls_config, - ) layout = plugin_manager.get_passmanager_stage( "layout", layout_method, pass_manager_config, optimization_level=2 ) @@ -105,7 +92,7 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa "scheduling", scheduling_method, pass_manager_config, optimization_level=2 ) - init = common.generate_control_flow_options_check( + pre_init = common.generate_control_flow_options_check( layout_method=layout_method, routing_method=routing_method, translation_method=translation_method, @@ -114,14 +101,12 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates=basis_gates, target=target, ) - if init_method is not None: - init += plugin_manager.get_passmanager_stage( - "init", init_method, pass_manager_config, optimization_level=2 - ) - elif unroll_3q is not None: - init += unroll_3q + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=2 + ) return StagedPassManager( + pre_init=pre_init, init=init, layout=layout, routing=routing, diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 1e4c7d589b35..90e3872adf64 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -18,9 +18,6 @@ from __future__ import annotations from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.passmanager import StagedPassManager -from qiskit.transpiler.passes import RemoveResetInZeroState -from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure -from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePluginManager, @@ -54,18 +51,14 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates = pass_manager_config.basis_gates coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout - init_method = pass_manager_config.init_method + init_method = pass_manager_config.init_method or "default" 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" scheduling_method = pass_manager_config.scheduling_method optimization_method = pass_manager_config.optimization_method or "default" scheduling_method = pass_manager_config.scheduling_method or "default" - 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 routing pass routing_pm = plugin_manager.get_passmanager_stage( @@ -73,7 +66,7 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa ) # Build pass manager - init = common.generate_control_flow_options_check( + pre_init = common.generate_control_flow_options_check( layout_method=layout_method, routing_method=routing_method, translation_method=translation_method, @@ -82,22 +75,9 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa basis_gates=basis_gates, target=target, ) - if init_method is not None: - init += plugin_manager.get_passmanager_stage( - "init", init_method, pass_manager_config, optimization_level=2 - ) - else: - init += common.generate_unroll_3q( - target, - basis_gates, - approximation_degree, - unitary_synthesis_method, - unitary_synthesis_plugin_config, - hls_config, - ) - init.append(RemoveResetInZeroState()) - init.append(OptimizeSwapBeforeMeasure()) - init.append(RemoveDiagonalGatesBeforeMeasure()) + init = plugin_manager.get_passmanager_stage( + "init", init_method, pass_manager_config, optimization_level=3 + ) if coupling_map or initial_layout: layout = plugin_manager.get_passmanager_stage( "layout", layout_method, pass_manager_config, optimization_level=3 @@ -118,11 +98,6 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa 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"] - ] else: pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True) @@ -131,6 +106,7 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa ) return StagedPassManager( + pre_init=pre_init, init=init, layout=layout, routing=routing, diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index d46a892e11c7..e0fabffc9d85 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -47,7 +47,7 @@ - Description and expectations * - ``init`` - ``qiskit.transpiler.init`` - - No reserved names + - ``default`` - 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 diff --git a/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml b/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml index ebb207eb837b..083c2ed90f83 100644 --- a/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml +++ b/releasenotes/notes/default-reserved-plugin-name-3825c2000a579e38.yaml @@ -2,7 +2,7 @@ upgrade: - | The plugin name ``default`` is reserved for the :ref:`stage_table` - ``layout``, ``optimization``, and ``scheduling``. These stages previously + ``init``, ``layout``, ``optimization``, 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 diff --git a/setup.py b/setup.py index 77f39a7aa068..c94693304d76 100644 --- a/setup.py +++ b/setup.py @@ -134,6 +134,9 @@ "permutation.basic = qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation", "permutation.acg = qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation", ], + "qiskit.transpiler.init": [ + "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager", + ], "qiskit.transpiler.translation": [ "translator = qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager", "unroller = qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager", diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 7074d0f2a8ea..f84beb3e5bda 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -70,6 +70,8 @@ def mock_get_passmanager_stage( ] ) return pm + elif stage_name == "init": + return PassManager([]) elif stage_name == "routing": return PassManager([]) elif stage_name == "optimization":