13
13
14
14
use hashbrown:: HashSet ;
15
15
use ndarray:: prelude:: * ;
16
- use numpy:: IntoPyArray ;
17
- use numpy:: PyReadonlyArray2 ;
16
+ use numpy:: { IntoPyArray , PyArray , PyReadonlyArray2 } ;
18
17
use pyo3:: prelude:: * ;
19
18
use pyo3:: wrap_pyfunction;
20
19
use pyo3:: Python ;
@@ -42,7 +41,7 @@ pub fn sabre_layout_and_routing(
42
41
num_random_trials : usize ,
43
42
seed : Option < u64 > ,
44
43
mut partial_layouts : Vec < Vec < Option < usize > > > ,
45
- ) -> ( [ NLayout ; 2 ] , ( SwapMap , PyObject , NodeBlockResults ) ) {
44
+ ) -> ( NLayout , PyObject , ( SwapMap , PyObject , NodeBlockResults ) ) {
46
45
let run_in_parallel = getenv_use_multiple_threads ( ) ;
47
46
let mut starting_layouts: Vec < Vec < Option < usize > > > =
48
47
( 0 ..num_random_trials) . map ( |_| vec ! [ ] ) . collect ( ) ;
@@ -76,7 +75,7 @@ pub fn sabre_layout_and_routing(
76
75
) ,
77
76
)
78
77
} )
79
- . min_by_key ( |( index, ( _, result) ) | {
78
+ . min_by_key ( |( index, ( _, _ , result) ) | {
80
79
(
81
80
result. map . map . values ( ) . map ( |x| x. len ( ) ) . sum :: < usize > ( ) ,
82
81
* index,
@@ -101,15 +100,16 @@ pub fn sabre_layout_and_routing(
101
100
& starting_layouts[ index] ,
102
101
)
103
102
} )
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 > ( ) )
105
104
. unwrap ( )
106
105
} ;
107
106
(
108
107
res. 0 ,
108
+ PyArray :: from_vec ( py, res. 1 ) . into ( ) ,
109
109
(
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 ,
113
113
) ,
114
114
)
115
115
}
@@ -124,133 +124,91 @@ fn layout_trial(
124
124
num_swap_trials : usize ,
125
125
run_swap_in_parallel : bool ,
126
126
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 ) {
129
128
let num_physical_qubits = distance_matrix. shape ( ) [ 0 ] ;
130
129
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)
151
155
} ;
152
156
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
161
166
. 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 ( ) ,
171
170
} ;
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
+ ) ;
172
177
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 ( ) ) ;
178
178
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 (
184
181
num_physical_qubits,
185
- & layout_dag ,
182
+ dag ,
186
183
neighbor_table,
187
184
distance_matrix,
188
185
heuristic,
189
186
Some ( seed) ,
190
- & mut pass_final_layout ,
187
+ & initial_layout ,
191
188
num_swap_trials,
192
189
Some ( run_swap_in_parallel) ,
193
190
) ;
194
- let final_layout = compose_layout ( & initial_layout, & pass_final_layout) ;
195
191
initial_layout = final_layout;
196
- std:: mem:: swap ( & mut dag_forward, & mut dag_reverse) ;
197
192
}
198
193
}
199
194
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 (
205
196
num_physical_qubits,
206
- & layout_dag ,
197
+ dag ,
207
198
neighbor_table,
208
199
distance_matrix,
209
200
heuristic,
210
201
Some ( seed) ,
211
- & mut final_layout ,
202
+ & initial_layout ,
212
203
num_swap_trials,
213
204
Some ( run_swap_in_parallel) ,
214
205
) ;
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
245
207
. phys_to_logic
246
208
. iter ( )
247
- . map ( |n| initial_layout . phys_to_logic [ * n ] )
209
+ . map ( |initial| final_layout . logic_to_phys [ * initial ] )
248
210
. collect ( ) ;
249
-
250
- NLayout {
251
- logic_to_phys,
252
- phys_to_logic,
253
- }
211
+ ( initial_layout, final_permutation, sabre_result)
254
212
}
255
213
256
214
#[ pymodule]
0 commit comments