Skip to content

Commit d430e58

Browse files
Deprecates StochasticSwap and suggests the use of SabreSwap (#12983)
* first attempt to fix issue 12552 * first attempt to fix issue 12552 * fixed issue 12552 and unittest * formatted and completed 12552 * formatted and completed 12552 documentation * fixed unit tests 12552 * Update qiskit/transpiler/passes/routing/stochastic_swap.py Co-authored-by: Elena Peña Tapia <[email protected]> * linted * passed all tests, including compiler test * final linting and unittest passing - hopefully * Update qiskit/transpiler/passes/routing/stochastic_swap.py Co-authored-by: Elena Peña Tapia <[email protected]> * Update releasenotes/notes/deprecate-StochasticSwap-451f46b273602b7b.yaml Co-authored-by: Elena Peña Tapia <[email protected]> * added test * Apply suggestions from code review --------- Co-authored-by: Elena Peña Tapia <[email protected]>
1 parent a11e76c commit d430e58

File tree

8 files changed

+469
-147
lines changed

8 files changed

+469
-147
lines changed

qiskit/transpiler/passes/routing/stochastic_swap.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from qiskit._accelerate import stochastic_swap as stochastic_swap_rs
3939
from qiskit._accelerate import nlayout
4040
from qiskit.transpiler.passes.layout import disjoint_utils
41+
from qiskit.utils import deprecate_func
4142

4243
from .utils import get_swap_map_dag
4344

@@ -59,6 +60,12 @@ class StochasticSwap(TransformationPass):
5960
the circuit.
6061
"""
6162

63+
@deprecate_func(
64+
since="1.3",
65+
removal_timeline="in the 2.0 release",
66+
additional_msg="The `StochasticSwap` transpilation pass is a suboptimal "
67+
"routing algorithm and has been superseded by the :class:`.SabreSwap` pass.",
68+
)
6269
def __init__(self, coupling_map, trials=20, seed=None, fake_run=False, initial_layout=None):
6370
"""StochasticSwap initializer.
6471
@@ -76,6 +83,7 @@ def __init__(self, coupling_map, trials=20, seed=None, fake_run=False, initial_l
7683
initial_layout (Layout): starting layout at beginning of pass.
7784
"""
7885
super().__init__()
86+
7987
if isinstance(coupling_map, Target):
8088
self.target = coupling_map
8189
self.coupling_map = self.target.build_coupling_map()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
deprecations_transpiler:
3+
- |
4+
Deprecated ``StochasticSwap`` which has been superseded by :class:`.SabreSwap`.
5+
If the class is called from the transpile function, the change would be, for example::
6+
7+
tqc = transpile(
8+
circuit,
9+
routing_method="stochastic",
10+
layout_method="dense",
11+
seed_transpiler=12342,
12+
target=GenericBackendV2(
13+
num_qubits=27,
14+
coupling_map=MUMBAI_CMAP,
15+
).target,
16+
)
17+
18+
to::
19+
20+
tqc = transpile(
21+
circuit,
22+
routing_method="sabre",
23+
layout_method="sabre",
24+
seed_transpiler=12342,
25+
target=GenericBackendV2(
26+
num_qubits=27,
27+
coupling_map=MUMBAI_CMAP,
28+
).target,
29+
)
30+
31+
While for a pass mananager change::
32+
33+
qr = QuantumRegister(4, "q")
34+
qc = QuantumCircuit(qr)
35+
passmanager = PassManager(StochasticSwap(coupling, 20, 13))
36+
new_qc = passmanager.run(qc)
37+
38+
to::
39+
40+
qr = QuantumRegister(5, "q")
41+
qc = QuantumCircuit(qr)
42+
passmanager = PassManager(SabreSwap(target, "basic"))
43+
new_qc = passmanager.run(qc)
44+
45+
46+

test/python/compiler/test_transpiler.py

Lines changed: 197 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,10 @@ def test_move_measurements(self):
928928
circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "move_measurements.qasm"))
929929

930930
lay = [0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6]
931-
out = transpile(circ, initial_layout=lay, coupling_map=cmap, routing_method="stochastic")
931+
with self.assertWarns(DeprecationWarning):
932+
out = transpile(
933+
circ, initial_layout=lay, coupling_map=cmap, routing_method="stochastic"
934+
)
932935
out_dag = circuit_to_dag(out)
933936
meas_nodes = out_dag.named_nodes("measure")
934937
for meas_node in meas_nodes:
@@ -3498,7 +3501,7 @@ def _visit_block(circuit, qubit_mapping=None):
34983501
)[0]
34993502
self.assertIn(qubit_map[op_node.qargs[0]], components[2])
35003503

3501-
@data("sabre", "stochastic", "basic", "lookahead")
3504+
@data("sabre", "basic", "lookahead")
35023505
def test_basic_connected_circuit_dense_layout(self, routing_method):
35033506
"""Test basic connected circuit on disjoint backend"""
35043507
qc = QuantumCircuit(5)
@@ -3522,8 +3525,34 @@ def test_basic_connected_circuit_dense_layout(self, routing_method):
35223525
continue
35233526
self.assertIn(qubits, self.backend.target[op_name])
35243527

3528+
@data("stochastic")
3529+
def test_basic_connected_circuit_dense_layout_stochastic(self, routing_method):
3530+
"""Test basic connected circuit on disjoint backend for deprecated stochastic swap"""
3531+
# TODO: Remove when StochasticSwap is removed
3532+
qc = QuantumCircuit(5)
3533+
qc.h(0)
3534+
qc.cx(0, 1)
3535+
qc.cx(0, 2)
3536+
qc.cx(0, 3)
3537+
qc.cx(0, 4)
3538+
qc.measure_all()
3539+
with self.assertWarns(DeprecationWarning):
3540+
tqc = transpile(
3541+
qc,
3542+
self.backend,
3543+
layout_method="dense",
3544+
routing_method=routing_method,
3545+
seed_transpiler=42,
3546+
)
3547+
for inst in tqc.data:
3548+
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
3549+
op_name = inst.operation.name
3550+
if op_name == "barrier":
3551+
continue
3552+
self.assertIn(qubits, self.backend.target[op_name])
3553+
35253554
# Lookahead swap skipped for performance
3526-
@data("sabre", "stochastic", "basic")
3555+
@data("sabre", "basic")
35273556
def test_triple_circuit_dense_layout(self, routing_method):
35283557
"""Test a split circuit with one circuit component per chip."""
35293558
qc = QuantumCircuit(30)
@@ -3572,7 +3601,58 @@ def test_triple_circuit_dense_layout(self, routing_method):
35723601
continue
35733602
self.assertIn(qubits, self.backend.target[op_name])
35743603

3575-
@data("sabre", "stochastic", "basic", "lookahead")
3604+
@data("stochastic")
3605+
def test_triple_circuit_dense_layout_stochastic(self, routing_method):
3606+
"""Test a split circuit with one circuit component per chip for deprecated StochasticSwap."""
3607+
# TODO: Remove when StochasticSwap is removed
3608+
qc = QuantumCircuit(30)
3609+
qc.h(0)
3610+
qc.h(10)
3611+
qc.h(20)
3612+
qc.cx(0, 1)
3613+
qc.cx(0, 2)
3614+
qc.cx(0, 3)
3615+
qc.cx(0, 4)
3616+
qc.cx(0, 5)
3617+
qc.cx(0, 6)
3618+
qc.cx(0, 7)
3619+
qc.cx(0, 8)
3620+
qc.cx(0, 9)
3621+
qc.ecr(10, 11)
3622+
qc.ecr(10, 12)
3623+
qc.ecr(10, 13)
3624+
qc.ecr(10, 14)
3625+
qc.ecr(10, 15)
3626+
qc.ecr(10, 16)
3627+
qc.ecr(10, 17)
3628+
qc.ecr(10, 18)
3629+
qc.ecr(10, 19)
3630+
qc.cy(20, 21)
3631+
qc.cy(20, 22)
3632+
qc.cy(20, 23)
3633+
qc.cy(20, 24)
3634+
qc.cy(20, 25)
3635+
qc.cy(20, 26)
3636+
qc.cy(20, 27)
3637+
qc.cy(20, 28)
3638+
qc.cy(20, 29)
3639+
qc.measure_all()
3640+
with self.assertWarns(DeprecationWarning):
3641+
tqc = transpile(
3642+
qc,
3643+
self.backend,
3644+
layout_method="dense",
3645+
routing_method=routing_method,
3646+
seed_transpiler=42,
3647+
)
3648+
for inst in tqc.data:
3649+
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
3650+
op_name = inst.operation.name
3651+
if op_name == "barrier":
3652+
continue
3653+
self.assertIn(qubits, self.backend.target[op_name])
3654+
3655+
@data("sabre", "basic", "lookahead")
35763656
def test_triple_circuit_invalid_layout(self, routing_method):
35773657
"""Test a split circuit with one circuit component per chip."""
35783658
qc = QuantumCircuit(30)
@@ -3616,8 +3696,54 @@ def test_triple_circuit_invalid_layout(self, routing_method):
36163696
seed_transpiler=42,
36173697
)
36183698

3619-
# Lookahead swap skipped for performance reasons
3620-
@data("sabre", "stochastic", "basic")
3699+
@data("stochastic")
3700+
def test_triple_circuit_invalid_layout_stochastic(self, routing_method):
3701+
"""Test a split circuit with one circuit component per chip for deprecated ``StochasticSwap``"""
3702+
# TODO: Remove when StochasticSwap is removed
3703+
qc = QuantumCircuit(30)
3704+
qc.h(0)
3705+
qc.h(10)
3706+
qc.h(20)
3707+
qc.cx(0, 1)
3708+
qc.cx(0, 2)
3709+
qc.cx(0, 3)
3710+
qc.cx(0, 4)
3711+
qc.cx(0, 5)
3712+
qc.cx(0, 6)
3713+
qc.cx(0, 7)
3714+
qc.cx(0, 8)
3715+
qc.cx(0, 9)
3716+
qc.ecr(10, 11)
3717+
qc.ecr(10, 12)
3718+
qc.ecr(10, 13)
3719+
qc.ecr(10, 14)
3720+
qc.ecr(10, 15)
3721+
qc.ecr(10, 16)
3722+
qc.ecr(10, 17)
3723+
qc.ecr(10, 18)
3724+
qc.ecr(10, 19)
3725+
qc.cy(20, 21)
3726+
qc.cy(20, 22)
3727+
qc.cy(20, 23)
3728+
qc.cy(20, 24)
3729+
qc.cy(20, 25)
3730+
qc.cy(20, 26)
3731+
qc.cy(20, 27)
3732+
qc.cy(20, 28)
3733+
qc.cy(20, 29)
3734+
qc.measure_all()
3735+
with self.assertWarns(DeprecationWarning):
3736+
with self.assertRaises(TranspilerError):
3737+
transpile(
3738+
qc,
3739+
self.backend,
3740+
layout_method="trivial",
3741+
routing_method=routing_method,
3742+
seed_transpiler=42,
3743+
)
3744+
3745+
# Lookahead swap skipped for performance reasons, stochastic moved to new test due to deprecation
3746+
@data("sabre", "basic")
36213747
def test_six_component_circuit_dense_layout(self, routing_method):
36223748
"""Test input circuit with more than 1 component per backend component."""
36233749
qc = QuantumCircuit(42)
@@ -3678,6 +3804,71 @@ def test_six_component_circuit_dense_layout(self, routing_method):
36783804
continue
36793805
self.assertIn(qubits, self.backend.target[op_name])
36803806

3807+
# Lookahead swap skipped for performance reasons
3808+
@data("stochastic")
3809+
def test_six_component_circuit_dense_layout_stochastic(self, routing_method):
3810+
"""Test input circuit with more than 1 component per backend component
3811+
for deprecated ``StochasticSwap``."""
3812+
# TODO: Remove when StochasticSwap is removed
3813+
qc = QuantumCircuit(42)
3814+
qc.h(0)
3815+
qc.h(10)
3816+
qc.h(20)
3817+
qc.cx(0, 1)
3818+
qc.cx(0, 2)
3819+
qc.cx(0, 3)
3820+
qc.cx(0, 4)
3821+
qc.cx(0, 5)
3822+
qc.cx(0, 6)
3823+
qc.cx(0, 7)
3824+
qc.cx(0, 8)
3825+
qc.cx(0, 9)
3826+
qc.ecr(10, 11)
3827+
qc.ecr(10, 12)
3828+
qc.ecr(10, 13)
3829+
qc.ecr(10, 14)
3830+
qc.ecr(10, 15)
3831+
qc.ecr(10, 16)
3832+
qc.ecr(10, 17)
3833+
qc.ecr(10, 18)
3834+
qc.ecr(10, 19)
3835+
qc.cy(20, 21)
3836+
qc.cy(20, 22)
3837+
qc.cy(20, 23)
3838+
qc.cy(20, 24)
3839+
qc.cy(20, 25)
3840+
qc.cy(20, 26)
3841+
qc.cy(20, 27)
3842+
qc.cy(20, 28)
3843+
qc.cy(20, 29)
3844+
qc.h(30)
3845+
qc.cx(30, 31)
3846+
qc.cx(30, 32)
3847+
qc.cx(30, 33)
3848+
qc.h(34)
3849+
qc.cx(34, 35)
3850+
qc.cx(34, 36)
3851+
qc.cx(34, 37)
3852+
qc.h(38)
3853+
qc.cx(38, 39)
3854+
qc.cx(39, 40)
3855+
qc.cx(39, 41)
3856+
qc.measure_all()
3857+
with self.assertWarns(DeprecationWarning):
3858+
tqc = transpile(
3859+
qc,
3860+
self.backend,
3861+
layout_method="dense",
3862+
routing_method=routing_method,
3863+
seed_transpiler=42,
3864+
)
3865+
for inst in tqc.data:
3866+
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
3867+
op_name = inst.operation.name
3868+
if op_name == "barrier":
3869+
continue
3870+
self.assertIn(qubits, self.backend.target[op_name])
3871+
36813872
@data(0, 1, 2, 3)
36823873
def test_transpile_target_with_qubits_without_ops(self, opt_level):
36833874
"""Test qubits without operations aren't ever used."""

test/python/transpiler/test_mappers.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,13 @@ def test_a_common_test(self):
7171
import unittest
7272
import os
7373
import sys
74+
import warnings
7475

7576
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit, transpile
7677
from qiskit.providers.basic_provider import BasicSimulator
7778
from qiskit.qasm2 import dump
7879
from qiskit.transpiler import PassManager
79-
from qiskit.transpiler.passes import BasicSwap, LookaheadSwap, StochasticSwap, SabreSwap
80+
from qiskit.transpiler.passes import BasicSwap, LookaheadSwap, SabreSwap, StochasticSwap
8081
from qiskit.transpiler.passes import SetLayout
8182
from qiskit.transpiler import CouplingMap, Layout
8283
from test import QiskitTestCase # pylint: disable=wrong-import-order
@@ -104,8 +105,15 @@ def create_passmanager(self, coupling_map, initial_layout=None):
104105
if initial_layout:
105106
passmanager.append(SetLayout(Layout(initial_layout)))
106107

107-
# pylint: disable=not-callable
108-
passmanager.append(self.pass_class(CouplingMap(coupling_map), **self.additional_args))
108+
with warnings.catch_warnings():
109+
# TODO: remove this filter when StochasticSwap is removed
110+
warnings.filterwarnings(
111+
"ignore",
112+
category=DeprecationWarning,
113+
message=r".*StochasticSwap.*",
114+
)
115+
# pylint: disable=not-callable
116+
passmanager.append(self.pass_class(CouplingMap(coupling_map), **self.additional_args))
109117
return passmanager
110118

111119
def create_backend(self):

test/python/transpiler/test_preset_passmanagers.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ def test_level1_runs_vf2post_layout_when_routing_required(self):
497497
self.assertNotIn("SabreSwap", self.passes)
498498

499499
def test_level1_runs_vf2post_layout_when_routing_method_set_and_required(self):
500-
"""Test that if we run routing as part of sabre layout VF2PostLayout runs."""
500+
"""Test that if we run routing as part of sabre layout then VF2PostLayout runs."""
501501
target = GenericBackendV2(num_qubits=7, coupling_map=LAGOS_CMAP, seed=42)
502502
qc = QuantumCircuit(5)
503503
qc.h(0)
@@ -507,7 +507,7 @@ def test_level1_runs_vf2post_layout_when_routing_method_set_and_required(self):
507507
qc.cy(0, 4)
508508
qc.measure_all()
509509
_ = transpile(
510-
qc, target, optimization_level=1, routing_method="stochastic", callback=self.callback
510+
qc, target, optimization_level=1, routing_method="sabre", callback=self.callback
511511
)
512512
# Expected call path for layout and routing is:
513513
# 1. TrivialLayout (no perfect match)
@@ -518,7 +518,6 @@ def test_level1_runs_vf2post_layout_when_routing_method_set_and_required(self):
518518
self.assertIn("VF2Layout", self.passes)
519519
self.assertIn("SabreLayout", self.passes)
520520
self.assertIn("VF2PostLayout", self.passes)
521-
self.assertIn("StochasticSwap", self.passes)
522521

523522
def test_level1_not_runs_vf2post_layout_when_layout_method_set(self):
524523
"""Test that if we don't run VF2PostLayout with custom layout_method."""

0 commit comments

Comments
 (0)