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

Optimization stage as a plugin. #10581

Merged
merged 8 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
121 changes: 120 additions & 1 deletion qiskit/transpiler/preset_passmanagers/builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,21 @@
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.preset_passmanagers.plugin import (
PassManagerStagePlugin,
PassManagerStagePluginManager,
)
from qiskit.transpiler.passes.optimization import (
Optimize1qGatesDecomposition,
CommutativeCancellation,
Collect2qBlocks,
ConsolidateBlocks,
CXCancellation,
)
from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint
from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis
from qiskit.transpiler.passes.synthesis.unitary_synthesis import UnitarySynthesis
from qiskit.passmanager.flow_controllers import ConditionalController
from qiskit.transpiler.timing_constraints import TimingConstraints
from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason

Expand Down Expand Up @@ -378,6 +392,111 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
)


class OptimizationPassManager(PassManagerStagePlugin):
"""Plugin class for optimization stage"""

def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager:
"""Build pass manager for optimization stage."""
# Obtain the translation method required for this pass to work
translation_method = pass_manager_config.translation_method or "translator"
optimization = PassManager()
if optimization_level != 0:
plugin_manager = PassManagerStagePluginManager()
_depth_check = [Depth(recurse=True), FixedPoint("depth")]
_size_check = [Size(recurse=True), FixedPoint("size")]
# Minimum point check for optimization level 3.
_minimum_point_check = [
Depth(recurse=True),
Size(recurse=True),
MinimumPoint(["depth", "size"], "optimization_loop"),
]

def _opt_control(property_set):
return (not property_set["depth_fixed_point"]) or (
not property_set["size_fixed_point"]
)

translation = plugin_manager.get_passmanager_stage(
"translation",
translation_method,
pass_manager_config,
optimization_level=optimization_level,
)
if optimization_level == 1:
# Steps for optimization level 1
_opt = [
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
CXCancellation(),
]
elif optimization_level == 2:
# Steps for optimization level 2
_opt = [
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
CommutativeCancellation(
basis_gates=pass_manager_config.basis_gates,
target=pass_manager_config.target,
),
]
elif optimization_level == 3:
# Steps for optimization level 3
_opt = [
Collect2qBlocks(),
ConsolidateBlocks(
basis_gates=pass_manager_config.basis_gates,
target=pass_manager_config.target,
approximation_degree=pass_manager_config.approximation_degree,
),
UnitarySynthesis(
pass_manager_config.basis_gates,
approximation_degree=pass_manager_config.approximation_degree,
coupling_map=pass_manager_config.coupling_map,
backend_props=pass_manager_config.backend_properties,
method=pass_manager_config.unitary_synthesis_method,
plugin_config=pass_manager_config.unitary_synthesis_plugin_config,
target=pass_manager_config.target,
),
Optimize1qGatesDecomposition(
basis=pass_manager_config.basis_gates, target=pass_manager_config.target
),
CommutativeCancellation(target=pass_manager_config.target),
]

def _opt_control(property_set):
return not property_set["optimization_loop_minimum_point"]

else:
raise TranspilerError(f"Invalid optimization_level: {optimization_level}")

unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]]
# Build nested Flow controllers
def _unroll_condition(property_set):
return not property_set["all_gates_in_basis"]

# Check if any gate is not in the basis, and if so, run unroll passes
_unroll_if_out_of_basis = [
GatesInBasis(pass_manager_config.basis_gates, target=pass_manager_config.target),
ConditionalController(unroll, condition=_unroll_condition),
]

if optimization_level == 3:
optimization.append(_minimum_point_check)
else:
optimization.append(_depth_check + _size_check)
Comment on lines +485 to +488
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 I don't remember why we don't use the same check at all optimisation levels; there doesn't appear to be any discussion in #9612, but I know we talked about it more offline.

Copy link
Member

@mtreinish mtreinish Aug 22, 2023

Choose a reason for hiding this comment

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

It's a runtime thing. MinimumPoint adds a lot of overhead for the deep copying on potentially each iteration to do the rollback. We only added it to O3 in #9612 because the real need for the pass was coming from the numeric precision in UnitarySynthesis with the 2 qubit optimization causing the instability. For the optimizations we do in level 1 and 2 they're more stable (at least currently) and doing the fixed point comparison was reliable and significantly faster.

opt_loop = (
_opt + _unroll_if_out_of_basis + _minimum_point_check
if optimization_level == 3
else _opt + _unroll_if_out_of_basis + _depth_check + _size_check
)
optimization.append(opt_loop, do_while=_opt_control)
return optimization
else:
return None


class AlapSchedulingPassManager(PassManagerStagePlugin):
"""Plugin class for alap scheduling stage."""

Expand Down
10 changes: 4 additions & 6 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
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
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
Expand Down Expand Up @@ -113,11 +113,9 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
)
elif unroll_3q is not None:
init += unroll_3q
optimization = None
if optimization_method is not None:
optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=0
)
optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=0
)

return StagedPassManager(
init=init,
Expand Down
45 changes: 5 additions & 40 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,8 @@
Level 1 pass manager: light optimization by simple adjacent gate collapsing.
"""
from __future__ import annotations
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 import ConditionalController, FlowController
from qiskit.transpiler.passes import CXCancellation
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 GatesInBasis
from qiskit.transpiler.preset_passmanagers import common
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePluginManager,
Expand Down Expand Up @@ -63,7 +54,7 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
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
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
Expand All @@ -76,16 +67,6 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
"routing", routing_method, pass_manager_config, optimization_level=1
)

# Build optimization loop: merge 1q rotations and cancel CNOT gates iteratively
# until no more change in depth
_depth_check = [Depth(recurse=True), FixedPoint("depth")]
_size_check = [Size(recurse=True), FixedPoint("size")]

def _opt_control(property_set):
return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"])

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

unroll_3q = None
# Build full pass manager
if coupling_map or initial_layout:
Expand Down Expand Up @@ -118,26 +99,10 @@ def _opt_control(property_set):
)
else:
pre_optimization = common.generate_pre_op_passmanager(remove_reset_in_zero=True)
if optimization_method is None:
optimization = PassManager()
unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]]
# Build nested Flow controllers
def _unroll_condition(property_set):
return not property_set["all_gates_in_basis"]

# Check if any gate is not in the basis, and if so, run unroll passes
_unroll_if_out_of_basis: list[BasePass | FlowController] = [
GatesInBasis(basis_gates, target=target),
ConditionalController(unroll, condition=_unroll_condition),
]

optimization.append(_depth_check + _size_check)
opt_loop = _opt + _unroll_if_out_of_basis + _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, optimization_level=1
)

optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=1
)

sched = plugin_manager.get_passmanager_stage(
"scheduling", scheduling_method, pass_manager_config, optimization_level=1
Expand Down
47 changes: 5 additions & 42 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,8 @@
gate cancellation using commutativity rules.
"""
from __future__ import annotations
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 import ConditionalController, FlowController
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.preset_passmanagers import common
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePluginManager,
Expand Down Expand Up @@ -64,7 +55,7 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
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
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
Expand All @@ -77,19 +68,6 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
"routing", routing_method, pass_manager_config, optimization_level=2
)

# Build optimization loop: 1q rotation merge and commutative cancellation iteratively until
# no more change in depth
_depth_check = [Depth(recurse=True), FixedPoint("depth")]
_size_check = [Size(recurse=True), FixedPoint("size")]

def _opt_control(property_set):
return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"])

_opt: list[BasePass] = [
Optimize1qGatesDecomposition(basis=basis_gates, target=target),
CommutativeCancellation(basis_gates=basis_gates, target=target),
]

unroll_3q = None
# Build pass manager
if coupling_map or initial_layout:
Expand Down Expand Up @@ -118,25 +96,10 @@ 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)
if optimization_method is None:
optimization = PassManager()
unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]]
# Build nested Flow controllers
def _unroll_condition(property_set):
return not property_set["all_gates_in_basis"]

# Check if any gate is not in the basis, and if so, run unroll passes
_unroll_if_out_of_basis: list[BasePass | FlowController] = [
GatesInBasis(basis_gates, target=target),
ConditionalController(unroll, condition=_unroll_condition),
]
optimization.append(_depth_check + _size_check)
opt_loop = _opt + _unroll_if_out_of_basis + _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, optimization_level=2
)

optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=2
)

sched = plugin_manager.get_passmanager_stage(
"scheduling", scheduling_method, pass_manager_config, optimization_level=2
Expand Down
Loading