-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Set max_trials
for VF2Layout
in preset pass managers.
#10054
Changes from 4 commits
1814a5a
9c0e5e8
0e315eb
9a6a4ca
5ef7a94
89148c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ | |
from qiskit.transpiler.passes.layout.vf2_post_layout import VF2PostLayoutStopReason | ||
from qiskit.transpiler.exceptions import TranspilerError | ||
from qiskit.transpiler.layout import Layout | ||
from qiskit.utils.deprecation import deprecate_func | ||
|
||
_CONTROL_FLOW_OP_NAMES = {"for_loop", "if_else", "while_loop", "switch_case"} | ||
|
||
|
@@ -263,6 +264,7 @@ def generate_routing_passmanager( | |
seed_transpiler=None, | ||
check_trivial=False, | ||
use_barrier_before_measurement=True, | ||
vf2_max_trials=None, | ||
): | ||
"""Generate a routing :class:`~qiskit.transpiler.PassManager` | ||
|
||
|
@@ -273,7 +275,8 @@ def generate_routing_passmanager( | |
coupling_map (CouplingMap): The coupling map of the backend to route | ||
for | ||
vf2_call_limit (int): The internal call limit for the vf2 post layout | ||
pass. If this is ``None`` the vf2 post layout will not be run. | ||
pass. If this is ``None`` or ``0`` the vf2 post layout will not be | ||
run. | ||
backend_properties (BackendProperties): Properties of a backend to | ||
synthesize for (e.g. gate fidelities). | ||
seed_transpiler (int): Sets random seed for the stochastic parts of | ||
|
@@ -287,6 +290,9 @@ def generate_routing_passmanager( | |
use_barrier_before_measurement (bool): If true (the default) the | ||
:class:`~.BarrierBeforeFinalMeasurements` transpiler pass will be run prior to the | ||
specified pass in the ``routing_pass`` argument. | ||
vf2_max_trials (int): The maximum number of trials to run VF2 when | ||
evaluating the vf2 post layout | ||
pass. If this is ``None`` or ``0`` the vf2 post layout will not be run. | ||
Returns: | ||
PassManager: The routing pass manager | ||
""" | ||
|
@@ -314,14 +320,16 @@ def _swap_condition(property_set): | |
else: | ||
routing.append([routing_pass], condition=_swap_condition) | ||
|
||
if (target is not None or backend_properties is not None) and vf2_call_limit is not None: | ||
is_vf2_fully_bounded = vf2_call_limit and vf2_max_trials | ||
if (target is not None or backend_properties is not None) and is_vf2_fully_bounded: | ||
routing.append( | ||
VF2PostLayout( | ||
target, | ||
coupling_map, | ||
backend_properties, | ||
seed_transpiler, | ||
call_limit=vf2_call_limit, | ||
max_trials=vf2_max_trials, | ||
strict_direction=False, | ||
), | ||
condition=_run_post_layout_condition, | ||
|
@@ -539,6 +547,10 @@ def _require_alignment(property_set): | |
return scheduling | ||
|
||
|
||
@deprecate_func( | ||
additional_msg="Instead, use ``get_vf2_limits``.", | ||
since="0.24.0", | ||
) | ||
def get_vf2_call_limit( | ||
optimization_level: int, | ||
layout_method: Optional[str] = None, | ||
|
@@ -554,3 +566,37 @@ def get_vf2_call_limit( | |
elif optimization_level == 3: | ||
vf2_call_limit = int(3e7) # Set call limit to ~60 sec with rustworkx 0.10.2 | ||
return vf2_call_limit | ||
|
||
|
||
VF2Limits = collections.namedtuple("VF2Limits", ("call_limit", "max_trials")) | ||
|
||
|
||
def get_vf2_limits( | ||
optimization_level: int, | ||
layout_method: Optional[str] = None, | ||
initial_layout: Optional[Layout] = None, | ||
) -> VF2Limits: | ||
"""Get the VF2 limits for VF2-based layout passes. | ||
|
||
Returns: | ||
VF2Limits: An namedtuple with optional elements | ||
``call_limit`` and ``max_trials``. | ||
""" | ||
limits = VF2Limits(None, None) | ||
if layout_method is None and initial_layout is None: | ||
if optimization_level == 1: | ||
limits = VF2Limits( | ||
int(5e4), # Set call limit to ~100ms with rustworkx 0.10.2 | ||
2500, # Limits layout scoring to < 600ms on ~400 qubit devices | ||
) | ||
elif optimization_level == 2: | ||
limits = VF2Limits( | ||
int(5e6), # Set call limit to ~10 sec with rustworkx 0.10.2 | ||
25000, # Limits layout scoring to < 6 sec on ~400 qubit devices | ||
) | ||
elif optimization_level == 3: | ||
limits = VF2Limits( | ||
int(3e7), # Set call limit to ~60 sec with rustworkx 0.10.2 | ||
250000, # Limits layout scoring to < 60 sec on ~400 qubit devices | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing the need to count zeros is good. Maybe int(2.5e5)
250_000
int(2.5e4)
25_000 It's not obvious to me whether the LHS or RHS of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case I personally don't view it as a big deal, the number here is just an arbitrary limit that was found by experimentation, the actual numeric value isn't really relevant. My personal preference would be for |
||
) | ||
return limits |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
--- | ||
upgrade: | ||
- | | ||
The maximum number of trials evaluated when searching for the best | ||
layout using :class:`.VF2Layout` and :class:`.VF2PostLayout` is now | ||
limited in | ||
:func:`qiskit.transpiler.preset_passmanagers.level_1_pass_manager`, | ||
:func:`qiskit.transpiler.preset_passmanagers.level_2_pass_manager`, | ||
and | ||
:func:`qiskit.transpiler.preset_passmanagers.level_3_pass_manager` | ||
to ``2,500``, ``25,000``, and ``250,000``, respectively. Previously, | ||
all possible layouts were evaluated. This change was made to prevent | ||
transpilation from hanging during layout scoring for circuits with many | ||
connected components on larger devices, which scales combinatorially | ||
since each connected component must be evaluated in all possible | ||
positions on the device. To perform a full search as | ||
before, manually run :class:`.VF2PostLayout` over the transpiled circuit | ||
in strict mode, specifying ``0`` for ``max_trials``. | ||
deprecations: | ||
- | | ||
The method | ||
:func:`~qiskit.transpiler.preset_passmanagers.common.get_vf2_call_limit` | ||
is deprecated. Instead, use replacement method | ||
:func:`~qiskit.transpiler.preset_passmanagers.common.get_vf2_limits` and | ||
access the ``call_limit`` field of the returned namedtuple. | ||
fixes: | ||
- | | ||
Fixed a potential performance scaling issue with layout scoring in preset | ||
pass managers, which could occur when transpiling circuits with many | ||
connected components on large devices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit too quick, we can't deprecate it until after we're included the alternative in the release, see: https://qiskit.org/documentation/deprecation_policy.html#removing-a-feature so can you remove the decorator and the release note for this. We can do this for 0.25.0, but not for this PR which we'll need to backport to 0.24.0 (alternatively you can set
pending=True
on the decorator and remove the release not to downgrade the warning emitted toPendingDeprecationWarning
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that's right, thanks for the reminder. I removed the decorator as well as the release note in 5ef7a94.