Skip to content

Commit

Permalink
Add more default trials to sabre layout (#13360)
Browse files Browse the repository at this point in the history
* Add more default trials to sabre layout

Right now sabre layout uses n random trials (as specified by the user or
defaulting to num_cpus) and since #12453 one additional trial taking the
qubits of the densest subgraph as a starting point. There are also a
couple of other trivial examples to try which may or may not produce
better results depending on the circuit, a trivial layout and a reverse
trivial layout. In the case of a hardware efficient circuit the trivial
layout will map exactly and would always be picked. When running in a
preset pass manager sabre should never be called in this scenario
because VF2Layout will find the mapping, but the incremental cost of
adding the trial is minimal. Similarly the cost of a reversed trivial
layout (where virtual qubit 0 -> n, 1 -> n - 1, etc.) is trivial and
may in some scenarios produce a better results.

* Update layouts for failing tests

* Add ring layouts for common connectivity graphs

* Add release notes and docs

* Apply suggestions from code review

Co-authored-by: Elena Peña Tapia <[email protected]>

* Fix lint

---------

Co-authored-by: Elena Peña Tapia <[email protected]>
  • Loading branch information
mtreinish and ElePT authored Nov 6, 2024
1 parent c7ccbd4 commit a18ef02
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 3 deletions.
64 changes: 64 additions & 0 deletions crates/accelerate/src/sabre/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,70 @@ pub fn sabre_layout_and_routing(
&target,
run_in_parallel,
));
starting_layouts.push(
(0..target.neighbors.num_qubits() as u32)
.map(Some)
.collect(),
);
starting_layouts.push(
(0..target.neighbors.num_qubits() as u32)
.rev()
.map(Some)
.collect(),
);
// This layout targets the largest ring on an IBM eagle device. It has been
// shown to have good results on some circuits targeting these backends. In
// all other cases this is no different from an additional random trial,
// see: https://xkcd.com/221/
if target.neighbors.num_qubits() == 127 {
starting_layouts.push(
[
0, 1, 2, 3, 4, 5, 6, 15, 22, 23, 24, 25, 34, 43, 42, 41, 40, 53, 60, 59, 61, 62,
72, 81, 80, 79, 78, 91, 98, 99, 100, 101, 102, 103, 92, 83, 82, 84, 85, 86, 73, 66,
65, 64, 63, 54, 45, 44, 46, 47, 35, 28, 29, 27, 26, 16, 7, 8, 9, 10, 11, 12, 13,
17, 30, 31, 32, 36, 51, 50, 49, 48, 55, 68, 67, 69, 70, 74, 89, 88, 87, 93, 106,
105, 104, 107, 108, 112, 126, 125, 124, 123, 122, 111, 121, 120, 119, 118, 110,
117, 116, 115, 114, 113, 109, 96, 97, 95, 94, 90, 75, 76, 77, 71, 58, 57, 56, 52,
37, 38, 39, 33, 20, 21, 19, 18, 14,
]
.into_iter()
.map(Some)
.collect(),
);
} else if target.neighbors.num_qubits() == 133 {
// Same for IBM Heron 133 qubit devices. This is the ring computed by using rustworkx's
// max(simple_cycles(graph), key=len) on the connectivity graph.
starting_layouts.push(
[
108, 107, 94, 88, 89, 90, 75, 71, 70, 69, 56, 50, 51, 52, 37, 33, 32, 31, 18, 12,
11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 36, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 53, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 74, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 91, 95, 96, 97,
110, 116, 117, 118, 119, 120, 111, 101, 102, 103, 104, 105, 112, 124, 125, 126,
127, 128, 113, 109,
]
.into_iter()
.map(Some)
.collect(),
);
} else if target.neighbors.num_qubits() == 156 {
// Same for IBM Heron 156 qubit devices. This is the ring computed by using rustworkx's
// max(simple_cycles(graph), key=len) on the connectivity graph.
starting_layouts.push(
[
136, 123, 122, 121, 116, 101, 102, 103, 96, 83, 82, 81, 76, 61, 62, 63, 56, 43, 42,
41, 36, 21, 22, 23, 16, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 19, 35, 34,
33, 32, 31, 30, 29, 28, 27, 26, 25, 37, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
59, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 77, 85, 86, 87, 88, 89, 90, 91, 92,
93, 94, 95, 99, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 117, 125,
126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 139, 155, 154, 153, 152, 151,
150, 149, 148, 147, 146, 145, 144, 143,
]
.into_iter()
.map(Some)
.collect(),
);
}
let outer_rng = match seed {
Some(seed) => Pcg64Mcg::seed_from_u64(seed),
None => Pcg64Mcg::from_entropy(),
Expand Down
4 changes: 3 additions & 1 deletion qiskit/transpiler/passes/layout/sabre_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ def __init__(
(and ``routing_pass`` is not set) then the number of local
physical CPUs will be used as the default value. This option is
mutually exclusive with the ``routing_pass`` argument and an error
will be raised if both are used.
will be raised if both are used. An additional 3 or 4 trials
depending on the ``coupling_map`` value are run with common layouts
on top of the random trial count specified by this value.
skip_routing (bool): If this is set ``True`` and ``routing_pass`` is not used
then routing will not be applied to the output circuit. Only the layout
will be set in the property set. This is a tradeoff to run custom
Expand Down
11 changes: 11 additions & 0 deletions releasenotes/notes/add-more-sabre-trials-9b421f05d2f48d18.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
features_transpiler:
- |
The :class:`.SabreLayout` transpiler pass has been updated to run an
additional 2 or 3 layout trials by default independently of the
``layout_trials`` keyword argument's value. A trivial
layout and its reverse are included for all backends, just like the :class:`.DenseLayout`
trial that was added in 1.2.0. In addition to this, the largest rings on
an IBM backend heavy hex connectivity graph are added if the backends are 127,
133, or 156 qubits. This can provide a good starting point for some circuits on these commonly run
backends, while for all others it's just an additional "random trial".
4 changes: 2 additions & 2 deletions test/python/transpiler/test_sabre_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def test_layout_with_classical_bits(self):
self.assertIsInstance(res, QuantumCircuit)
layout = res._layout.initial_layout
self.assertEqual(
[layout[q] for q in qc.qubits], [11, 19, 18, 16, 26, 8, 21, 1, 5, 15, 3, 12, 14, 13]
[layout[q] for q in qc.qubits], [2, 0, 5, 1, 7, 3, 14, 6, 9, 8, 10, 11, 4, 12]
)

# pylint: disable=line-too-long
Expand Down Expand Up @@ -271,7 +271,7 @@ def test_layout_many_search_trials(self):
self.assertIsInstance(res, QuantumCircuit)
layout = res._layout.initial_layout
self.assertEqual(
[layout[q] for q in qc.qubits], [22, 7, 2, 12, 1, 5, 14, 4, 11, 0, 16, 15, 3, 10]
[layout[q] for q in qc.qubits], [0, 12, 7, 3, 6, 11, 1, 10, 4, 9, 2, 5, 13, 8]
)

def test_support_var_with_rust_fastpath(self):
Expand Down

0 comments on commit a18ef02

Please sign in to comment.