diff --git a/crates/accelerate/src/sabre/layout.rs b/crates/accelerate/src/sabre/layout.rs index 5ea568559119..9ab67c3fcfbc 100644 --- a/crates/accelerate/src/sabre/layout.rs +++ b/crates/accelerate/src/sabre/layout.rs @@ -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(), diff --git a/qiskit/transpiler/passes/layout/sabre_layout.py b/qiskit/transpiler/passes/layout/sabre_layout.py index 78af67ad9118..af17cc226cb7 100644 --- a/qiskit/transpiler/passes/layout/sabre_layout.py +++ b/qiskit/transpiler/passes/layout/sabre_layout.py @@ -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 diff --git a/releasenotes/notes/add-more-sabre-trials-9b421f05d2f48d18.yaml b/releasenotes/notes/add-more-sabre-trials-9b421f05d2f48d18.yaml new file mode 100644 index 000000000000..c91244c944d6 --- /dev/null +++ b/releasenotes/notes/add-more-sabre-trials-9b421f05d2f48d18.yaml @@ -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". diff --git a/test/python/transpiler/test_sabre_layout.py b/test/python/transpiler/test_sabre_layout.py index 7565ee17655f..2942cab30cdd 100644 --- a/test/python/transpiler/test_sabre_layout.py +++ b/test/python/transpiler/test_sabre_layout.py @@ -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 @@ -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):