1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
+ use crate :: noise:: PauliNoise ;
5
+ use crate :: val:: Value ;
4
6
use num_bigint:: BigUint ;
5
7
use num_complex:: Complex ;
6
8
use quantum_sparse_sim:: QuantumSim ;
7
- use rand:: RngCore ;
9
+ use rand:: { rngs :: StdRng , Rng , RngCore , SeedableRng } ;
8
10
9
- use crate :: val:: Value ;
11
+ #[ cfg( test) ]
12
+ mod noise_tests;
10
13
11
14
/// The trait that must be implemented by a quantum backend, whose functions will be invoked when
12
15
/// quantum intrinsics are called.
@@ -82,7 +85,11 @@ pub trait Backend {
82
85
fn qubit_allocate ( & mut self ) -> usize {
83
86
unimplemented ! ( "qubit_allocate operation" ) ;
84
87
}
85
- fn qubit_release ( & mut self , _q : usize ) {
88
+ /// `false` indicates that the qubit was in a non-zero state before the release,
89
+ /// but should have been in the zero state.
90
+ /// `true` otherwise. This includes the case when the qubit was in
91
+ /// a non-zero state during a noisy simulation, which is allowed.
92
+ fn qubit_release ( & mut self , _q : usize ) -> bool {
86
93
unimplemented ! ( "qubit_release operation" ) ;
87
94
}
88
95
fn qubit_swap_id ( & mut self , _q0 : usize , _q1 : usize ) {
@@ -102,7 +109,14 @@ pub trait Backend {
102
109
103
110
/// Default backend used when targeting sparse simulation.
104
111
pub struct SparseSim {
112
+ /// Noiseless Sparse simulator to be used by this instance.
105
113
pub sim : QuantumSim ,
114
+ /// Pauli noise that is applied after a gate or before a measurement is executed.
115
+ /// Service functions aren't subject to noise.
116
+ pub noise : PauliNoise ,
117
+ /// Random number generator to sample Pauli noise.
118
+ /// Noise is not applied when rng is None.
119
+ pub rng : Option < StdRng > ,
106
120
}
107
121
108
122
impl Default for SparseSim {
@@ -116,132 +130,213 @@ impl SparseSim {
116
130
pub fn new ( ) -> Self {
117
131
Self {
118
132
sim : QuantumSim :: new ( None ) ,
133
+ noise : PauliNoise :: default ( ) ,
134
+ rng : None ,
135
+ }
136
+ }
137
+
138
+ #[ must_use]
139
+ pub fn new_with_noise ( noise : & PauliNoise ) -> Self {
140
+ Self {
141
+ sim : QuantumSim :: new ( None ) ,
142
+ noise : * noise,
143
+ rng : if noise. is_noiseless ( ) {
144
+ None
145
+ } else {
146
+ Some ( StdRng :: from_entropy ( ) )
147
+ } ,
119
148
}
120
149
}
150
+
151
+ #[ must_use]
152
+ fn is_noiseless ( & self ) -> bool {
153
+ self . rng . is_none ( )
154
+ }
155
+
156
+ fn apply_noise ( & mut self , q : usize ) {
157
+ if let Some ( rng) = & mut self . rng {
158
+ let p = rng. gen_range ( 0.0 ..1.0 ) ;
159
+ if p >= self . noise . distribution [ 2 ] {
160
+ // In the most common case we don't apply noise
161
+ } else if p < self . noise . distribution [ 0 ] {
162
+ self . sim . x ( q) ;
163
+ } else if p < self . noise . distribution [ 1 ] {
164
+ self . sim . y ( q) ;
165
+ } else {
166
+ self . sim . z ( q) ;
167
+ }
168
+ }
169
+ // No noise applied if rng is None.
170
+ }
121
171
}
122
172
123
173
impl Backend for SparseSim {
124
174
type ResultType = bool ;
125
175
126
176
fn ccx ( & mut self , ctl0 : usize , ctl1 : usize , q : usize ) {
127
177
self . sim . mcx ( & [ ctl0, ctl1] , q) ;
178
+ self . apply_noise ( ctl0) ;
179
+ self . apply_noise ( ctl1) ;
180
+ self . apply_noise ( q) ;
128
181
}
129
182
130
183
fn cx ( & mut self , ctl : usize , q : usize ) {
131
184
self . sim . mcx ( & [ ctl] , q) ;
185
+ self . apply_noise ( ctl) ;
186
+ self . apply_noise ( q) ;
132
187
}
133
188
134
189
fn cy ( & mut self , ctl : usize , q : usize ) {
135
190
self . sim . mcy ( & [ ctl] , q) ;
191
+ self . apply_noise ( ctl) ;
192
+ self . apply_noise ( q) ;
136
193
}
137
194
138
195
fn cz ( & mut self , ctl : usize , q : usize ) {
139
196
self . sim . mcz ( & [ ctl] , q) ;
197
+ self . apply_noise ( ctl) ;
198
+ self . apply_noise ( q) ;
140
199
}
141
200
142
201
fn h ( & mut self , q : usize ) {
143
202
self . sim . h ( q) ;
203
+ self . apply_noise ( q) ;
144
204
}
145
205
146
206
fn m ( & mut self , q : usize ) -> Self :: ResultType {
207
+ self . apply_noise ( q) ;
147
208
self . sim . measure ( q)
148
209
}
149
210
150
211
fn mresetz ( & mut self , q : usize ) -> Self :: ResultType {
212
+ self . apply_noise ( q) ; // Applying noise before measurement
151
213
let res = self . sim . measure ( q) ;
152
214
if res {
153
215
self . sim . x ( q) ;
154
216
}
217
+ self . apply_noise ( q) ; // Applying noise after reset
155
218
res
156
219
}
157
220
158
221
fn reset ( & mut self , q : usize ) {
159
222
self . mresetz ( q) ;
223
+ // Noise applied in mresetz.
160
224
}
161
225
162
226
fn rx ( & mut self , theta : f64 , q : usize ) {
163
227
self . sim . rx ( theta, q) ;
228
+ self . apply_noise ( q) ;
164
229
}
165
230
166
231
fn rxx ( & mut self , theta : f64 , q0 : usize , q1 : usize ) {
167
- self . h ( q0) ;
168
- self . h ( q1) ;
169
- self . rzz ( theta, q0, q1) ;
170
- self . h ( q1) ;
171
- self . h ( q0) ;
232
+ self . sim . h ( q0) ;
233
+ self . sim . h ( q1) ;
234
+ self . sim . mcx ( & [ q1] , q0) ;
235
+ self . sim . rz ( theta, q0) ;
236
+ self . sim . mcx ( & [ q1] , q0) ;
237
+ self . sim . h ( q1) ;
238
+ self . sim . h ( q0) ;
239
+ self . apply_noise ( q0) ;
240
+ self . apply_noise ( q1) ;
172
241
}
173
242
174
243
fn ry ( & mut self , theta : f64 , q : usize ) {
175
244
self . sim . ry ( theta, q) ;
245
+ self . apply_noise ( q) ;
176
246
}
177
247
178
248
fn ryy ( & mut self , theta : f64 , q0 : usize , q1 : usize ) {
179
- self . h ( q0) ;
180
- self . s ( q0) ;
181
- self . h ( q0) ;
182
- self . h ( q1) ;
183
- self . s ( q1) ;
184
- self . h ( q1) ;
185
- self . rzz ( theta, q0, q1) ;
186
- self . h ( q1) ;
187
- self . sadj ( q1) ;
188
- self . h ( q1) ;
189
- self . h ( q0) ;
190
- self . sadj ( q0) ;
191
- self . h ( q0) ;
249
+ self . sim . h ( q0) ;
250
+ self . sim . s ( q0) ;
251
+ self . sim . h ( q0) ;
252
+ self . sim . h ( q1) ;
253
+ self . sim . s ( q1) ;
254
+ self . sim . h ( q1) ;
255
+ self . sim . mcx ( & [ q1] , q0) ;
256
+ self . sim . rz ( theta, q0) ;
257
+ self . sim . mcx ( & [ q1] , q0) ;
258
+ self . sim . h ( q1) ;
259
+ self . sim . sadj ( q1) ;
260
+ self . sim . h ( q1) ;
261
+ self . sim . h ( q0) ;
262
+ self . sim . sadj ( q0) ;
263
+ self . sim . h ( q0) ;
264
+ self . apply_noise ( q0) ;
265
+ self . apply_noise ( q1) ;
192
266
}
193
267
194
268
fn rz ( & mut self , theta : f64 , q : usize ) {
195
269
self . sim . rz ( theta, q) ;
270
+ self . apply_noise ( q) ;
196
271
}
197
272
198
273
fn rzz ( & mut self , theta : f64 , q0 : usize , q1 : usize ) {
199
- self . cx ( q1, q0) ;
200
- self . rz ( theta, q0) ;
201
- self . cx ( q1, q0) ;
274
+ self . sim . mcx ( & [ q1] , q0) ;
275
+ self . sim . rz ( theta, q0) ;
276
+ self . sim . mcx ( & [ q1] , q0) ;
277
+ self . apply_noise ( q0) ;
278
+ self . apply_noise ( q1) ;
202
279
}
203
280
204
281
fn sadj ( & mut self , q : usize ) {
205
282
self . sim . sadj ( q) ;
283
+ self . apply_noise ( q) ;
206
284
}
207
285
208
286
fn s ( & mut self , q : usize ) {
209
287
self . sim . s ( q) ;
288
+ self . apply_noise ( q) ;
210
289
}
211
290
212
291
fn swap ( & mut self , q0 : usize , q1 : usize ) {
213
292
self . sim . swap_qubit_ids ( q0, q1) ;
293
+ self . apply_noise ( q0) ;
294
+ self . apply_noise ( q1) ;
214
295
}
215
296
216
297
fn tadj ( & mut self , q : usize ) {
217
298
self . sim . tadj ( q) ;
299
+ self . apply_noise ( q) ;
218
300
}
219
301
220
302
fn t ( & mut self , q : usize ) {
221
303
self . sim . t ( q) ;
304
+ self . apply_noise ( q) ;
222
305
}
223
306
224
307
fn x ( & mut self , q : usize ) {
225
308
self . sim . x ( q) ;
309
+ self . apply_noise ( q) ;
226
310
}
227
311
228
312
fn y ( & mut self , q : usize ) {
229
313
self . sim . y ( q) ;
314
+ self . apply_noise ( q) ;
230
315
}
231
316
232
317
fn z ( & mut self , q : usize ) {
233
318
self . sim . z ( q) ;
319
+ self . apply_noise ( q) ;
234
320
}
235
321
236
322
fn qubit_allocate ( & mut self ) -> usize {
323
+ // Fresh qubit start in ground state even with noise.
237
324
self . sim . allocate ( )
238
325
}
239
326
240
- fn qubit_release ( & mut self , q : usize ) {
241
- self . sim . release ( q) ;
327
+ fn qubit_release ( & mut self , q : usize ) -> bool {
328
+ if self . is_noiseless ( ) {
329
+ let was_zero = self . sim . qubit_is_zero ( q) ;
330
+ self . sim . release ( q) ;
331
+ was_zero
332
+ } else {
333
+ self . sim . release ( q) ;
334
+ true
335
+ }
242
336
}
243
337
244
338
fn qubit_swap_id ( & mut self , q0 : usize , q1 : usize ) {
339
+ // This is a service function rather than a gate so it doesn't incur noise.
245
340
self . sim . swap_qubit_ids ( q0, q1) ;
246
341
}
247
342
@@ -266,10 +361,12 @@ impl Backend for SparseSim {
266
361
}
267
362
268
363
fn qubit_is_zero ( & mut self , q : usize ) -> bool {
364
+ // This is a service function rather than a measurement so it doesn't incur noise.
269
365
self . sim . qubit_is_zero ( q)
270
366
}
271
367
272
368
fn custom_intrinsic ( & mut self , name : & str , arg : Value ) -> Option < Result < Value , String > > {
369
+ // These intrinsics aren't subject to noise.
273
370
match name {
274
371
"GlobalPhase" => {
275
372
// Apply a global phase to the simulation by doing an Rz to a fresh qubit.
@@ -301,9 +398,16 @@ impl Backend for SparseSim {
301
398
}
302
399
303
400
fn set_seed ( & mut self , seed : Option < u64 > ) {
304
- match seed {
305
- Some ( seed) => self . sim . set_rng_seed ( seed) ,
306
- None => self . sim . set_rng_seed ( rand:: thread_rng ( ) . next_u64 ( ) ) ,
401
+ if let Some ( seed) = seed {
402
+ if !self . is_noiseless ( ) {
403
+ self . rng = Some ( StdRng :: seed_from_u64 ( seed) ) ;
404
+ }
405
+ self . sim . set_rng_seed ( seed) ;
406
+ } else {
407
+ if !self . is_noiseless ( ) {
408
+ self . rng = Some ( StdRng :: from_entropy ( ) ) ;
409
+ }
410
+ self . sim . set_rng_seed ( rand:: thread_rng ( ) . next_u64 ( ) ) ;
307
411
}
308
412
}
309
413
}
@@ -458,9 +562,9 @@ where
458
562
self . main . qubit_allocate ( )
459
563
}
460
564
461
- fn qubit_release ( & mut self , q : usize ) {
462
- self . chained . qubit_release ( q) ;
463
- self . main . qubit_release ( q) ;
565
+ fn qubit_release ( & mut self , q : usize ) -> bool {
566
+ let _ = self . chained . qubit_release ( q) ;
567
+ self . main . qubit_release ( q)
464
568
}
465
569
466
570
fn qubit_swap_id ( & mut self , q0 : usize , q1 : usize ) {
0 commit comments