Skip to content

Commit 5757fa0

Browse files
authored
Stop shuffling coupling map node indices in VF2 passes (Qiskit#13492)
* 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. * Fix expected value in primitives test * Restore removed argument functionality and change default instead * Fix lint
1 parent b2d3df0 commit 5757fa0

File tree

4 files changed

+38
-26
lines changed

4 files changed

+38
-26
lines changed

qiskit/transpiler/preset_passmanagers/builtin_plugins.py

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ class BasicSwapPassManager(PassManagerStagePlugin):
238238

239239
def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager:
240240
"""Build routing stage PassManager."""
241-
seed_transpiler = pass_manager_config.seed_transpiler
242241
target = pass_manager_config.target
243242
coupling_map = pass_manager_config.coupling_map
244243
backend_properties = pass_manager_config.backend_properties
@@ -257,7 +256,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
257256
routing_pass,
258257
target,
259258
coupling_map=coupling_map,
260-
seed_transpiler=seed_transpiler,
259+
seed_transpiler=-1,
261260
use_barrier_before_measurement=True,
262261
)
263262
if optimization_level == 1:
@@ -268,7 +267,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
268267
vf2_call_limit=vf2_call_limit,
269268
vf2_max_trials=vf2_max_trials,
270269
backend_properties=backend_properties,
271-
seed_transpiler=seed_transpiler,
270+
seed_transpiler=-1,
272271
check_trivial=True,
273272
use_barrier_before_measurement=True,
274273
)
@@ -280,7 +279,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
280279
vf2_call_limit=vf2_call_limit,
281280
vf2_max_trials=vf2_max_trials,
282281
backend_properties=backend_properties,
283-
seed_transpiler=seed_transpiler,
282+
seed_transpiler=-1,
284283
use_barrier_before_measurement=True,
285284
)
286285
if optimization_level == 3:
@@ -291,7 +290,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
291290
vf2_call_limit=vf2_call_limit,
292291
vf2_max_trials=vf2_max_trials,
293292
backend_properties=backend_properties,
294-
seed_transpiler=seed_transpiler,
293+
seed_transpiler=-1,
295294
use_barrier_before_measurement=True,
296295
)
297296
raise TranspilerError(f"Invalid optimization level specified: {optimization_level}")
@@ -324,7 +323,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
324323
routing_pass,
325324
target,
326325
coupling_map=coupling_map,
327-
seed_transpiler=seed_transpiler,
326+
seed_transpiler=-1,
328327
use_barrier_before_measurement=True,
329328
)
330329
if optimization_level == 1:
@@ -335,7 +334,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
335334
vf2_call_limit=vf2_call_limit,
336335
vf2_max_trials=vf2_max_trials,
337336
backend_properties=backend_properties,
338-
seed_transpiler=seed_transpiler,
337+
seed_transpiler=-1,
339338
check_trivial=True,
340339
use_barrier_before_measurement=True,
341340
)
@@ -347,7 +346,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
347346
vf2_call_limit=vf2_call_limit,
348347
vf2_max_trials=vf2_max_trials,
349348
backend_properties=backend_properties,
350-
seed_transpiler=seed_transpiler,
349+
seed_transpiler=-1,
351350
use_barrier_before_measurement=True,
352351
)
353352
raise TranspilerError(f"Invalid optimization level specified: {optimization_level}")
@@ -358,7 +357,6 @@ class LookaheadSwapPassManager(PassManagerStagePlugin):
358357

359358
def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager:
360359
"""Build routing stage PassManager."""
361-
seed_transpiler = pass_manager_config.seed_transpiler
362360
target = pass_manager_config.target
363361
coupling_map = pass_manager_config.coupling_map
364362
coupling_map_routing = target
@@ -376,7 +374,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
376374
routing_pass,
377375
target,
378376
coupling_map=coupling_map,
379-
seed_transpiler=seed_transpiler,
377+
seed_transpiler=-1,
380378
use_barrier_before_measurement=True,
381379
)
382380
if optimization_level == 1:
@@ -388,7 +386,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
388386
vf2_call_limit=vf2_call_limit,
389387
vf2_max_trials=vf2_max_trials,
390388
backend_properties=backend_properties,
391-
seed_transpiler=seed_transpiler,
389+
seed_transpiler=-1,
392390
check_trivial=True,
393391
use_barrier_before_measurement=True,
394392
)
@@ -401,7 +399,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
401399
vf2_call_limit=vf2_call_limit,
402400
vf2_max_trials=vf2_max_trials,
403401
backend_properties=backend_properties,
404-
seed_transpiler=seed_transpiler,
402+
seed_transpiler=-1,
405403
use_barrier_before_measurement=True,
406404
)
407405
if optimization_level == 3:
@@ -413,7 +411,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
413411
vf2_call_limit=vf2_call_limit,
414412
vf2_max_trials=vf2_max_trials,
415413
backend_properties=backend_properties,
416-
seed_transpiler=seed_transpiler,
414+
seed_transpiler=-1,
417415
use_barrier_before_measurement=True,
418416
)
419417
raise TranspilerError(f"Invalid optimization level specified: {optimization_level}")
@@ -448,7 +446,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
448446
routing_pass,
449447
target,
450448
coupling_map=coupling_map,
451-
seed_transpiler=seed_transpiler,
449+
seed_transpiler=-1,
452450
use_barrier_before_measurement=True,
453451
)
454452
if optimization_level == 1:
@@ -466,7 +464,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
466464
vf2_call_limit=vf2_call_limit,
467465
vf2_max_trials=vf2_max_trials,
468466
backend_properties=backend_properties,
469-
seed_transpiler=seed_transpiler,
467+
seed_transpiler=-1,
470468
check_trivial=True,
471469
use_barrier_before_measurement=True,
472470
)
@@ -486,7 +484,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
486484
vf2_call_limit=vf2_call_limit,
487485
vf2_max_trials=vf2_max_trials,
488486
backend_properties=backend_properties,
489-
seed_transpiler=seed_transpiler,
487+
seed_transpiler=-1,
490488
use_barrier_before_measurement=True,
491489
)
492490
if optimization_level == 3:
@@ -504,7 +502,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
504502
vf2_call_limit=vf2_call_limit,
505503
vf2_max_trials=vf2_max_trials,
506504
backend_properties=backend_properties,
507-
seed_transpiler=seed_transpiler,
505+
seed_transpiler=-1,
508506
use_barrier_before_measurement=True,
509507
)
510508
raise TranspilerError(f"Invalid optimization level specified: {optimization_level}")
@@ -515,7 +513,6 @@ class NoneRoutingPassManager(PassManagerStagePlugin):
515513

516514
def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager:
517515
"""Build routing stage PassManager."""
518-
seed_transpiler = pass_manager_config.seed_transpiler
519516
target = pass_manager_config.target
520517
coupling_map = pass_manager_config.coupling_map
521518
routing_pass = Error(
@@ -527,7 +524,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana
527524
routing_pass,
528525
target,
529526
coupling_map=coupling_map,
530-
seed_transpiler=seed_transpiler,
527+
seed_transpiler=-1,
531528
use_barrier_before_measurement=True,
532529
)
533530

@@ -793,7 +790,7 @@ def _swap_mapped(property_set):
793790
)
794791
choose_layout_1 = VF2Layout(
795792
coupling_map=pass_manager_config.coupling_map,
796-
seed=pass_manager_config.seed_transpiler,
793+
seed=-1,
797794
call_limit=int(5e4), # Set call limit to ~100ms with rustworkx 0.10.2
798795
properties=pass_manager_config.backend_properties,
799796
target=pass_manager_config.target,
@@ -826,7 +823,7 @@ def _swap_mapped(property_set):
826823
elif optimization_level == 2:
827824
choose_layout_0 = VF2Layout(
828825
coupling_map=pass_manager_config.coupling_map,
829-
seed=pass_manager_config.seed_transpiler,
826+
seed=-1,
830827
call_limit=int(5e6), # Set call limit to ~10s with rustworkx 0.10.2
831828
properties=pass_manager_config.backend_properties,
832829
target=pass_manager_config.target,
@@ -861,7 +858,7 @@ def _swap_mapped(property_set):
861858
elif optimization_level == 3:
862859
choose_layout_0 = VF2Layout(
863860
coupling_map=pass_manager_config.coupling_map,
864-
seed=pass_manager_config.seed_transpiler,
861+
seed=-1,
865862
call_limit=int(3e7), # Set call limit to ~60s with rustworkx 0.10.2
866863
properties=pass_manager_config.backend_properties,
867864
target=pass_manager_config.target,

qiskit/transpiler/preset_passmanagers/common.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def generate_routing_passmanager(
281281
coupling_map=None,
282282
vf2_call_limit=None,
283283
backend_properties=None,
284-
seed_transpiler=None,
284+
seed_transpiler=-1,
285285
check_trivial=False,
286286
use_barrier_before_measurement=True,
287287
vf2_max_trials=None,
@@ -300,7 +300,10 @@ def generate_routing_passmanager(
300300
backend_properties (BackendProperties): Properties of a backend to
301301
synthesize for (e.g. gate fidelities).
302302
seed_transpiler (int): Sets random seed for the stochastic parts of
303-
the transpiler.
303+
the transpiler. This is currently only used for :class:`.VF2PostLayout` and the
304+
default value of ``-1`` is strongly recommended (which is no randomization).
305+
If a value of ``None`` is provided this will seed from system
306+
entropy.
304307
check_trivial (bool): If set to true this will condition running the
305308
:class:`~.VF2PostLayout` pass after routing on whether a trivial
306309
layout was tried and was found to not be perfect. This is only
@@ -358,7 +361,7 @@ def _swap_condition(property_set):
358361
target,
359362
coupling_map,
360363
backend_properties,
361-
seed_transpiler,
364+
seed=seed_transpiler,
362365
call_limit=vf2_call_limit,
363366
max_trials=vf2_max_trials,
364367
strict_direction=False,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
upgrade_transpiler:
3+
- |
4+
The default value for the :func:`.generate_routing_passmanager` argument
5+
``seed_transpiler`` has changed from ``None`` to ``-1``. This was done
6+
because this flag was only used to configure the :class:`.VF2PostLayout`
7+
transpiler pass in the output, and for that pass in particular the
8+
randomization typically only hurts performance and is not desirable.
9+
If you were relying on the previous default value you can restore this
10+
behavior by explicitly setting the argument ``seed_transpiler=None``. If
11+
you were explicitly setting a seed value for this parameter there is no
12+
change in behavior.

test/python/primitives/test_backend_estimator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ def test_layout(self, backend):
440440
estimator.set_transpile_options(seed_transpiler=15, optimization_level=1)
441441
value = estimator.run(qc, op, shots=10000).result().values[0]
442442
if optionals.HAS_AER:
443-
ref_value = -0.9954 if isinstance(backend, GenericBackendV2) else -0.916
443+
ref_value = -0.9954 if isinstance(backend, GenericBackendV2) else -0.934
444444
else:
445445
ref_value = -1
446446
self.assertEqual(value, ref_value)

0 commit comments

Comments
 (0)