-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add MinimumPoint transpiler pass (#9612)
* Add MinimumPoint transpiler pass This commit adds a new transpiler pass MinimumPoint which is used to find a local minimum point from the property set between executions of the pass. This is similar to the existing FixedPoint pass but will find the minimum fixed point over the past n exeuctions as opposed to finding a when two subsequent exectuions are at the same point. This is then used in optimization level 3 because the 2q unitary synthesis optimization that is part of the optimization loop can occasionally get stuck oscillating between multiple different equivalent decompositions which prevents the fixed point condition from ever being reached. By checking that we've reached the minimum over the last 5 executions we ensure we're reaching an exit condition in this situation. Fixes #5832 Fixes #9177 (the underlying cause of the optimization loop's inability to converge at optimization level 3 is not fixed here, there is still a root cause of instability in UnitarySynthesis this just changes the loop exit condition so we're never stuck in an infinte loop) * Doc copy-paste error cleanups * Rework logic to track state with a dataclass This commit reworks the logic of the pass to track the state via a dataclass instead of using separate property set fields. This cleans up the code for dealing with checking the current state relative to earlier iterations by making the access just attributes instead of secret strings. At the same time the tests were updated to handle this new data structure comments were added to better explain the logic flow being tested. * Fix doc typo * Apply suggestions from code review Co-authored-by: Jake Lishman <[email protected]> * Update slots syntax for dataclass to be compatible with Python < 3.10 * Update property set check in tests --------- Co-authored-by: Jake Lishman <[email protected]>
- Loading branch information
1 parent
1282a04
commit 694ade4
Showing
7 changed files
with
431 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2023 | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
|
||
"""Check if the DAG has reached a relative semi-stable point over previous runs.""" | ||
|
||
from copy import deepcopy | ||
from dataclasses import dataclass | ||
import math | ||
from typing import Tuple | ||
|
||
from qiskit.dagcircuit.dagcircuit import DAGCircuit | ||
from qiskit.transpiler.basepasses import TransformationPass | ||
|
||
|
||
class MinimumPoint(TransformationPass): | ||
"""Check if the DAG has reached a relative semi-stable point over previous runs | ||
This pass is similar to the :class:`~.FixedPoint` transpiler pass and is intended | ||
primarily to be used to set a loop break condition in the property set. | ||
However, unlike the :class:`~.FixedPoint` class which only sets the | ||
condition if 2 consecutive runs have the same value property set value | ||
this pass is designed to find a local minimum and use that instead. This | ||
pass is designed for an optimization loop where a fixed point may never | ||
get reached (for example if synthesis is used and there are multiple | ||
equivalent outputs for some cases). | ||
This pass will track the state of fields in the property set over its past | ||
executions and set a boolean field when either a fixed point is reached | ||
over the backtracking depth or selecting the minimum value found if the | ||
backtracking depth is reached. To do this it stores a deep copy of the | ||
current minimum DAG in the property set and when ``backtrack_depth`` number | ||
of executions is reached since the last minimum the output dag is set to | ||
that copy of the earlier minimum. | ||
Fields used by this pass in the property set are (all relative to the ``prefix`` | ||
argument): | ||
* ``{prefix}_minimum_point_state`` - Used to track the state of the minimpoint search | ||
* ``{prefix}_minimum_point`` - This value gets set to ``True`` when either a fixed point | ||
is reached over the ``backtrack_depth`` executions, or ``backtrack_depth`` was exceeded | ||
and an earlier minimum is restored. | ||
""" | ||
|
||
def __init__(self, property_set_list, prefix, backtrack_depth=5): | ||
"""Initialize an instance of this pass | ||
Args: | ||
property_set_list (list): A list of property set keys that will | ||
be used to evaluate the local minimum. The values of these | ||
property set keys will be used as a tuple for comparison | ||
prefix (str): The prefix to use for the property set key that is used | ||
for tracking previous evaluations | ||
backtrack_depth (int): The maximum number of entries to store. If | ||
this number is reached and the next iteration doesn't have | ||
a decrease in the number of values the minimum of the previous | ||
n will be set as the output dag and ``minimum_point`` will be set to | ||
``True`` in the property set | ||
""" | ||
super().__init__() | ||
self.property_set_list = property_set_list | ||
|
||
self.backtrack_name = f"{prefix}_minimum_point_state" | ||
self.minimum_reached = f"{prefix}_minimum_point" | ||
self.backtrack_depth = backtrack_depth | ||
|
||
def run(self, dag): | ||
"""Run the MinimumPoint pass on `dag`.""" | ||
score = tuple(self.property_set[x] for x in self.property_set_list) | ||
state = self.property_set[self.backtrack_name] | ||
|
||
# The pass starts at None and the first iteration doesn't set a real | ||
# score so the overall loop is treated as a do-while to ensure we have | ||
# at least 2 iterations. | ||
if state is None: | ||
self.property_set[self.backtrack_name] = _MinimumPointState( | ||
dag=None, score=(math.inf,) * len(self.property_set_list), since=0 | ||
) | ||
# If the score of this execution is worse than the previous execution | ||
# increment 'since' since we have not found a new minimum point | ||
elif score > state.score: | ||
state.since += 1 | ||
if state.since == self.backtrack_depth: | ||
self.property_set[self.minimum_reached] = True | ||
return self.property_set[self.backtrack_name].dag | ||
# If the score has decreased (gotten better) then this iteration is | ||
# better performing and this iteration should be the new minimum state. | ||
# So update the state to be this iteration and reset counter | ||
elif score < state.score: | ||
state.since = 1 | ||
state.score = score | ||
state.dag = deepcopy(dag) | ||
# If the current execution is equal to the previous minimum value then | ||
# we've reached an equivalent fixed point and we should use this iteration's | ||
# dag as the output and set the property set flag that we've found a minimum | ||
# point. | ||
elif score == state.score: | ||
self.property_set[self.minimum_reached] = True | ||
return dag | ||
|
||
return dag | ||
|
||
|
||
@dataclass | ||
class _MinimumPointState: | ||
__slots__ = ("dag", "score", "since") | ||
|
||
dag: DAGCircuit | ||
score: Tuple[float, ...] | ||
since: int |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
releasenotes/notes/add-minimum-point-pass-09cf9a9eec86fd48.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
--- | ||
features: | ||
- | | ||
Added a new transpiler pass, :class:`~.MinimumPoint` which is used primarily as | ||
a pass to check a loop condition in a :class:`~.PassManager`. This pass will | ||
track the state of fields in the property set over its past executions and set | ||
a boolean field when either a fixed point is reached over the backtracking depth | ||
or selecting the minimum value found if the backtracking depth is reached. This | ||
is an alternative to the :class:`~.FixedPoint` which simply checks for a fixed | ||
value in a property set field between subsequent executions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.