From 51114d421629518bb4b2460471346d830a3b1eb7 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 2 Nov 2021 10:28:26 -0400 Subject: [PATCH] Use VF2Layout in all preset passmanagers 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 #7156 --- .../transpiler/preset_passmanagers/level0.py | 24 ++++++- .../transpiler/preset_passmanagers/level1.py | 37 ++++++----- .../transpiler/preset_passmanagers/level2.py | 63 ++++++------------- .../transpiler/preset_passmanagers/level3.py | 56 +++++------------ 4 files changed, 80 insertions(+), 100 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 7b24f1ceff06..b86207ecee92 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -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 @@ -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": @@ -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) diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index c7308ae969c7..c2aac8c53411 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -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 @@ -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 @@ -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) @@ -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()] @@ -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) diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 30f26e518253..080b604c1034 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -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 @@ -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 @@ -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) @@ -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) diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index e43192f5c9f6..769a7c4510db 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -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 @@ -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 @@ -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) @@ -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)