Skip to content

Commit d20ce1e

Browse files
committed
Expand backend specific default methods to cover all stages
This commit expands the optional backend interface methods for specifying an alternative default plugin for a transpiler stage to cover all the stages in the preset pass manager. Originally this interface was introduced in Qiskit#8648 for the translation and scheduling stages. This was just a starting point as these were two stages most likely to require backend specific compilation. However, since then this interface has proven a useful tool for backend implementations but it's utility has been limited to just translation and scheduling. There is a potential role for a backend to provide an alternative default for the other stages of the preset pass manager. This finishes what Qiskit#8648 starts by expanding the interface to cover all the stages.
1 parent e63669b commit d20ce1e

5 files changed

Lines changed: 90 additions & 25 deletions

File tree

qiskit/compiler/transpiler.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ def callback_func(**kwargs):
277277
scheduling_method = backend.get_scheduling_stage_plugin()
278278
if translation_method is None and hasattr(backend, "get_translation_stage_plugin"):
279279
translation_method = backend.get_translation_stage_plugin()
280+
if init_method is None and hasattr(backend, "get_init_stage_plugin"):
281+
init_method = backend.get_init_stage_plugin()
282+
if layout_method is None and hasattr(backend, "get_layout_stage_plugin"):
283+
layout_method = backend.get_layout_stage_plugin()
284+
if routing_method is None and hasattr(backend, "get_routing_stage_plugin"):
285+
routing_method = backend.get_routing_stage_plugin()
286+
if optimization_method is None and hasattr(backend, "get_optimization_stage_plugin"):
287+
optimization_method = backend.get_optimization_stage_plugin()
280288

281289
output_name = _parse_output_name(output_name, circuits)
282290
coupling_map = _parse_coupling_map(coupling_map)

qiskit/providers/backend.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,26 @@ class BackendV2(Backend, ABC):
5151
something like a ``shots`` field for a backend that runs experiments which
5252
would contain an int for how many shots to execute.
5353
54-
A backend object can optionally contain methods named
55-
``get_translation_stage_plugin`` and ``get_scheduling_stage_plugin``. If these
56-
methods are present on a backend object and this object is used for
54+
A backend object can optionally contain methods named:
55+
* ``get_init_stage_plugin``
56+
* ``get_layout_stage_plugin``
57+
* ``get_routing_stage_plugin``
58+
* ``get_translation_stage_plugin``
59+
* ``get_scheduling_stage_plugin``.
60+
If these methods are present on a backend object and this object is used for
5761
:func:`~.transpile` or :func:`~.generate_preset_pass_manager` the
5862
transpilation process will default to using the output from those methods
59-
as the scheduling stage and the translation compilation stage. This
60-
enables a backend which has custom requirements for compilation to specify a
61-
stage plugin for these stages to enable custom transformation of
62-
the circuit to ensure it is runnable on the backend. These hooks are enabled
63-
by default and should only be used to enable extra compilation steps
64-
if they are **required** to ensure a circuit is executable on the backend or
65-
have the expected level of performance. These methods are passed no input
66-
arguments and are expected to return a ``str`` representing the method name
67-
which should be a stage plugin (see: :mod:`qiskit.transpiler.preset_passmanagers.plugin`
68-
for more details on plugins). The typical expected use case is for a backend
69-
provider to implement a stage plugin for ``translation`` or ``scheduling``
70-
that contains the custom compilation passes and then for the hook methods on
63+
as the respective compilation stage. This enables a backend which has custom
64+
requirements or bespoke methods for compilation to specify a stage plugin for
65+
these stages to enable custom compilation of the circuit to ensure it is
66+
runnable on the backend. These hooks are enabled by default and should only
67+
be used to enable extra compilation steps if they are **required** to ensure
68+
a circuit is executable on the backend or have the expected level of performance.
69+
These methods are passed no input arguments and are expected to return a ``str``
70+
representing the method name which should be a stage plugin (see:
71+
:mod:`qiskit.transpiler.preset_passmanagers.plugin` for more details on plugins).
72+
The typical expected use case is for a backend provider to implement a stage
73+
plugin that contains the custom compilation passes and then for the hook methods on
7174
the backend object to return the plugin name so that :func:`~.transpile` will
7275
use it by default when targeting the backend.
7376

qiskit/transpiler/passmanager_config.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,11 @@ def from_backend(cls, backend, _skip_target=False, **pass_manager_options):
138138
res.instruction_durations = backend.instruction_durations
139139
if res.target is None and not _skip_target:
140140
res.target = backend.target
141-
if res.scheduling_method is None and hasattr(backend, "get_scheduling_stage_plugin"):
142-
res.scheduling_method = backend.get_scheduling_stage_plugin()
143-
if res.translation_method is None and hasattr(backend, "get_translation_stage_plugin"):
144-
res.translation_method = backend.get_translation_stage_plugin()
141+
stages = ["init", "layout", "routing", "translation", "optimization", "scheduling"]
142+
for stage in stages:
143+
if getattr(res, f"{stage}_method", None) is None:
144+
if plugin_meth := getattr(backend, f"get_{stage}_stage_plugin", None):
145+
setattr(res, f"{stage}_method", plugin_meth())
145146
return res
146147

147148
def __str__(self):
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
features_providers:
3+
- Implementations of the :class:`.BackendV2` abstract class can now define the optional
4+
methods ``get_init_stage_plugin``, ``get_layout_stage_plugin``,
5+
``get_routing_stage_plugin``, and ``get_optimization_stage_plugin`` which can be used
6+
to specify alternative default plugins to use when invoking the transpiler for that
7+
backend. When defined these methods should return the name of the plugin
8+
to use by default when running the corresponding :ref:`transpiler-preset` stages when
9+
targeting this backend object. These methods work in the same as the existing :class:`.BackendV2` methods
10+
``get_translation_stage_plugin`` and ``get_scheduling_stage_plugin`` but for the other
11+
stages of the :ref:`transpiler-preset`.

test/python/transpiler/test_preset_passmanagers.py

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
ALAPScheduleAnalysis,
4545
PadDynamicalDecoupling,
4646
RemoveResetInZeroState,
47+
RemoveIdentityEquivalent,
48+
TrivialLayout,
49+
BasicSwap,
50+
Optimize1qGates,
4751
)
4852
from qiskit.providers.fake_provider import GenericBackendV2
4953
from qiskit.converters import circuit_to_dag
@@ -81,13 +85,25 @@ def mock_get_passmanager_stage(
8185
)
8286
return pm
8387
elif stage_name == "init":
84-
return PassManager([])
88+
if plugin_name == "custom_stage_for_test":
89+
return PassManager([RemoveIdentityEquivalent()])
90+
else:
91+
return PassManager([])
8592
elif stage_name == "routing":
86-
return PassManager([])
93+
if plugin_name == "custom_stage_for_test":
94+
return PassManager([BasicSwap(pm_config.coupling_map)])
95+
else:
96+
return PassManager([])
8797
elif stage_name == "optimization":
88-
return OptimizationPassManager().pass_manager(pm_config, optimization_level)
98+
if plugin_name == "custom_stage_for_test":
99+
return PassManager([Optimize1qGates()])
100+
else:
101+
return OptimizationPassManager().pass_manager(pm_config, optimization_level)
89102
elif stage_name == "layout":
90-
return PassManager([])
103+
if plugin_name == "custom_stage_for_test":
104+
return PassManager([TrivialLayout(pm_config.coupling_map)])
105+
else:
106+
return PassManager([])
91107
else:
92108
raise RuntimeError("Failure, unexpected stage plugin combo for test")
93109

@@ -1381,13 +1397,39 @@ def get_translation_stage_plugin(self):
13811397
"""Custom post translation stage."""
13821398
return "custom_stage_for_test"
13831399

1400+
def get_init_stage_plugin(self):
1401+
"""Custom init stage."""
1402+
return "custom_stage_for_test"
1403+
1404+
def get_layout_stage_plugin(self):
1405+
"""Custom layout stage."""
1406+
return "custom_stage_for_test"
1407+
1408+
def get_routing_stage_plugin(self):
1409+
"""Custom routing stage."""
1410+
return "custom_stage_for_test"
1411+
1412+
def get_optimization_stage_plugin(self):
1413+
"""Custom optimization stage."""
1414+
return "custom_stage_for_test"
1415+
13841416
target = TargetBackend(num_qubits=7, coupling_map=LAGOS_CMAP, seed=42)
13851417
pm = generate_preset_pass_manager(optimization_level, backend=target)
13861418
self.assertIsInstance(pm, PassManager)
13871419

13881420
pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks]
1389-
self.assertIn("PadDynamicalDecoupling", pass_list)
1390-
self.assertIn("ALAPScheduleAnalysis", pass_list)
1421+
expected = [
1422+
"ContainsInstruction",
1423+
"ConditionalController",
1424+
"RemoveIdentityEquivalent",
1425+
"TrivialLayout",
1426+
"BasicSwap",
1427+
"RemoveResetInZeroState",
1428+
"Optimize1qGates",
1429+
"ALAPScheduleAnalysis",
1430+
"PadDynamicalDecoupling",
1431+
]
1432+
self.assertEqual(pass_list, expected)
13911433
post_translation_pass_list = [
13921434
x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks
13931435
]

0 commit comments

Comments
 (0)