Skip to content

Commit

Permalink
Pivot backend transpiler hook points to leverage plugins
Browse files Browse the repository at this point in the history
Since in qiskit-terra 0.22.0 we're adding a plugin interface for
transpiler stages already, the more natural fit for enabling backends to
inject custom passes into a pass maanger is via that plugin interface.
This commit updates the hook points to return a plugin method name that
a backend should use by default. This will provide the same level of
flexibility but also export any custom stages as standalone methods that
users can call into if they need to and also advertise the custom stage
methods along with all other installed methods.
  • Loading branch information
mtreinish committed Sep 21, 2022
1 parent 69e5b25 commit cf6a7ec
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 109 deletions.
17 changes: 13 additions & 4 deletions qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def transpile(
target: Target = None,
init_method: str = None,
optimization_method: str = None,
ignore_backend_default_methods: bool = False,
) -> Union[QuantumCircuit, List[QuantumCircuit]]:
"""Transpile one or more circuits, according to some desired transpilation targets.
Expand Down Expand Up @@ -269,6 +270,11 @@ def callback_func(**kwargs):
plugin is not used. You can see a list of installed plugins by
using :func:`~.list_stage_plugins` with ``"optimization"`` for the
``stage_name`` argument.
ignore_backend_default_methods: If set to ``True`` any default methods specified by
a backend will be ignored. Some backends specify alternative default methods
to support custom compilation target passes/plugins which support backend
specific compilation techniques. If you'd prefer that these did not run and
use the defaults for a given optimization level this can be used.
Returns:
The transpiled circuit(s).
Expand Down Expand Up @@ -336,6 +342,7 @@ def callback_func(**kwargs):
target,
init_method,
optimization_method,
ignore_backend_default_methods,
)
# Get transpile_args to configure the circuit transpilation job(s)
if coupling_map in unique_transpile_args:
Expand Down Expand Up @@ -588,6 +595,7 @@ def _parse_transpile_args(
target,
init_method,
optimization_method,
ignore_backend_default_methods,
) -> Tuple[List[Dict], Dict]:
"""Resolve the various types of args allowed to the transpile() function through
duck typing, overriding args, etc. Refer to the transpile() docstring for details on
Expand Down Expand Up @@ -660,8 +668,11 @@ def _parse_transpile_args(
}

list_transpile_args = []
if scheduling_method is None and hasattr(backend, "get_scheduling_stage"):
scheduling_method = backend.get_scheduling_stage()
if not ignore_backend_default_methods:
if scheduling_method is None and hasattr(backend, "get_scheduling_stage_method"):
scheduling_method = backend.get_scheduling_stage_method()
if translation_method is None and hasattr(backend, "get_translation_stage_method"):
translation_method = backend.get_translation_stage_method()

for key, value in {
"inst_map": inst_map,
Expand Down Expand Up @@ -694,8 +705,6 @@ def _parse_transpile_args(
"pass_manager_config": kwargs,
}
list_transpile_args.append(transpile_args)
if hasattr(backend, "get_post_translation_stage"):
shared_dict["post_translation_pm"] = backend.get_post_translation_stage()

return list_transpile_args, shared_dict

Expand Down
56 changes: 28 additions & 28 deletions qiskit/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,46 +411,46 @@ def _define(self):
As part of the transpiler there is a provision for backends to provide custom
stage implementation to facilitate hardware specific optimizations and
circuit transformations. Currently there are two hook points supported,
``get_post_translation_stage()`` which is used for a backend to specify a
:class:`~PassManager` which will be run after basis translation stage in the
compiler and ``get_scheduling_stage()`` which is used for a backend to
specify a :class:`~PassManager` which will be run for the scheduling stage
``get_translation_stage_method()`` which is used for a backend to specify a
string of the translation stage plugin to use in the compiler and
``get_scheduling_stage_method()`` which is used for a backend to
specify a string scheduling method pluging to use for the scheduling stage
by default (which is the last defined stage in a default compilation). These
hook points in a :class:`~.BackendV2` class should only be used if your
backend has special requirements for compilation that are not met by the
default backend.
default backend/:class:`~.Target` interface. Ideally we can expand these
interfaces to cover more details and information to inform the transpiler on
how/when to perform certain steps/optimizations.
To leverage these hook points you just need to add the methods to your
:class:`~.BackendV2` implementation and have them return a
:class:`~.PassManager` object. For example::
:class:`~.BackendV2` implementation and have them return a string method
For example::
from qiskit.circuit.library import XGate
from qiskit.transpiler.passes import (
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
ResetAfterMeasureSimplification
)
class Mybackend(BackendV2):
def get_scheduling_stage(self):
dd_sequence = [XGate(), XGate()]
pm = PassManager([
ALAPScheduleAnalysis(self.instruction_durations),
PadDynamicalDecoupling(self.instruction_durations, dd_sequence)
])
return pm
def get_scheduling_stage_method(self):
return "SpecialDD"
def get_post_translation_stage(self):
pm = PassManager([ResetAfterMeasureSimplification()])
return pm
def get_translation_stage_method(self):
return "BasisTranslatorWithCustom1qOptimization"
This snippet of a backend implementation will now have the :func:`~.transpile`
function run a custom stage for scheduling (unless the user manually requests a
different one explicitly) which will insert dynamical decoupling sequences and
also simplify resets after measurements after the basis translation stage. This
way if these two compilation steps are **required** for running on ``Mybackend``
the transpiler will be able to perform these steps without any manual user input.
function use the ``SpecialDD`` plugin for the scheduling stage and
the ``BasisTranslatorWithCustom1qOptimization`` plugin for the translation
stage by default when the target is set to ``Mybackend`` (unless the user manually
explicitly selects a different method). For this interface to work though transpiler
stage plugins must be implemented for the returned plugin name. You can refer
to :mod:`qiskit.transpiler.preset_passmanagers.plugin` module documentation for
details on how to implement plugins. The typical expectation is that if your backend
requires custom passes as part of a compilation stage the provider package will
include the transpiler stage plugins that use those passes. However, this is not
required and any valid method (from a built-in method or external plugin) can
be used.
This way if these two compilation steps are **required** for running or providing
efficient output on ``Mybackend`` the transpiler will be able to perform these
custom steps without any manual user input.
Run Method
----------
Expand Down
20 changes: 13 additions & 7 deletions qiskit/providers/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,18 +289,24 @@ class BackendV2(Backend, ABC):
defined in this class for backwards compatibility.
A backend object can optionally contain methods named
``get_post_translation_stage`` and ``get_scheduling_stage``. If these
``get_translation_stage_method`` and ``get_scheduling_stage_method``. If these
methods are present on a backend object and this object is used for
:func:`~.transpile` or :func:`~.generate_preset_pass_manager` the
transpilation process will default to using the output from those methods
as the scheduling stage and the post-translation compilation stage. This
enables a backend which has custom requirements for compilation to transform
as the scheduling stage and the translation compilation stage. This
enables a backend which has custom requirements for compilation to specify a
stage plugin for these stages to enable custom transformation of the circuit
the circuit to ensure it is runnable on the backend. These hooks are enabled
by default and should only be used to enable extra compilation steps
if they are **required** to ensure a circuit is executable on the backend.
These methods are passed no input arguments and are expected to return
a :class:`~.PassManager` object representing that stage of the transpilation
process.
if they are **required** to ensure a circuit is executable on the backend or
have the expected level of performance. These methods are passed no input
arguments and are expected to return a ``str`` representing the method name
which is typically a stage plugin (see: :mod:`qiskit.transpiler.preset_passmanagers.plugin`
for more details on plugins). The typical expected use case is for a backend
provider to implement a stage plugin for ``translation`` or ``scheduling``
that contains the custom compilation passes and then for the hook methods on
the backend object to return the plugin name so that :func:`~.transpile` will
use it by default when targetting the backend.
"""

version = 2
Expand Down
12 changes: 4 additions & 8 deletions qiskit/transpiler/passmanager_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def __init__(
target=None,
init_method=None,
optimization_method=None,
post_translation_pm=None,
):
"""Initialize a PassManagerConfig object
Expand Down Expand Up @@ -80,8 +79,6 @@ def __init__(
init_method (str): The plugin name for the init stage plugin to use
optimization_method (str): The plugin name for the optimization stage plugin
to use.
post_translation_pm (PassManager): An optional pass manager representing a
post-translation stage.
"""
self.initial_layout = initial_layout
self.basis_gates = basis_gates
Expand All @@ -101,7 +98,6 @@ def __init__(
self.unitary_synthesis_method = unitary_synthesis_method
self.unitary_synthesis_plugin_config = unitary_synthesis_plugin_config
self.target = target
self.post_translation_pm = post_translation_pm

@classmethod
def from_backend(cls, backend, **pass_manager_options):
Expand Down Expand Up @@ -153,10 +149,10 @@ def from_backend(cls, backend, **pass_manager_options):
if res.target is None:
if backend_version >= 2:
res.target = backend.target
if res.scheduling_method is None and hasattr(backend, "get_scheduling_stage"):
res.scheduling_method = backend.get_scheduling_stage()
if hasattr(backend, "get_post_translation_stage"):
res.post_translation_pm = backend.get_post_translation_stage()
if res.scheduling_method is None and hasattr(backend, "get_scheduling_stage_method"):
res.scheduling_method = backend.get_scheduling_stage_method()
if res.translation_method is None and hasattr(backend, "get_translation_stage_method"):
res.translation_method = backend.get_translation_stage_method()
return res

def __str__(self):
Expand Down
6 changes: 0 additions & 6 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,6 @@ def _choose_layout_condition(property_set):
sched = common.generate_scheduling(
instruction_durations, scheduling_method, timing_constraints, inst_map
)
elif isinstance(scheduling_method, PassManager):
sched = scheduling_method
else:
sched = plugin_manager.get_passmanager_stage(
"scheduling", scheduling_method, pass_manager_config, optimization_level=0
Expand All @@ -193,17 +191,13 @@ def _choose_layout_condition(property_set):
optimization = plugin_manager.get_passmanager_stage(
"optimization", optimization_method, pass_manager_config, optimization_level=0
)
post_translation = None
if pass_manager_config.post_translation_pm is not None:
post_translation = pass_manager_config.post_translation_pm

return StagedPassManager(
init=init,
layout=layout,
pre_routing=pre_routing,
routing=routing,
translation=translation,
post_translation=post_translation,
pre_optimization=pre_opt,
optimization=optimization,
scheduling=sched,
Expand Down
7 changes: 0 additions & 7 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,6 @@ def _unroll_condition(property_set):
sched = common.generate_scheduling(
instruction_durations, scheduling_method, timing_constraints, inst_map
)
elif isinstance(scheduling_method, PassManager):
sched = scheduling_method
else:
sched = plugin_manager.get_passmanager_stage(
"scheduling", scheduling_method, pass_manager_config, optimization_level=1
Expand All @@ -293,17 +291,12 @@ def _unroll_condition(property_set):
else:
init = unroll_3q

post_translation = None
if pass_manager_config.post_translation_pm is not None:
post_translation = pass_manager_config.post_translation_pm

return StagedPassManager(
init=init,
layout=layout,
pre_routing=pre_routing,
routing=routing,
translation=translation,
post_translation=post_translation,
pre_optimization=pre_optimization,
optimization=optimization,
scheduling=sched,
Expand Down
7 changes: 0 additions & 7 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,6 @@ def _unroll_condition(property_set):
sched = common.generate_scheduling(
instruction_durations, scheduling_method, timing_constraints, inst_map
)
elif isinstance(scheduling_method, PassManager):
sched = scheduling_method
else:
sched = plugin_manager.get_passmanager_stage(
"scheduling", scheduling_method, pass_manager_config, optimization_level=2
Expand All @@ -268,17 +266,12 @@ def _unroll_condition(property_set):
else:
init = unroll_3q

post_translation = None
if pass_manager_config.post_translation_pm is not None:
post_translation = pass_manager_config.post_translation_pm

return StagedPassManager(
init=init,
layout=layout,
pre_routing=pre_routing,
routing=routing,
translation=translation,
post_translation=post_translation,
pre_optimization=pre_optimization,
optimization=optimization,
scheduling=sched,
Expand Down
5 changes: 0 additions & 5 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,17 +315,12 @@ def _unroll_condition(property_set):
"scheduling", scheduling_method, pass_manager_config, optimization_level=3
)

post_translation = None
if pass_manager_config.post_translation_pm is not None:
post_translation = pass_manager_config.post_translation_pm

return StagedPassManager(
init=init,
layout=layout,
pre_routing=pre_routing,
routing=routing,
translation=translation,
post_translation=post_translation,
pre_optimization=pre_optimization,
optimization=optimization,
scheduling=sched,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ features:
points enabling backends to inject custom compilation steps as part of
:func:`~.transpile` and :func:`~.generate_preset_pass_manager`. If a
:class:`~.BackendV2` implementation includes the methods
``get_scheduling_stage()`` or ``get_post_translation_stage()`` the
``get_scheduling_stage_method()`` or ``get_translation_stage_method()`` the
transpiler will use the returned :class:`~.PassManager` object to run
additional custom transpiler passes when targetting that backend.
For more details on how to use this see :ref:`custom_transpiler_backend`.
- |
Added a new keyword argument, ``ignore_backend_default_methods``, to the
:func:`~.transpile` function can be used to disable a backend's custom
default method if the target backend has one set.
Loading

0 comments on commit cf6a7ec

Please sign in to comment.