From 06e208f71b776400e57aef4fd2b04df1bf00f790 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 22 May 2024 09:11:23 -0400 Subject: [PATCH] Add `DenseLayout` trial to `SabreLayout` Building on the work done in #10829, #10721, and #12104 this commit adds a new trial to all runs of `SabreLayout` that runs the dense layout pass. In general the sabre layout algorithm starts from a random layout and then runs a routing algorithm to permute that layout virtually where swaps would be inserted to select a layout that would result in fewer swaps. As was discussed in #10721 and #10829 that random starting point is often not ideal especially for larger targets where the distance between qubits can be quite far. Especially when the circuit qubit count is low relative to the target's qubit count this can result it poor layouts as the distance between the qubits is too large. In qiskit we have an existing pass, `DenseLayout`, which tries to find the most densely connected n qubit subgraph of a connectivity graph. This algorithm necessarily will select a starting layout where the qubits are near each other and for those large backends where the random starting layout doesn't work well this can improve the output quality. As the overhead of `DenseLayout` is quite low and the core algorithm is written in rust already this commit adds a default trial that uses DenseLayout as a starting point on top of the random trials (and any input starting points). For example if the user specifies to run SabreLayout with 20 layout trials this will run 20 random trials and one trial with `DenseLayout` as the starting point. This is all done directly in the sabre layout rust code for efficiency. The other difference between the standalone `DenseLayout` pass is that in the standalone pass a sparse matrix is built and a reverse Cuthill-McKee permutation is run on the densest subgraph qubits to pick the final layout. This permutation is skipped because in the case of Sabre's use of dense layout we're relying on the sabre algorithm to perform the permutation. Depends on: #12104 --- crates/accelerate/src/sabre/layout.rs | 40 +++++++++++++++------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/crates/accelerate/src/sabre/layout.rs b/crates/accelerate/src/sabre/layout.rs index 36db7e8cbd73..a1e5e9ce6418 100644 --- a/crates/accelerate/src/sabre/layout.rs +++ b/crates/accelerate/src/sabre/layout.rs @@ -15,8 +15,8 @@ use pyo3::prelude::*; use pyo3::Python; use hashbrown::HashSet; -use numpy::{IntoPyArray, PyArray, PyReadonlyArray2}; use ndarray::prelude::*; +use numpy::{IntoPyArray, PyArray, PyReadonlyArray2}; use rand::prelude::*; use rand_pcg::Pcg64Mcg; use rayon::prelude::*; @@ -56,7 +56,11 @@ pub fn sabre_layout_and_routing( (0..num_random_trials).map(|_| vec![]).collect(); starting_layouts.append(&mut partial_layouts); // Run a dense layout trial - starting_layouts.push(compute_dense_starting_layout(dag.num_qubits, &target, run_in_parallel)); + starting_layouts.push(compute_dense_starting_layout( + dag.num_qubits, + &target, + run_in_parallel, + )); let outer_rng = match seed { Some(seed) => Pcg64Mcg::seed_from_u64(seed), None => Pcg64Mcg::from_entropy(), @@ -214,25 +218,25 @@ fn layout_trial( (initial_layout, final_permutation, sabre_result) } -fn compute_dense_starting_layout(num_qubits: usize, target: &RoutingTargetView, run_in_parallel: bool) -> Vec> { +fn compute_dense_starting_layout( + num_qubits: usize, + target: &RoutingTargetView, + run_in_parallel: bool, +) -> Vec> { let mut adj_matrix = target.distance.to_owned(); if run_in_parallel { - adj_matrix.par_mapv_inplace(|x| { - if x == 1. { - 1. - } else { - 0. - } - }); + adj_matrix.par_mapv_inplace(|x| if x == 1. { 1. } else { 0. }); } else { - adj_matrix.mapv_inplace(|x| { - if x == 1. { - 1. - } else { - 0. - } - }); + adj_matrix.mapv_inplace(|x| if x == 1. { 1. } else { 0. }); } - let [_rows, _cols, map] = best_subset_inner(num_qubits, adj_matrix.view(), 0, 0, false, true, aview2(&[[0.]])); + let [_rows, _cols, map] = best_subset_inner( + num_qubits, + adj_matrix.view(), + 0, + 0, + false, + true, + aview2(&[[0.]]), + ); map.into_iter().map(|x| Some(x as u32)).collect() }