Skip to content
Open
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
8 changes: 8 additions & 0 deletions qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ def callback_func(**kwargs):
scheduling_method = backend.get_scheduling_stage_plugin()
if translation_method is None and hasattr(backend, "get_translation_stage_plugin"):
translation_method = backend.get_translation_stage_plugin()
if init_method is None and hasattr(backend, "get_init_stage_plugin"):
init_method = backend.get_init_stage_plugin()
if layout_method is None and hasattr(backend, "get_layout_stage_plugin"):
layout_method = backend.get_layout_stage_plugin()
if routing_method is None and hasattr(backend, "get_routing_stage_plugin"):
routing_method = backend.get_routing_stage_plugin()
if optimization_method is None and hasattr(backend, "get_optimization_stage_plugin"):
optimization_method = backend.get_optimization_stage_plugin()

output_name = _parse_output_name(output_name, circuits)
coupling_map = _parse_coupling_map(coupling_map)
Expand Down
33 changes: 18 additions & 15 deletions qiskit/providers/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,26 @@ class BackendV2(Backend, ABC):
something like a ``shots`` field for a backend that runs experiments which
would contain an int for how many shots to execute.

A backend object can optionally contain methods named
``get_translation_stage_plugin`` and ``get_scheduling_stage_plugin``. If these
methods are present on a backend object and this object is used for
A backend object can optionally contain methods named:
* ``get_init_stage_plugin``
* ``get_layout_stage_plugin``
* ``get_routing_stage_plugin``
* ``get_translation_stage_plugin``
* ``get_scheduling_stage_plugin``.
Comment on lines +55 to +59
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Missing optimization?

Comment on lines +54 to +59
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also the docs-build error is a true error and it's complaining that these bullet points are indented by a space, when they shouldn't be indented at all (but you might need a blank line before and after them?).

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 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 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 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 should be 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
as the respective compilation stage. This enables a backend which has custom
requirements or bespoke methods for compilation to specify a stage plugin for
these stages to enable custom compilation of 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 or have the expected level of performance.
These methods are passed no input arguments and are expected to return a ``str``
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This new text adds in the bit about "bespoke methods" in as an additional the "custom requirements" reasons, but that then sounds weird
with the later sentence

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 or have the expected level of performance.

  1. they're "enabled by default" but we don't have a comment on how to disable them (it's a user setting of transpile, not a BackendV2-author setting)
  2. I think the whole rest of the sentence about "should only be used ..." is kind of unnecessary? It boils down to "if you believe Qiskit's defaults are not optimal for your backend", which might be a cleaner way of phrasing more of this text (since the context of the text has changed a little now it's every stage, and not just translation/scheduling).

representing the method name which should be 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 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 targeting the backend.
Comment on lines +72 to 75
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't "the typical expected use case", I don't think? It's a description of the actual calling signature. It might be clearer by writing out the actual signature (although see the other review comment about just making them overrideable methods).


Expand Down
9 changes: 5 additions & 4 deletions qiskit/transpiler/passmanager_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ def from_backend(cls, backend, _skip_target=False, **pass_manager_options):
res.instruction_durations = backend.instruction_durations
if res.target is None and not _skip_target:
res.target = backend.target
if res.scheduling_method is None and hasattr(backend, "get_scheduling_stage_plugin"):
res.scheduling_method = backend.get_scheduling_stage_plugin()
if res.translation_method is None and hasattr(backend, "get_translation_stage_plugin"):
res.translation_method = backend.get_translation_stage_plugin()
stages = ["init", "layout", "routing", "translation", "optimization", "scheduling"]
for stage in stages:
if getattr(res, f"{stage}_method", None) is None:
if plugin_meth := getattr(backend, f"get_{stage}_stage_plugin", None):
setattr(res, f"{stage}_method", plugin_meth())
return res

def __str__(self):
Expand Down
11 changes: 11 additions & 0 deletions releasenotes/notes/expand_plugins_backend-e731bba7ef87ad2f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
features_providers:
- Implementations of the :class:`.BackendV2` abstract class can now define the optional
methods ``get_init_stage_plugin``, ``get_layout_stage_plugin``,
``get_routing_stage_plugin``, and ``get_optimization_stage_plugin`` which can be used
to specify alternative default plugins to use when invoking the transpiler for that
backend. When defined these methods should return the name of the plugin
to use by default when running the corresponding :ref:`transpiler-preset` stages when
targeting this backend object. These methods work in the same as the existing :class:`.BackendV2` methods
``get_translation_stage_plugin`` and ``get_scheduling_stage_plugin`` but for the other
stages of the :ref:`transpiler-preset`.
54 changes: 48 additions & 6 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
RemoveResetInZeroState,
RemoveIdentityEquivalent,
TrivialLayout,
BasicSwap,
Optimize1qGates,
)
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.converters import circuit_to_dag
Expand Down Expand Up @@ -81,13 +85,25 @@ def mock_get_passmanager_stage(
)
return pm
elif stage_name == "init":
return PassManager([])
if plugin_name == "custom_stage_for_test":
return PassManager([RemoveIdentityEquivalent()])
else:
return PassManager([])
elif stage_name == "routing":
return PassManager([])
if plugin_name == "custom_stage_for_test":
return PassManager([BasicSwap(pm_config.coupling_map)])
else:
return PassManager([])
elif stage_name == "optimization":
return OptimizationPassManager().pass_manager(pm_config, optimization_level)
if plugin_name == "custom_stage_for_test":
return PassManager([Optimize1qGates()])
else:
return OptimizationPassManager().pass_manager(pm_config, optimization_level)
elif stage_name == "layout":
return PassManager([])
if plugin_name == "custom_stage_for_test":
return PassManager([TrivialLayout(pm_config.coupling_map)])
else:
return PassManager([])
else:
raise RuntimeError("Failure, unexpected stage plugin combo for test")

Expand Down Expand Up @@ -1381,13 +1397,39 @@ def get_translation_stage_plugin(self):
"""Custom post translation stage."""
return "custom_stage_for_test"

def get_init_stage_plugin(self):
"""Custom init stage."""
return "custom_stage_for_test"

def get_layout_stage_plugin(self):
"""Custom layout stage."""
return "custom_stage_for_test"

def get_routing_stage_plugin(self):
"""Custom routing stage."""
return "custom_stage_for_test"

def get_optimization_stage_plugin(self):
"""Custom optimization stage."""
return "custom_stage_for_test"

target = TargetBackend(num_qubits=7, coupling_map=LAGOS_CMAP, seed=42)
pm = generate_preset_pass_manager(optimization_level, backend=target)
self.assertIsInstance(pm, PassManager)

pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks]
self.assertIn("PadDynamicalDecoupling", pass_list)
self.assertIn("ALAPScheduleAnalysis", pass_list)
expected = [
"ContainsInstruction",
"ConditionalController",
"RemoveIdentityEquivalent",
"TrivialLayout",
"BasicSwap",
"RemoveResetInZeroState",
"Optimize1qGates",
"ALAPScheduleAnalysis",
"PadDynamicalDecoupling",
]
self.assertEqual(pass_list, expected)
post_translation_pass_list = [
x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks
]
Expand Down
Loading