Skip to content

Commit

Permalink
Use VF2Layout in all preset passmanagers
Browse files Browse the repository at this point in the history
With the introduction of the VF2Layout pass we now have a very fast
method of searching for a perfect layout. Previously we only had the
CSPLayout method for doing this which could be quite slow and we only
used in level 2 and level 3. Since VF2Layout operates quickly adding the
pass to each preset pass manager makes sense so we always use a perfect
layout if available (or unless a user explicitly specifies an initial
layout or layout method). This commit makes this change and adds
VF2Layout to each optimization level and uses a perfect layout if found
by default.

Fixes Qiskit#7156
  • Loading branch information
mtreinish committed Nov 11, 2021
1 parent aac9917 commit 51114d4
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 100 deletions.
24 changes: 22 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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
Expand Down Expand Up @@ -99,6 +100,24 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
def _choose_layout_condition(property_set):
return not property_set["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 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"] != "solution found"
):
return True
return False

_choose_layout_0 = (
[] if pass_manager_config.layout_method else VF2Layout(coupling_map, seed=seed_transpiler)
)

if layout_method == "trivial":
_choose_layout = TrivialLayout(coupling_map)
elif layout_method == "dense":
Expand Down Expand Up @@ -239,10 +258,11 @@ def _contains_delay(property_set):
# Build pass manager
pm0 = PassManager()
if coupling_map or initial_layout:
pm0.append(_unroll3q)
pm0.append(_given_layout)
pm0.append(_choose_layout, condition=_choose_layout_condition)
pm0.append(_choose_layout_0, condition=_choose_layout_condition)
pm0.append(_choose_layout, condition=_vf2_match_not_found)
pm0.append(_embed)
pm0.append(_unroll3q)
pm0.append(_swap_check)
pm0.append(_swap, condition=_swap_condition)
pm0.append(_unroll)
Expand Down
37 changes: 22 additions & 15 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +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 VF2Layout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
Expand All @@ -44,7 +45,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 Down Expand Up @@ -103,14 +103,27 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
# 1. Use trivial layout if no layout given
_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 _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"] != "solution found"
):
return True
return False

_choose_layout_0 = (
[] if pass_manager_config.layout_method else VF2Layout(coupling_map, seed=seed_transpiler)
)

# 2. Use a better layout on densely connected qubits, if circuit needs swaps
if layout_method == "trivial":
_improve_layout = TrivialLayout(coupling_map)
Expand All @@ -123,12 +136,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
)

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

Expand Down Expand Up @@ -272,11 +279,11 @@ def _contains_delay(property_set):
# Build pass manager
pm1 = PassManager()
if coupling_map or initial_layout:
pm1.append(_unroll3q)
pm1.append(_given_layout)
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(_improve_layout, condition=_vf2_match_not_found)
pm1.append(_embed)
pm1.append(_unroll3q)
pm1.append(_swap_check)
pm1.append(_swap, condition=_swap_condition)
pm1.append(_unroll)
Expand Down
63 changes: 20 additions & 43 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 Down Expand Up @@ -111,55 +110,34 @@ 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"),
]
)
# 1b. 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 contuing.
if property_set["trivial_layout_score"] is not None:
if property_set["trivial_layout_score"] != 0:
property_set["layout"]._wrapped = None
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"] != "solution found"
):
return True
return False

# 1c. if CSP layout doesn't converge on a solution use layout_method (dense) to get a layout
# 1a. 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)
)

# 1b. 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 @@ -309,12 +287,11 @@ def _contains_delay(property_set):
# Build pass manager
pm2 = PassManager()
if coupling_map or initial_layout:
pm2.append(_unroll3q)
pm2.append(_given_layout)
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(_unroll3q)
pm2.append(_swap_check)
pm2.append(_swap, condition=_swap_condition)
pm2.append(_unroll)
Expand Down
56 changes: 16 additions & 40 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 Down Expand Up @@ -128,55 +127,33 @@ 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"] != "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)
[] if pass_manager_config.layout_method else VF2Layout(coupling_map, 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 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:
property_set["layout"]._wrapped = None
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 @@ -323,8 +300,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

0 comments on commit 51114d4

Please sign in to comment.