Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate init stage to plugins #10689

Merged
merged 2 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,54 @@
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
from qiskit.transpiler.timing_constraints import TimingConstraints
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,
)
Comment on lines +67 to +74
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing, but is this definitely an "initialisation" action and not just a shared requirement of most of our routing plugins?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, since almost all layout and routing stages only work with 1 and 2 qubit gates this is why it's in the default. You could argue it's a better fit for the pre_layout stage, but I think init works better here because it allows for plugins to override it. So in a hypothetical case where there is a layout and routing pass for hardware that has native 3q gates they can have users do something like transpile(..., routing_method="toffoli", init_method="only_opt").

I guess there is an argument that the init stage should be more granular. We do have an issue for adding a dedicated synthesis stage #8936. But, at the end of the day there isn't very much in there and I worry about having too many stages that all only do one or two passes.

Copy link
Member

@jakelishman jakelishman Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's fine. I'd originally written "layout" in my comment, then mistakenly changed it because I thought it was only Sabre that really had a problem (which is via Sabre routing anyway - the Sabre layout logic itself doesn't care), but that's not true - noise aware, CSP and VF2 all have issues with 3q.

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`"""

Expand Down
27 changes: 6 additions & 21 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
)
Expand All @@ -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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole concept of a pre-initialisation stage is funny to me, but this does seem like an ok compromise.

layout_method=layout_method,
routing_method=routing_method,
translation_method=translation_method,
Expand All @@ -107,19 +94,17 @@ 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 = None
if optimization_method is not None:
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,
Expand Down
27 changes: 6 additions & 21 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,15 @@ 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"
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"
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(
Expand All @@ -86,17 +82,8 @@ def _opt_control(property_set):

_opt = [Optimize1qGatesDecomposition(basis=basis_gates, target=target), CXCancellation()]

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
)
Expand Down Expand Up @@ -143,7 +130,7 @@ def _unroll_condition(property_set):
"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,
Expand All @@ -152,14 +139,12 @@ def _unroll_condition(property_set):
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,
Expand Down
27 changes: 6 additions & 21 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,13 @@ 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
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(
Expand All @@ -90,17 +86,8 @@ def _opt_control(property_set):
CommutativeCancellation(basis_gates=basis_gates, target=target),
]

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
)
Expand Down Expand Up @@ -142,7 +129,7 @@ def _unroll_condition(property_set):
"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,
Expand All @@ -151,14 +138,12 @@ def _unroll_condition(property_set):
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,
Expand Down
28 changes: 6 additions & 22 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@
from qiskit.transpiler.passes import MinimumPoint
from qiskit.transpiler.passes import Depth
from qiskit.transpiler.passes import Size
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import CommutativeCancellation
from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure
from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure
from qiskit.transpiler.passes import Collect2qBlocks
from qiskit.transpiler.passes import ConsolidateBlocks
from qiskit.transpiler.passes import UnitarySynthesis
Expand Down Expand Up @@ -66,7 +63,7 @@ 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"
Expand All @@ -77,7 +74,6 @@ def level_3_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
hls_config = pass_manager_config.hls_config

# Choose routing pass
routing_pm = plugin_manager.get_passmanager_stage(
Expand Down Expand Up @@ -114,7 +110,7 @@ def _opt_control(property_set):
]

# 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,
Expand All @@ -123,22 +119,9 @@ def _opt_control(property_set):
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
Expand Down Expand Up @@ -201,6 +184,7 @@ def _unroll_condition(property_set):
)

return StagedPassManager(
pre_init=pre_init,
init=init,
layout=layout,
routing=routing,
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/preset_passmanagers/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
upgrade:
- |
The plugin name ``default`` is reserved for the :ref:`stage_table`
``layout`` and ``scheduling``. These stages previously did not reserve this
``init``, ``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
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def mock_get_passmanager_stage(
]
)
return pm
elif stage_name == "init":
return PassManager([])
elif stage_name == "routing":
return PassManager([])
elif stage_name == "layout":
Expand Down