From bdaff355b1a89db3738a9983e91692188dcc4953 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 25 Nov 2024 16:52:34 -0500 Subject: [PATCH 1/4] Stop shuffling coupling map node indices in VF2 passes This commit updates the preset pass manager construction usage of the VF2Layout and VF2PostLayout to stop shuffling the coupling map nodes by default. The theory behind the node shuffling is that since we limit the search space in the interest of runtime shuffling the node indices would change the search order to hopefully find a match that would be otherwise missed because we hit the internal state visit limit. However, this is showing in practice not to having a huge impact, especially since we're using the ordering heuristic from vf2++ that orders nodes by degree for the vf2 search. However, in the case of a circuit that was hardware efficient this can have the negative effect of making it harder for vf2 to find potential matches, especially on regular lattices. For example, in cases of a square grid lattice coupling map, and a path interaction graph (e.g. 0->1->2->3) the shuffling makes it much harder to find the mapping. This is because the lattice graphs the node degree is the same (or fall into the same few types of nodes) so the influence of the vf2++ heuristic isn't as significant and the index order has a larger impact because it is the search order for vf2. For smaller graphs this wasn't as noticeable but as devices scale up this effect has more of an impact. Since we rely solely on VF2 to find a perfect layout at higher optimization levels this shuffling is not desireable because we always want to find the perfect layout if it exists, especially for hardware efficient circuits that are constructed to not require swaps. So prioritizing the results for hardware efficient circuits is desireable by default. From an API impact perspective this doesn't change any of the interfaces or defaults for the VF2 passes in the interest of backwards compatibility. The only change is that this updates how we instantiate the VF2 passes to always use a deterministic node ordering independent of any user specified seed. This will be fully deterministic even in cases the user specifies a seed value for the transpilation, the output just might not be the same as before with the fixed seed; which is not guaranteed between releases. --- qiskit/transpiler/preset_passmanagers/builtin_plugins.py | 6 +++--- qiskit/transpiler/preset_passmanagers/common.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 85b0c05d5cae..1fa1bfcee3a3 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -793,7 +793,7 @@ def _swap_mapped(property_set): ) choose_layout_1 = VF2Layout( coupling_map=pass_manager_config.coupling_map, - seed=pass_manager_config.seed_transpiler, + seed=-1, call_limit=int(5e4), # Set call limit to ~100ms with rustworkx 0.10.2 properties=pass_manager_config.backend_properties, target=pass_manager_config.target, @@ -826,7 +826,7 @@ def _swap_mapped(property_set): elif optimization_level == 2: choose_layout_0 = VF2Layout( coupling_map=pass_manager_config.coupling_map, - seed=pass_manager_config.seed_transpiler, + seed=-1, call_limit=int(5e6), # Set call limit to ~10s with rustworkx 0.10.2 properties=pass_manager_config.backend_properties, target=pass_manager_config.target, @@ -861,7 +861,7 @@ def _swap_mapped(property_set): elif optimization_level == 3: choose_layout_0 = VF2Layout( coupling_map=pass_manager_config.coupling_map, - seed=pass_manager_config.seed_transpiler, + seed=-1, call_limit=int(3e7), # Set call limit to ~60s with rustworkx 0.10.2 properties=pass_manager_config.backend_properties, target=pass_manager_config.target, diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index c9bcc9a7904c..4d81f1ed3e46 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -358,7 +358,7 @@ def _swap_condition(property_set): target, coupling_map, backend_properties, - seed_transpiler, + seed=-1, call_limit=vf2_call_limit, max_trials=vf2_max_trials, strict_direction=False, From aacc29e10e94d97c2253500bb2bc583904ec45ce Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 6 Jan 2025 12:11:12 -0500 Subject: [PATCH 2/4] Fix expected value in primitives test --- test/python/primitives/test_backend_estimator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 62845461dacf..49d5efa2d782 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -440,7 +440,7 @@ def test_layout(self, backend): estimator.set_transpile_options(seed_transpiler=15, optimization_level=1) value = estimator.run(qc, op, shots=10000).result().values[0] if optionals.HAS_AER: - ref_value = -0.9954 if isinstance(backend, GenericBackendV2) else -0.916 + ref_value = -0.9954 if isinstance(backend, GenericBackendV2) else -0.934 else: ref_value = -1 self.assertEqual(value, ref_value) From 0c8ccb8543aab5e6da64b33e75aa5acf4ef0dfcb Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 6 Jan 2025 13:56:35 -0500 Subject: [PATCH 3/4] Restore removed argument functionality and change default instead --- .../preset_passmanagers/builtin_plugins.py | 32 +++++++++---------- .../transpiler/preset_passmanagers/common.py | 9 ++++-- .../notes/vf2-order-3ef2b4ca5ebd0588.yaml | 12 +++++++ 3 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 releasenotes/notes/vf2-order-3ef2b4ca5ebd0588.yaml diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 1fa1bfcee3a3..014fb3cb4e37 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -257,7 +257,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana routing_pass, target, coupling_map=coupling_map, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 1: @@ -268,7 +268,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, check_trivial=True, use_barrier_before_measurement=True, ) @@ -280,7 +280,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 3: @@ -291,7 +291,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") @@ -324,7 +324,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana routing_pass, target, coupling_map=coupling_map, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 1: @@ -335,7 +335,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, check_trivial=True, use_barrier_before_measurement=True, ) @@ -347,7 +347,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") @@ -376,7 +376,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana routing_pass, target, coupling_map=coupling_map, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 1: @@ -388,7 +388,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, check_trivial=True, use_barrier_before_measurement=True, ) @@ -401,7 +401,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 3: @@ -413,7 +413,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") @@ -448,7 +448,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana routing_pass, target, coupling_map=coupling_map, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 1: @@ -466,7 +466,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, check_trivial=True, use_barrier_before_measurement=True, ) @@ -486,7 +486,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) if optimization_level == 3: @@ -504,7 +504,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana vf2_call_limit=vf2_call_limit, vf2_max_trials=vf2_max_trials, backend_properties=backend_properties, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) raise TranspilerError(f"Invalid optimization level specified: {optimization_level}") @@ -527,7 +527,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana routing_pass, target, coupling_map=coupling_map, - seed_transpiler=seed_transpiler, + seed_transpiler=-1, use_barrier_before_measurement=True, ) diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 4d81f1ed3e46..431089f657dd 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -281,7 +281,7 @@ def generate_routing_passmanager( coupling_map=None, vf2_call_limit=None, backend_properties=None, - seed_transpiler=None, + seed_transpiler=-1, check_trivial=False, use_barrier_before_measurement=True, vf2_max_trials=None, @@ -300,7 +300,10 @@ def generate_routing_passmanager( 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 - the transpiler. + the transpiler. This is currently only used for :class:`.VF2PostLayout` and the + default value of ``-1`` is strongly recommended (which is no randomization). + If a value of ``None`` is provided this will seed from system + entropy. check_trivial (bool): If set to true this will condition running the :class:`~.VF2PostLayout` pass after routing on whether a trivial layout was tried and was found to not be perfect. This is only @@ -358,7 +361,7 @@ def _swap_condition(property_set): target, coupling_map, backend_properties, - seed=-1, + seed=seed_transpiler, call_limit=vf2_call_limit, max_trials=vf2_max_trials, strict_direction=False, diff --git a/releasenotes/notes/vf2-order-3ef2b4ca5ebd0588.yaml b/releasenotes/notes/vf2-order-3ef2b4ca5ebd0588.yaml new file mode 100644 index 000000000000..3d573a21901a --- /dev/null +++ b/releasenotes/notes/vf2-order-3ef2b4ca5ebd0588.yaml @@ -0,0 +1,12 @@ +--- +upgrade_transpiler: + - | + The default value for the :func:`.generate_routing_passmanager` argument + ``seed_transpiler`` has changed from ``None`` to ``-1``. This was done + because this flag was only used to configure the :class:`.VF2PostLayout` + transpiler pass in the output, and for that pass in particular the + randomization typically only hurts performance and is not desirable. + If you were relying on the previous default value you can restore this + behavior by explicitly setting the argument ``seed_transpiler=None``. If + you were explicitly setting a seed value for this parameter there is no + change in behavior. From f3cfca78b0ef8eb923346b68f797518f41eddb64 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 7 Jan 2025 16:40:45 -0500 Subject: [PATCH 4/4] Fix lint --- qiskit/transpiler/preset_passmanagers/builtin_plugins.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 014fb3cb4e37..1e5ca4519864 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -238,7 +238,6 @@ class BasicSwapPassManager(PassManagerStagePlugin): def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map backend_properties = pass_manager_config.backend_properties @@ -358,7 +357,6 @@ class LookaheadSwapPassManager(PassManagerStagePlugin): def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map coupling_map_routing = target @@ -515,7 +513,6 @@ class NoneRoutingPassManager(PassManagerStagePlugin): def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: """Build routing stage PassManager.""" - seed_transpiler = pass_manager_config.seed_transpiler target = pass_manager_config.target coupling_map = pass_manager_config.coupling_map routing_pass = Error(