Skip to content

Commit 07124bd

Browse files
committed
Merge remote-tracking branch 'origin/main' into partial-layout-interface
2 parents 14a3426 + dbc4506 commit 07124bd

File tree

11 files changed

+1120
-508
lines changed

11 files changed

+1120
-508
lines changed

crates/accelerate/src/sabre_layout.rs

+62-104
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313

1414
use hashbrown::HashSet;
1515
use ndarray::prelude::*;
16-
use numpy::IntoPyArray;
17-
use numpy::PyReadonlyArray2;
16+
use numpy::{IntoPyArray, PyArray, PyReadonlyArray2};
1817
use pyo3::prelude::*;
1918
use pyo3::wrap_pyfunction;
2019
use pyo3::Python;
@@ -42,7 +41,7 @@ pub fn sabre_layout_and_routing(
4241
num_random_trials: usize,
4342
seed: Option<u64>,
4443
mut partial_layouts: Vec<Vec<Option<usize>>>,
45-
) -> ([NLayout; 2], (SwapMap, PyObject, NodeBlockResults)) {
44+
) -> (NLayout, PyObject, (SwapMap, PyObject, NodeBlockResults)) {
4645
let run_in_parallel = getenv_use_multiple_threads();
4746
let mut starting_layouts: Vec<Vec<Option<usize>>> =
4847
(0..num_random_trials).map(|_| vec![]).collect();
@@ -76,7 +75,7 @@ pub fn sabre_layout_and_routing(
7675
),
7776
)
7877
})
79-
.min_by_key(|(index, (_, result))| {
78+
.min_by_key(|(index, (_, _, result))| {
8079
(
8180
result.map.map.values().map(|x| x.len()).sum::<usize>(),
8281
*index,
@@ -101,15 +100,16 @@ pub fn sabre_layout_and_routing(
101100
&starting_layouts[index],
102101
)
103102
})
104-
.min_by_key(|(_, result)| result.map.map.values().map(|x| x.len()).sum::<usize>())
103+
.min_by_key(|(_, _, result)| result.map.map.values().map(|x| x.len()).sum::<usize>())
105104
.unwrap()
106105
};
107106
(
108107
res.0,
108+
PyArray::from_vec(py, res.1).into(),
109109
(
110-
res.1.map,
111-
res.1.node_order.into_pyarray(py).into(),
112-
res.1.node_block_results,
110+
res.2.map,
111+
res.2.node_order.into_pyarray(py).into(),
112+
res.2.node_block_results,
113113
),
114114
)
115115
}
@@ -124,133 +124,91 @@ fn layout_trial(
124124
num_swap_trials: usize,
125125
run_swap_in_parallel: bool,
126126
starting_layout: &[Option<usize>],
127-
) -> ([NLayout; 2], SabreResult) {
128-
// Pick a random initial layout and fully populate ancillas in that layout too
127+
) -> (NLayout, Vec<usize>, SabreResult) {
129128
let num_physical_qubits = distance_matrix.shape()[0];
130129
let mut rng = Pcg64Mcg::seed_from_u64(seed);
131-
let physical_qubits = if !starting_layout.is_empty() {
132-
let used_bits: HashSet<usize> = starting_layout
133-
.iter()
134-
.filter_map(|x| x.as_ref())
135-
.copied()
136-
.collect();
137-
let mut free_bits: Vec<usize> = (0..num_physical_qubits)
138-
.filter(|x| !used_bits.contains(x))
139-
.collect();
140-
free_bits.shuffle(&mut rng);
141-
(0..num_physical_qubits)
142-
.map(|x| match starting_layout.get(x) {
143-
Some(phys) => phys.unwrap_or_else(|| free_bits.pop().unwrap()),
144-
None => free_bits.pop().unwrap(),
145-
})
146-
.collect()
147-
} else {
148-
let mut physical_qubits: Vec<usize> = (0..num_physical_qubits).collect();
149-
physical_qubits.shuffle(&mut rng);
150-
physical_qubits
130+
131+
// Pick a random initial layout including a full ancilla allocation.
132+
let mut initial_layout = {
133+
let physical_qubits = if !starting_layout.is_empty() {
134+
let used_bits: HashSet<usize> = starting_layout
135+
.iter()
136+
.filter_map(|x| x.as_ref())
137+
.copied()
138+
.collect();
139+
let mut free_bits: Vec<usize> = (0..num_physical_qubits)
140+
.filter(|x| !used_bits.contains(x))
141+
.collect();
142+
free_bits.shuffle(&mut rng);
143+
(0..num_physical_qubits)
144+
.map(|x| match starting_layout.get(x) {
145+
Some(phys) => phys.unwrap_or_else(|| free_bits.pop().unwrap()),
146+
None => free_bits.pop().unwrap(),
147+
})
148+
.collect()
149+
} else {
150+
let mut physical_qubits: Vec<usize> = (0..num_physical_qubits).collect();
151+
physical_qubits.shuffle(&mut rng);
152+
physical_qubits
153+
};
154+
NLayout::from_logical_to_physical(physical_qubits)
151155
};
152156

153-
let mut initial_layout = NLayout::from_logical_to_physical(physical_qubits);
154-
let new_dag_fn = |nodes| {
155-
// Because the current implementation of Sabre swap doesn't permute
156-
// the layout when placing control flow ops, there's no need to
157-
// recurse into blocks. We remove them here, but still map control
158-
// flow node IDs to an empty block list so Sabre treats these ops
159-
// as control flow nodes, but doesn't route their blocks.
160-
let node_blocks_empty = dag
157+
// Sabre routing currently enforces that control-flow blocks return to their starting layout,
158+
// which means they don't actually affect any heuristics that affect our layout choice.
159+
let dag_no_control_forward = SabreDAG {
160+
num_qubits: dag.num_qubits,
161+
num_clbits: dag.num_clbits,
162+
dag: dag.dag.clone(),
163+
nodes: dag.nodes.clone(),
164+
first_layer: dag.first_layer.clone(),
165+
node_blocks: dag
161166
.node_blocks
162-
.iter()
163-
.map(|(node_index, _)| (*node_index, Vec::with_capacity(0)));
164-
SabreDAG::new(
165-
dag.num_qubits,
166-
dag.num_clbits,
167-
nodes,
168-
node_blocks_empty.collect(),
169-
)
170-
.unwrap()
167+
.keys()
168+
.map(|index| (*index, Vec::new()))
169+
.collect(),
171170
};
171+
let dag_no_control_reverse = SabreDAG::new(
172+
dag_no_control_forward.num_qubits,
173+
dag_no_control_forward.num_clbits,
174+
dag_no_control_forward.nodes.iter().rev().cloned().collect(),
175+
dag_no_control_forward.node_blocks.clone(),
176+
);
172177

173-
// Create forward and reverse dags (without node blocks).
174-
// Once we've settled on a layout, we recursively apply it to the original
175-
// DAG and its node blocks.
176-
let mut dag_forward: SabreDAG = new_dag_fn(dag.nodes.clone());
177-
let mut dag_reverse: SabreDAG = new_dag_fn(dag.nodes.iter().rev().cloned().collect());
178178
for _iter in 0..max_iterations {
179-
// forward and reverse
180-
for _direction in 0..2 {
181-
let layout_dag = apply_layout(&dag_forward, &initial_layout);
182-
let mut pass_final_layout = NLayout::generate_trivial_layout(num_physical_qubits);
183-
build_swap_map_inner(
179+
for dag in [&dag_no_control_forward, &dag_no_control_reverse] {
180+
let (_result, final_layout) = build_swap_map_inner(
184181
num_physical_qubits,
185-
&layout_dag,
182+
dag,
186183
neighbor_table,
187184
distance_matrix,
188185
heuristic,
189186
Some(seed),
190-
&mut pass_final_layout,
187+
&initial_layout,
191188
num_swap_trials,
192189
Some(run_swap_in_parallel),
193190
);
194-
let final_layout = compose_layout(&initial_layout, &pass_final_layout);
195191
initial_layout = final_layout;
196-
std::mem::swap(&mut dag_forward, &mut dag_reverse);
197192
}
198193
}
199194

200-
// Apply the layout to the original DAG.
201-
let layout_dag = apply_layout(dag, &initial_layout);
202-
203-
let mut final_layout = NLayout::generate_trivial_layout(num_physical_qubits);
204-
let sabre_result = build_swap_map_inner(
195+
let (sabre_result, final_layout) = build_swap_map_inner(
205196
num_physical_qubits,
206-
&layout_dag,
197+
dag,
207198
neighbor_table,
208199
distance_matrix,
209200
heuristic,
210201
Some(seed),
211-
&mut final_layout,
202+
&initial_layout,
212203
num_swap_trials,
213204
Some(run_swap_in_parallel),
214205
);
215-
([initial_layout, final_layout], sabre_result)
216-
}
217-
218-
fn apply_layout(dag: &SabreDAG, layout: &NLayout) -> SabreDAG {
219-
let layout_nodes = dag.nodes.iter().map(|(node_index, qargs, cargs)| {
220-
let new_qargs: Vec<usize> = qargs.iter().map(|n| layout.logic_to_phys[*n]).collect();
221-
(*node_index, new_qargs, cargs.clone())
222-
});
223-
let node_blocks = dag.node_blocks.iter().map(|(node_index, blocks)| {
224-
(
225-
*node_index,
226-
blocks.iter().map(|d| apply_layout(d, layout)).collect(),
227-
)
228-
});
229-
SabreDAG::new(
230-
dag.num_qubits,
231-
dag.num_clbits,
232-
layout_nodes.collect(),
233-
node_blocks.collect(),
234-
)
235-
.unwrap()
236-
}
237-
238-
fn compose_layout(initial_layout: &NLayout, final_layout: &NLayout) -> NLayout {
239-
let logic_to_phys = initial_layout
240-
.logic_to_phys
241-
.iter()
242-
.map(|n| final_layout.logic_to_phys[*n])
243-
.collect();
244-
let phys_to_logic = final_layout
206+
let final_permutation = initial_layout
245207
.phys_to_logic
246208
.iter()
247-
.map(|n| initial_layout.phys_to_logic[*n])
209+
.map(|initial| final_layout.logic_to_phys[*initial])
248210
.collect();
249-
250-
NLayout {
251-
logic_to_phys,
252-
phys_to_logic,
253-
}
211+
(initial_layout, final_permutation, sabre_result)
254212
}
255213

256214
#[pymodule]

0 commit comments

Comments
 (0)