Skip to content

Commit

Permalink
Add swap_trials argument to SabreLayout
Browse files Browse the repository at this point in the history
This commit adds a swap_trials argument to the SabreLayout pass so that
users can control how many trials to run in SabreSwap internally. This
is necessary for reproducibility between systems for the same reason
it's required on SabreSwap.
  • Loading branch information
mtreinish committed Aug 22, 2022
1 parent 90d52cc commit 7b60168
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 8 deletions.
28 changes: 26 additions & 2 deletions qiskit/transpiler/passes/layout/sabre_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,42 @@ class SabreLayout(AnalysisPass):
`arXiv:1809.02573 <https://arxiv.org/pdf/1809.02573.pdf>`_
"""

def __init__(self, coupling_map, routing_pass=None, seed=None, max_iterations=3):
def __init__(
self, coupling_map, routing_pass=None, seed=None, max_iterations=3, swap_trials=None
):
"""SabreLayout initializer.
Args:
coupling_map (Coupling): directed graph representing a coupling map.
routing_pass (BasePass): the routing pass to use while iterating.
This is mutually exclusive with the ``swap_trials`` argument and
if both are set an error will be raised.
seed (int): seed for setting a random first trial layout.
max_iterations (int): number of forward-backward iterations.
swap_trials (int): The number of trials to run of
:class:`~.SabreSwap` for each iteration. This is equivalent to
the ``trials`` argument on :class:`~.SabreSwap`. If this is not
specified (and ``routing_pass`` isn't set) by default the number
of physical CPUs on your local system will be used. For
reproducibility between environments it is best to set this
to an explicit number because the output will potentially depend
on the number of trials run. This option is mutually exclusive
with the ``routing_pass`` argument and an error will be raised
if both are used.
Raises:
TranspilerError: If both ``routing_pass`` and ``swap_trials`` are
specified
"""
super().__init__()
self.coupling_map = coupling_map
if routing_pass is not None and swap_trials is not None:
raise TranspilerError("Both routing_pass and swap_trials can't be set at the same time")
self.routing_pass = routing_pass
self.seed = seed
self.max_iterations = max_iterations
self.trials = swap_trials
self.swap_trials = swap_trials

def run(self, dag):
"""Run the SabreLayout pass on `dag`.
Expand All @@ -86,7 +108,9 @@ def run(self, dag):
initial_layout = Layout({q: dag.qubits[i] for i, q in enumerate(physical_qubits)})

if self.routing_pass is None:
self.routing_pass = SabreSwap(self.coupling_map, "decay", seed=self.seed, fake_run=True)
self.routing_pass = SabreSwap(
self.coupling_map, "decay", seed=self.seed, fake_run=True, trials=self.swap_trials
)
else:
self.routing_pass.fake_run = True

Expand Down
6 changes: 4 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ def _choose_layout_condition(property_set):
elif layout_method == "noise_adaptive":
_choose_layout = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
_choose_layout = SabreLayout(coupling_map, max_iterations=1, seed=seed_transpiler)
_choose_layout = SabreLayout(
coupling_map, max_iterations=1, seed=seed_transpiler, swap_trials=5
)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

Expand All @@ -97,7 +99,7 @@ def _choose_layout_condition(property_set):
elif routing_method == "lookahead":
routing_pass = LookaheadSwap(coupling_map, search_depth=2, search_width=2)
elif routing_method == "sabre":
routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler)
routing_pass = SabreSwap(coupling_map, heuristic="basic", seed=seed_transpiler, trials=5)
elif routing_method == "toqm":
HAS_TOQM.require_now("TOQM-based routing")
from qiskit_toqm import ToqmSwap, ToqmStrategyO0, latencies_from_target
Expand Down
4 changes: 3 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ def _vf2_match_not_found(property_set):
elif layout_method == "noise_adaptive":
_improve_layout = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
_improve_layout = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler)
_improve_layout = SabreLayout(
coupling_map, max_iterations=2, seed=seed_transpiler, swap_trials=5
)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

Expand Down
4 changes: 3 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ def _vf2_match_not_found(property_set):
elif layout_method == "noise_adaptive":
_choose_layout_1 = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
_choose_layout_1 = SabreLayout(coupling_map, max_iterations=2, seed=seed_transpiler)
_choose_layout_1 = SabreLayout(
coupling_map, max_iterations=2, seed=seed_transpiler, swap_trials=10
)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

Expand Down
4 changes: 3 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ def _vf2_match_not_found(property_set):
elif layout_method == "noise_adaptive":
_choose_layout_1 = NoiseAdaptiveLayout(backend_properties)
elif layout_method == "sabre":
_choose_layout_1 = SabreLayout(coupling_map, max_iterations=4, seed=seed_transpiler)
_choose_layout_1 = SabreLayout(
coupling_map, max_iterations=4, seed=seed_transpiler, swap_trials=20
)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ features:
now run the algorithm with ``trials`` number of random seeds and pick the
best (with the fewest swaps inserted). If ``trials`` is not specified the
pass will default to use the number of physical CPUs on the local system.
- |
The :class:`~.SabreLayout` transpiler pass has a new keyword argument on
its constructor, ``swap_trials``. The ``swap_trials`` argument is used
to specify how many random seed trials to run on the :class:`~.SabreSwap`
pass internally. It corresponds to the ``trials`` arugment on the
:class:`~.SabreSwap` pass. When set For each iteration of
:class:`~.SabreLayout` :class:`~.SabreSwap` will be run internally with
``swap_trials``random seed trials. If ``swap_trials`` is not specified
the will default to use the number of physical CPUs on the local system.
2 changes: 1 addition & 1 deletion test/python/transpiler/test_sabre_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_5q_circuit_20q_coupling(self):
circuit.cx(qr[1], qr[2])

dag = circuit_to_dag(circuit)
pass_ = SabreLayout(CouplingMap(self.cmap20), seed=0)
pass_ = SabreLayout(CouplingMap(self.cmap20), seed=0, swap_trials=32)
pass_.run(dag)

layout = pass_.property_set["layout"]
Expand Down

0 comments on commit 7b60168

Please sign in to comment.