Skip to content

Commit

Permalink
Migrate layout stage to plugins (Qiskit#10622)
Browse files Browse the repository at this point in the history
* 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 Qiskit#9455

* Remove unnecessary if statement

* Handle invalid optimization levels in plugins

* Remove unused variable post-rebase
  • Loading branch information
mtreinish authored Aug 22, 2023
1 parent 4c88f07 commit b039687
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 348 deletions.
278 changes: 278 additions & 0 deletions qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
62 changes: 4 additions & 58 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit b039687

Please sign in to comment.