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

Use VF2Layout in all preset passmanagers #7213

Merged
merged 49 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
51114d4
Use VF2Layout in all preset passmanagers
mtreinish Nov 2, 2021
2abcec9
Revert changes to level 0
mtreinish Nov 2, 2021
f84d851
Set seed on perfect layout test
mtreinish Nov 2, 2021
ff7e0c6
Fix test failures and unexpected change in behavior
mtreinish Nov 2, 2021
269e45e
Add missing release note
mtreinish Nov 2, 2021
82be5c5
Apply suggestions from code review in release note
mtreinish Nov 2, 2021
0a28e2b
Make vf2 layout stop reason an Enum
mtreinish Nov 2, 2021
7c9e1c0
Update releasenotes/notes/vf2layout-preset-passmanager-db46513a24e79a…
mtreinish Nov 2, 2021
a2c06e6
Add back initial layout to level1
mtreinish Nov 3, 2021
94ad975
Tweak seeds to reduce effect of noise on fake_yorktown with aer
mtreinish Nov 3, 2021
70a5b26
Update release note
mtreinish Nov 3, 2021
2959b3f
Use id_order=True on vf2_mapping() for VF2Layout pass
mtreinish Nov 3, 2021
e95b8e2
Revert "Use id_order=True on vf2_mapping() for VF2Layout pass"
mtreinish Nov 10, 2021
f7b3902
Set real limits on calling vf2 and add quality heuristic
mtreinish Nov 11, 2021
6d3e2d1
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Nov 11, 2021
df47835
Remove initial layout default from level 1
mtreinish Nov 11, 2021
65ae6ee
Remove unused imports
mtreinish Nov 11, 2021
5eadd28
Use vigo instead of yorktown for oracle tests
mtreinish Nov 11, 2021
e22940c
Revert "Remove initial layout default from level 1"
mtreinish Nov 11, 2021
e45c58d
Fix warning and update docs to not emit one
mtreinish Nov 12, 2021
d0eb76d
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Nov 12, 2021
b145d52
Add debug logging and fix heurstic usage
mtreinish Nov 12, 2021
6c34b4f
Add better test coverage of new pass features
mtreinish Nov 12, 2021
434d3d4
Merge branch 'main' into vf2-everywhere
mtreinish Nov 12, 2021
4a2fbb2
Only run a single trial if the graphs are the same size
mtreinish Nov 12, 2021
5042350
Merge branch 'main' into vf2-everywhere
mtreinish Nov 16, 2021
107fda6
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Nov 22, 2021
f6bf092
Fix rebase error
mtreinish Nov 22, 2021
f77e102
Use enum type for stop reason condition
mtreinish Nov 23, 2021
59ef326
Add comment about call_limit value
mtreinish Nov 23, 2021
1adf7b5
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Nov 23, 2021
37aabdc
Undo unecessary seed change
mtreinish Nov 23, 2021
3b9eb49
Add back default seed randomization
mtreinish Nov 23, 2021
8888fde
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Nov 23, 2021
e71d9e3
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Jan 10, 2022
7105d1c
Merge branch 'main' into vf2-everywhere
mtreinish Jan 10, 2022
b8d7ef1
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Jan 26, 2022
9374913
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Jan 27, 2022
f1315d1
Permute operator bits based on layout
mtreinish Jan 28, 2022
b720ca6
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Feb 2, 2022
714b362
Fix docs build
mtreinish Feb 2, 2022
1b20e11
Merge remote-tracking branch 'origin/main' into vf2-everywhere
mtreinish Feb 14, 2022
039b2b5
Remove warning on use of TrivialLayout in opt level 1
mtreinish Feb 14, 2022
1bc29f0
Apply suggestions from code review
mtreinish Feb 14, 2022
c4a17e0
Cleanup inline comment numbering in preset pass manager modules
mtreinish Feb 14, 2022
df599db
Fix lint
mtreinish Feb 14, 2022
78eb1dd
Remove operator_permuted_layout() from backendv2 tests
mtreinish Feb 14, 2022
846f2de
Remove unused import
mtreinish Feb 14, 2022
486417c
Merge branch 'main' into vf2-everywhere
mergify[bot] Feb 24, 2022
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
68 changes: 53 additions & 15 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
from qiskit.transpiler.passes import CheckMap
from qiskit.transpiler.passes import GateDirection
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import VF2Layout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
from qiskit.transpiler.passes import SabreLayout
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import BasicSwap
from qiskit.transpiler.passes import LookaheadSwap
from qiskit.transpiler.passes import StochasticSwap
Expand All @@ -44,7 +46,6 @@
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import CheckGateDirection
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import Collect2qBlocks
from qiskit.transpiler.passes import ConsolidateBlocks
from qiskit.transpiler.passes import UnitarySynthesis
Expand All @@ -56,6 +57,7 @@
from qiskit.transpiler.passes import PulseGates
from qiskit.transpiler.passes import Error
from qiskit.transpiler.passes import ContainsInstruction
from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason

from qiskit.transpiler import TranspilerError

Expand Down Expand Up @@ -102,17 +104,58 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
timing_constraints = pass_manager_config.timing_constraints or TimingConstraints()
target = pass_manager_config.target

# 1. Use trivial layout if no layout given
# 1. Use trivial layout if no layout given if that isn't perfect use vf2 layout
_given_layout = SetLayout(initial_layout)

_choose_layout_and_score = [
TrivialLayout(coupling_map),
Layout2qDistance(coupling_map, property_name="trivial_layout_score"),
]

def _choose_layout_condition(property_set):
return not property_set["layout"]

def _trivial_not_perfect(property_set):
# Verify that a trivial layout is perfect. If trivial_layout_score > 0
# the layout is not perfect. The layout is unconditionally set by trivial
# layout so we need to clear it before contuing.
if (
property_set["trivial_layout_score"] is not None
and property_set["trivial_layout_score"] != 0
):
return True
return False

def _vf2_match_not_found(property_set):
# If a layout hasn't been set by the time we run vf2 layout we need to
# run layout
if property_set["layout"] is None:
return True
# if VF2 layout stopped for any reason other than solution found we need
# to run layout since VF2 didn't converge.
if (
property_set["VF2Layout_stop_reason"] is not None
and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND
):
return True
return False

_choose_layout_0 = (
[]
if pass_manager_config.layout_method
else [
TrivialLayout(coupling_map),
Layout2qDistance(coupling_map, property_name="trivial_layout_score"),
]
)

_choose_layout_1 = (
[]
if pass_manager_config.layout_method
else VF2Layout(
coupling_map,
seed=seed_transpiler,
call_limit=int(5e4), # Set call limit to ~100ms with retworkx 0.10.2
time_limit=0.1,
properties=backend_properties,
)
)

# 2. Decompose so only 1-qubit and 2-qubit gates remain
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
Expand All @@ -138,12 +181,6 @@ def _choose_layout_condition(property_set):
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

def _not_perfect_yet(property_set):
return (
property_set["trivial_layout_score"] is not None
and property_set["trivial_layout_score"] != 0
)

# 4. Extend dag/layout with ancillas using the full coupling map
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

Expand Down Expand Up @@ -279,8 +316,9 @@ def _contains_delay(property_set):
if coupling_map or initial_layout:
pm1.append(_given_layout)
pm1.append(_unroll3q)
pm1.append(_choose_layout_and_score, condition=_choose_layout_condition)
pm1.append(_improve_layout, condition=_not_perfect_yet)
pm1.append(_choose_layout_0, condition=_choose_layout_condition)
pm1.append(_choose_layout_1, condition=_trivial_not_perfect)
pm1.append(_improve_layout, condition=_vf2_match_not_found)
pm1.append(_embed)
pm1.append(_swap_check)
pm1.append(_swap, condition=_swap_condition)
Expand Down
69 changes: 28 additions & 41 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from qiskit.transpiler.passes import CheckMap
from qiskit.transpiler.passes import GateDirection
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import CSPLayout
from qiskit.transpiler.passes import VF2Layout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
Expand All @@ -45,7 +45,6 @@
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import CommutativeCancellation
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import CheckGateDirection
from qiskit.transpiler.passes import Collect2qBlocks
from qiskit.transpiler.passes import ConsolidateBlocks
Expand All @@ -58,6 +57,7 @@
from qiskit.transpiler.passes import PulseGates
from qiskit.transpiler.passes import Error
from qiskit.transpiler.passes import ContainsInstruction
from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason

from qiskit.transpiler import TranspilerError

Expand Down Expand Up @@ -126,54 +126,42 @@ def _choose_layout_condition(property_set):
# layout hasn't been set yet
return not property_set["layout"]

# 1a. If layout_method is not set, first try a trivial layout
_choose_layout_0 = (
[]
if pass_manager_config.layout_method
else [
TrivialLayout(coupling_map),
Layout2qDistance(coupling_map, property_name="trivial_layout_score"),
]
)
# 2b. If a trivial layout wasn't perfect (ie no swaps are needed) then try using
# CSP layout to find a perfect layout
_choose_layout_1 = (
[]
if pass_manager_config.layout_method
else CSPLayout(coupling_map, call_limit=1000, time_limit=10, seed=seed_transpiler)
)

def _trivial_not_perfect(property_set):
# Verify that a trivial layout is perfect. If trivial_layout_score > 0
# the layout is not perfect. The layout is unconditionally set by trivial
# layout so we need to clear it before continuing.
if property_set["trivial_layout_score"] is not None:
if property_set["trivial_layout_score"] != 0:
return True
return False

def _csp_not_found_match(property_set):
# If a layout hasn't been set by the time we run csp we need to run layout
def _vf2_match_not_found(property_set):
# If a layout hasn't been set by the time we run vf2 layout we need to
# run layout
if property_set["layout"] is None:
return True
# if CSP layout stopped for any reason other than solution found we need
# to run layout since CSP didn't converge.
# if VF2 layout stopped for any reason other than solution found we need
# to run layout since VF2 didn't converge.
if (
property_set["CSPLayout_stop_reason"] is not None
and property_set["CSPLayout_stop_reason"] != "solution found"
property_set["VF2Layout_stop_reason"] is not None
and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND
):
return True
return False

# 2c. if CSP layout doesn't converge on a solution use layout_method (dense) to get a layout
# 2a. Try using VF2 layout to find a perfect layout
_choose_layout_0 = (
[]
if pass_manager_config.layout_method
else VF2Layout(
coupling_map,
seed=seed_transpiler,
call_limit=int(5e6), # Set call limit to ~10 sec with retworkx 0.10.2
time_limit=10.0,
properties=backend_properties,
)
)

# 2b. if VF2 layout doesn't converge on a solution use layout_method (dense) to get a layout
if layout_method == "trivial":
_choose_layout_2 = TrivialLayout(coupling_map)
_choose_layout_1 = TrivialLayout(coupling_map)
elif layout_method == "dense":
_choose_layout_2 = DenseLayout(coupling_map, backend_properties)
_choose_layout_1 = DenseLayout(coupling_map, backend_properties)
elif layout_method == "noise_adaptive":
_choose_layout_2 = NoiseAdaptiveLayout(backend_properties)
_choose_layout_1 = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
_choose_layout_2 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler)
_choose_layout_1 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

Expand Down Expand Up @@ -317,8 +305,7 @@ def _contains_delay(property_set):
pm2.append(_given_layout)
pm2.append(_unroll3q)
pm2.append(_choose_layout_0, condition=_choose_layout_condition)
pm2.append(_choose_layout_1, condition=_trivial_not_perfect)
pm2.append(_choose_layout_2, condition=_csp_not_found_match)
pm2.append(_choose_layout_1, condition=_vf2_match_not_found)
pm2.append(_embed)
pm2.append(_swap_check)
pm2.append(_swap, condition=_swap_condition)
Expand Down
60 changes: 23 additions & 37 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from qiskit.transpiler.passes import CheckMap
from qiskit.transpiler.passes import GateDirection
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import CSPLayout
from qiskit.transpiler.passes import VF2Layout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
Expand All @@ -51,7 +51,6 @@
from qiskit.transpiler.passes import ConsolidateBlocks
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import CheckGateDirection
from qiskit.transpiler.passes import TimeUnitConversion
from qiskit.transpiler.passes import ALAPSchedule
Expand All @@ -61,6 +60,7 @@
from qiskit.transpiler.passes import PulseGates
from qiskit.transpiler.passes import Error
from qiskit.transpiler.passes import ContainsInstruction
from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason

from qiskit.transpiler import TranspilerError

Expand Down Expand Up @@ -129,54 +129,41 @@ def _choose_layout_condition(property_set):
# layout hasn't been set yet
return not property_set["layout"]

def _csp_not_found_match(property_set):
# If a layout hasn't been set by the time we run csp we need to run layout
def _vf2_match_not_found(property_set):
# If a layout hasn't been set by the time we run vf2 layout we need to
# run layout
if property_set["layout"] is None:
return True
# if CSP layout stopped for any reason other than solution found we need
# to run layout since CSP didn't converge.
# if VF2 layout stopped for any reason other than solution found we need
# to run layout since VF2 didn't converge.
if (
property_set["CSPLayout_stop_reason"] is not None
and property_set["CSPLayout_stop_reason"] != "solution found"
property_set["VF2Layout_stop_reason"] is not None
and property_set["VF2Layout_stop_reason"] is not VF2LayoutStopReason.SOLUTION_FOUND
):
return True
return False

# 2a. If layout method is not set, first try a trivial layout
# 2a. If layout method is not set, first try VF2Layout
_choose_layout_0 = (
[]
if pass_manager_config.layout_method
else [
TrivialLayout(coupling_map),
Layout2qDistance(coupling_map, property_name="trivial_layout_score"),
]
)
# 2b. If trivial layout wasn't perfect (ie no swaps are needed) then try
# using CSP layout to find a perfect layout
_choose_layout_1 = (
[]
if pass_manager_config.layout_method
else CSPLayout(coupling_map, call_limit=10000, time_limit=60, seed=seed_transpiler)
else VF2Layout(
coupling_map,
seed=seed_transpiler,
call_limit=int(3e7), # Set call limit to ~60 sec with retworkx 0.10.2
Copy link
Member

Choose a reason for hiding this comment

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

Isn't the relationship between number of calls and time taken dependent on the degree of the graph?

Copy link
Member Author

Choose a reason for hiding this comment

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

There is some dependency on graph structure to determine how the graph is traversed which will determines when we increment the call count and compare against the limit. But in practice it should be relatively constant except for edge some pathological edge cases (the ones I can think of might not even matter because I think they'll eval as not matching earlier). It's a bit dense but you can trace through the traversal part of the algorithm here: https://github.com/Qiskit/retworkx/blob/main/src/isomorphism/vf2.rs#L862-L976

Regardless I don't think it matters too much here because we need to pick something to set a per iteration limit on vf2_mapping() iterator to ensure retworkx doesn't take too long trying to find the first mapping (for hard isomorphism problems it can be slow, despite generally being fast) so even if it's not perfectly 60 seconds here it'll be close enough in most cases (there's also some fuzz around hardware, python version, and how you installed retworkx).

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that seems fine in general to me.

Copy link
Member Author

@mtreinish mtreinish Feb 14, 2022

Choose a reason for hiding this comment

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

Related to this something I've been thinking about after talking to @nonhermitian about vf2 for https://github.com/Qiskit-Partners/mapomatic is that we might want to add a scaling factor here based on the number of qubits. When I did scale testing on this with larger device coupling graphs things did take some more time to generate an initial mapping like 20 secs for a ~200 qubit graph. I'll generate a plot now to get a better feel for it but I'm thinking that something like call_limit=int(3e7 * 10**(abs(log10(coupling_map.size()) - 1))) might do the trick

time_limit=60,
properties=backend_properties,
)
)

def _trivial_not_perfect(property_set):
# Verify that a trivial layout is perfect. If trivial_layout_score > 0
# the layout is not perfect. The layout property set is unconditionally
# set by trivial layout so we clear that before running CSP
if property_set["trivial_layout_score"] is not None:
if property_set["trivial_layout_score"] != 0:
return True
return False

# 2c. if CSP didn't converge on a solution use layout_method (dense).
# 2b. if VF2 didn't converge on a solution use layout_method (dense).
if layout_method == "trivial":
_choose_layout_2 = TrivialLayout(coupling_map)
_choose_layout_1 = TrivialLayout(coupling_map)
elif layout_method == "dense":
_choose_layout_2 = DenseLayout(coupling_map, backend_properties)
_choose_layout_1 = DenseLayout(coupling_map, backend_properties)
elif layout_method == "noise_adaptive":
_choose_layout_2 = NoiseAdaptiveLayout(backend_properties)
_choose_layout_1 = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
_choose_layout_2 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler)
_choose_layout_1 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

Expand Down Expand Up @@ -329,8 +316,7 @@ def _contains_delay(property_set):
if coupling_map or initial_layout:
pm3.append(_given_layout)
pm3.append(_choose_layout_0, condition=_choose_layout_condition)
pm3.append(_choose_layout_1, condition=_trivial_not_perfect)
pm3.append(_choose_layout_2, condition=_csp_not_found_match)
pm3.append(_choose_layout_1, condition=_vf2_match_not_found)
pm3.append(_embed)
pm3.append(_swap_check)
pm3.append(_swap, condition=_swap_condition)
Expand Down
6 changes: 3 additions & 3 deletions qiskit/visualization/pulse_v2/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def draw(
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
qc = transpile(qc, FakeAlmaden())
qc = transpile(qc, FakeAlmaden(), layout_method='trivial')
sched = schedule(qc, FakeAlmaden())

draw(sched, backend=FakeAlmaden())
Expand All @@ -331,7 +331,7 @@ def draw(
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
qc = transpile(qc, FakeAlmaden())
qc = transpile(qc, FakeAlmaden(), layout_method='trivial')
sched = schedule(qc, FakeAlmaden())

draw(sched, style=IQXSimple(), backend=FakeAlmaden())
Expand All @@ -348,7 +348,7 @@ def draw(
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
qc = transpile(qc, FakeAlmaden())
qc = transpile(qc, FakeAlmaden(), layout_method='trivial')
sched = schedule(qc, FakeAlmaden())

draw(sched, style=IQXDebugging(), backend=FakeAlmaden())
Expand Down
6 changes: 3 additions & 3 deletions qiskit/visualization/timeline/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def draw(
qc.h(0)
qc.cx(0,1)

qc = transpile(qc, FakeAlmaden(), scheduling_method='alap')
qc = transpile(qc, FakeAlmaden(), scheduling_method='alap', layout_method='trivial')
draw(qc)

Drawing with the simple stylesheet.
Expand All @@ -312,7 +312,7 @@ def draw(
qc.h(0)
qc.cx(0,1)

qc = transpile(qc, FakeAlmaden(), scheduling_method='alap')
qc = transpile(qc, FakeAlmaden(), scheduling_method='alap', layout_method='trivial')
draw(qc, style=IQXSimple())

Drawing with the stylesheet suited for program debugging.
Expand All @@ -327,7 +327,7 @@ def draw(
qc.h(0)
qc.cx(0,1)

qc = transpile(qc, FakeAlmaden(), scheduling_method='alap')
qc = transpile(qc, FakeAlmaden(), scheduling_method='alap', layout_method='trivial')
draw(qc, style=IQXDebugging())

You can partially customize a preset stylesheet when call it::
Expand Down
Loading