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

Add a default optimization level to generate_preset_pass_manager #12150

Merged
merged 14 commits into from
Jul 26, 2024
Merged
Changes from 1 commit
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
22 changes: 17 additions & 5 deletions qiskit/transpiler/preset_passmanagers/__init__.py
Original file line number Diff line number Diff line change
@@ -60,7 +60,8 @@
import warnings

from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.target import target_to_backend_properties
from qiskit.transpiler.target import target_to_backend_properties, Target
from qiskit.providers.backend import Backend
from qiskit.transpiler import CouplingMap

from .level0 import level_0_pass_manager
@@ -70,7 +71,7 @@


def generate_preset_pass_manager(
optimization_level,
optimization_level=2,
backend=None,
target=None,
basis_gates=None,
@@ -104,9 +105,10 @@ def generate_preset_pass_manager(
Args:
optimization_level (int): The optimization level to generate a
:class:`~.PassManager` for. This can be 0, 1, 2, or 3. Higher
levels generate more optimized circuits, at the expense of
longer transpilation time:
:class:`~.StagedPassManager` for. By default optimization level 2
is used if this is not specified. This can be 0, 1, 2, or 3. Higher
levels generate potentially more optimized circuits, at the expense
of longer transpilation time:
* 0: no optimization
* 1: light optimization
@@ -205,6 +207,16 @@ def generate_preset_pass_manager(
ValueError: if an invalid value for ``optimization_level`` is passed in.
"""

# Handle positional arguments for target and backend. This enables the usage
# pattern `generate_preset_pass_manager(backend.target)` to generate a default
# pass manager for a given target.
if isinstance(optimization_level, Target):
Copy link
Contributor

Choose a reason for hiding this comment

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

Personally, I'd rather this function not support any positional arguments.

I think we want a more ergonomic API on top of this function eventually anyways, e.g. #12161, so it seems too kludgy to me to try and make this accept a target positionally here.

Copy link
Member Author

Choose a reason for hiding this comment

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

#12161 is actually what I'm not sure we want to do. Adding a 3rd API with different semantics to do what we already have two interfaces for seems like a mistake to me. If people really want a different name I feel like we really should just alias it (but I still don't think it's worth it). But adding yet another entrypoint to accomplish the same thing feels like a mistake. If people are complaining about the ergonomics of the existing interface I feel like we should just evolve it in-place instead of diverging it again and requiring people to learn yet another thing.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, it's unfortunate that we've ended up here, but I think the right thing to do for users is to make it so the common patterns {transpile,generate_preset_pass_manager}({backend,target}, optimization_level=2) work the same in both forms. It ends up in an ugly signature for us, but we can tidy that up in place and potentially fix the signature properly for Qiskit 2.0+.

target = optimization_level
optimization_level = 2
elif isinstance(optimization_level, Backend):
backend = optimization_level
optimization_level = 2

if coupling_map is not None and not isinstance(coupling_map, CouplingMap):
coupling_map = CouplingMap(coupling_map)

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
features_transpiler:
- |
The ``optimization_level`` argument for the :func:`.generate_preset_pass_manager` function is
now optional. If it's not specified it will default to using optimization level 2. As the argument
is now optional, the first positional argument has been expanded to enable passing a :class:`.Target`
or a :class:`.BackendV2` as the first argument for more convenient construction. For example::
from qiskit.transpiler.preset_passmanager import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(100)
generate_preset_pass_manager(backend.Target)
will construct a default pass manager for the 100 qubit :class`.GenericBackendV2` instance.
18 changes: 18 additions & 0 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
@@ -1252,6 +1252,24 @@ def test_with_backend(self, optimization_level):
pm = generate_preset_pass_manager(optimization_level, target)
self.assertIsInstance(pm, PassManager)

def test_default_optimization_level(self):
"""Test a pass manager is constructed with no optimization level."""
backend = GenericBackendV2(num_qubits=14, coupling_map=MELBOURNE_CMAP)
pm = generate_preset_pass_manager(backend=backend)
self.assertIsInstance(pm, PassManager)

def test_default_optimization_level_backend_first_pos_arg(self):
"""Test a pass manager is constructed with only a positional backend."""
backend = GenericBackendV2(num_qubits=14, coupling_map=MELBOURNE_CMAP)
pm = generate_preset_pass_manager(backend)
self.assertIsInstance(pm, PassManager)

def test_default_optimization_level_target_first_pos_arg(self):
"""Test a pass manager is constructed with only a positional target."""
backend = GenericBackendV2(num_qubits=14, coupling_map=MELBOURNE_CMAP)
pm = generate_preset_pass_manager(backend.target)
self.assertIsInstance(pm, PassManager)

@data(0, 1, 2, 3)
def test_with_no_backend(self, optimization_level):
"""Test a passmanager is constructed with no backend and optimization level."""