@@ -155,7 +155,7 @@ fn circuit_kak(
155
155
// NOTE: The following normalization is safe, because the gphase correction below
156
156
// fixes a particular diagonal entry to 1, which prevents any potential phase
157
157
// slippage coming from _mod_2pi injecting multiples of 2pi.
158
- lam = mod_2pi ( lam) ;
158
+ lam = mod_2pi ( lam, atol ) ;
159
159
if lam. abs ( ) > atol {
160
160
circuit. push ( ( String :: from ( k_gate) , vec ! [ lam] ) ) ;
161
161
global_phase += lam / 2. ;
@@ -170,18 +170,18 @@ fn circuit_kak(
170
170
lam -= phi;
171
171
phi = 0. ;
172
172
}
173
- if mod_2pi ( lam + PI ) . abs ( ) < atol || mod_2pi ( phi + PI ) . abs ( ) < atol {
173
+ if mod_2pi ( lam + PI , atol ) . abs ( ) < atol || mod_2pi ( phi + PI , atol ) . abs ( ) < atol {
174
174
lam += PI ;
175
175
theta = -theta;
176
176
phi += PI ;
177
177
}
178
- lam = mod_2pi ( lam) ;
178
+ lam = mod_2pi ( lam, atol ) ;
179
179
if lam. abs ( ) > atol {
180
180
global_phase += lam / 2. ;
181
181
circuit. push ( ( String :: from ( k_gate) , vec ! [ lam] ) ) ;
182
182
}
183
183
circuit. push ( ( String :: from ( a_gate) , vec ! [ theta] ) ) ;
184
- phi = mod_2pi ( phi) ;
184
+ phi = mod_2pi ( phi, atol ) ;
185
185
if phi. abs ( ) > atol {
186
186
global_phase += phi / 2. ;
187
187
circuit. push ( ( String :: from ( k_gate) , vec ! [ phi] ) ) ;
@@ -201,12 +201,12 @@ fn circuit_u3(
201
201
atol : Option < f64 > ,
202
202
) -> OneQubitGateSequence {
203
203
let mut circuit = Vec :: new ( ) ;
204
- let phi = mod_2pi ( phi) ;
205
- let lam = mod_2pi ( lam) ;
206
204
let atol = match atol {
207
205
Some ( atol) => atol,
208
206
None => DEFAULT_ATOL ,
209
207
} ;
208
+ let phi = mod_2pi ( phi, atol) ;
209
+ let lam = mod_2pi ( lam, atol) ;
210
210
if !simplify || theta. abs ( ) > atol || phi. abs ( ) > atol || lam. abs ( ) > atol {
211
211
circuit. push ( ( String :: from ( "u3" ) , vec ! [ theta, phi, lam] ) ) ;
212
212
}
@@ -233,14 +233,20 @@ fn circuit_u321(
233
233
atol = -1.0 ;
234
234
}
235
235
if theta. abs ( ) < atol {
236
- let tot = mod_2pi ( phi + lam) ;
236
+ let tot = mod_2pi ( phi + lam, atol ) ;
237
237
if tot. abs ( ) > atol {
238
238
circuit. push ( ( String :: from ( "u1" ) , vec ! [ tot] ) ) ;
239
239
}
240
240
} else if ( theta - PI / 2. ) . abs ( ) < atol {
241
- circuit. push ( ( String :: from ( "u2" ) , vec ! [ mod_2pi( phi) , mod_2pi( lam) ] ) ) ;
241
+ circuit. push ( (
242
+ String :: from ( "u2" ) ,
243
+ vec ! [ mod_2pi( phi, atol) , mod_2pi( lam, atol) ] ,
244
+ ) ) ;
242
245
} else {
243
- circuit. push ( ( String :: from ( "u3" ) , vec ! [ theta, mod_2pi( phi) , mod_2pi( lam) ] ) ) ;
246
+ circuit. push ( (
247
+ String :: from ( "u3" ) ,
248
+ vec ! [ theta, mod_2pi( phi, atol) , mod_2pi( lam, atol) ] ,
249
+ ) ) ;
244
250
}
245
251
OneQubitGateSequence {
246
252
gates : circuit,
@@ -264,8 +270,8 @@ fn circuit_u(
264
270
if !simplify {
265
271
atol = -1.0 ;
266
272
}
267
- let phi = mod_2pi ( phi) ;
268
- let lam = mod_2pi ( lam) ;
273
+ let phi = mod_2pi ( phi, atol ) ;
274
+ let lam = mod_2pi ( lam, atol ) ;
269
275
if theta. abs ( ) > atol || phi. abs ( ) > atol || lam. abs ( ) > atol {
270
276
circuit. push ( ( String :: from ( "u" ) , vec ! [ theta, phi, lam] ) ) ;
271
277
}
@@ -323,7 +329,7 @@ where
323
329
phi -= lam;
324
330
lam = 0. ;
325
331
}
326
- if mod_2pi ( lam + PI ) . abs ( ) < atol || mod_2pi ( phi) . abs ( ) < atol {
332
+ if mod_2pi ( lam + PI , atol ) . abs ( ) < atol || mod_2pi ( phi, atol ) . abs ( ) < atol {
327
333
lam += PI ;
328
334
theta = -theta;
329
335
phi += PI ;
@@ -338,7 +344,7 @@ where
338
344
// emit circuit
339
345
pfun ( & mut circuit, lam) ;
340
346
match xpifun {
341
- Some ( xpifun) if mod_2pi ( theta) . abs ( ) < atol => xpifun ( & mut circuit) ,
347
+ Some ( xpifun) if mod_2pi ( theta, atol ) . abs ( ) < atol => xpifun ( & mut circuit) ,
342
348
_ => {
343
349
xfun ( & mut circuit) ;
344
350
pfun ( & mut circuit, theta) ;
@@ -372,9 +378,15 @@ fn circuit_rr(
372
378
} ;
373
379
}
374
380
if ( theta - PI ) . abs ( ) > atol {
375
- circuit. push ( ( String :: from ( "r" ) , vec ! [ theta - PI , mod_2pi( PI / 2. - lam) ] ) ) ;
376
- }
377
- circuit. push ( ( String :: from ( "r" ) , vec ! [ PI , mod_2pi( 0.5 * ( phi - lam + PI ) ) ] ) ) ;
381
+ circuit. push ( (
382
+ String :: from ( "r" ) ,
383
+ vec ! [ theta - PI , mod_2pi( PI / 2. - lam, atol) ] ,
384
+ ) ) ;
385
+ }
386
+ circuit. push ( (
387
+ String :: from ( "r" ) ,
388
+ vec ! [ PI , mod_2pi( 0.5 * ( phi - lam + PI ) , atol) ] ,
389
+ ) ) ;
378
390
OneQubitGateSequence {
379
391
gates : circuit,
380
392
global_phase : phase,
@@ -408,7 +420,7 @@ pub fn generate_circuit(
408
420
inner_atol = -1.0 ;
409
421
}
410
422
let fnz = |circuit : & mut OneQubitGateSequence , phi : f64 | {
411
- let phi = mod_2pi ( phi) ;
423
+ let phi = mod_2pi ( phi, inner_atol ) ;
412
424
if phi. abs ( ) > inner_atol {
413
425
circuit. gates . push ( ( String :: from ( "p" ) , vec ! [ phi] ) ) ;
414
426
}
@@ -438,7 +450,7 @@ pub fn generate_circuit(
438
450
inner_atol = -1.0 ;
439
451
}
440
452
let fnz = |circuit : & mut OneQubitGateSequence , phi : f64 | {
441
- let phi = mod_2pi ( phi) ;
453
+ let phi = mod_2pi ( phi, inner_atol ) ;
442
454
if phi. abs ( ) > inner_atol {
443
455
circuit. gates . push ( ( String :: from ( "rz" ) , vec ! [ phi] ) ) ;
444
456
circuit. global_phase += phi / 2. ;
@@ -468,7 +480,7 @@ pub fn generate_circuit(
468
480
inner_atol = -1.0 ;
469
481
}
470
482
let fnz = |circuit : & mut OneQubitGateSequence , phi : f64 | {
471
- let phi = mod_2pi ( phi) ;
483
+ let phi = mod_2pi ( phi, inner_atol ) ;
472
484
if phi. abs ( ) > inner_atol {
473
485
circuit. gates . push ( ( String :: from ( "u1" ) , vec ! [ phi] ) ) ;
474
486
}
@@ -498,7 +510,7 @@ pub fn generate_circuit(
498
510
inner_atol = -1.0 ;
499
511
}
500
512
let fnz = |circuit : & mut OneQubitGateSequence , phi : f64 | {
501
- let phi = mod_2pi ( phi) ;
513
+ let phi = mod_2pi ( phi, inner_atol ) ;
502
514
if phi. abs ( ) > inner_atol {
503
515
circuit. gates . push ( ( String :: from ( "rz" ) , vec ! [ phi] ) ) ;
504
516
circuit. global_phase += phi / 2. ;
@@ -608,11 +620,14 @@ pub fn compute_error_list(
608
620
}
609
621
610
622
#[ pyfunction]
623
+ #[ pyo3( signature = ( unitary, target_basis_list, qubit, error_map=None , simplify=true , atol=None ) ) ]
611
624
pub fn unitary_to_gate_sequence (
612
625
unitary : PyReadonlyArray2 < Complex64 > ,
613
626
target_basis_list : Vec < & str > ,
614
627
qubit : usize ,
615
628
error_map : Option < & OneQubitGateErrorMap > ,
629
+ simplify : bool ,
630
+ atol : Option < f64 > ,
616
631
) -> PyResult < Option < OneQubitGateSequence > > {
617
632
const VALID_BASES : [ & str ; 12 ] = [
618
633
"U321" , "U3" , "U" , "PSX" , "ZSX" , "ZSXX" , "U1X" , "RR" , "ZYZ" , "ZXZ" , "XYX" , "XZX" ,
@@ -629,7 +644,7 @@ pub fn unitary_to_gate_sequence(
629
644
. iter ( )
630
645
. map ( |target_basis| {
631
646
let [ theta, phi, lam, phase] = angles_from_unitary ( unitary_mat, target_basis) ;
632
- generate_circuit ( target_basis, theta, phi, lam, phase, true , None ) . unwrap ( )
647
+ generate_circuit ( target_basis, theta, phi, lam, phase, simplify , atol ) . unwrap ( )
633
648
} )
634
649
. min_by ( |a, b| {
635
650
let error_a = compare_error_fn ( a, & error_map, qubit) ;
@@ -649,12 +664,18 @@ fn complex_phase(x: Complex64) -> f64 {
649
664
x. im . atan2 ( x. re )
650
665
}
651
666
667
+ /// Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π
652
668
#[ inline]
653
- fn mod_2pi ( angle : f64 ) -> f64 {
669
+ fn mod_2pi ( angle : f64 , atol : f64 ) -> f64 {
654
670
// f64::rem_euclid() isn't exactly the same as Python's % operator, but because
655
671
// the RHS here is a constant and positive it is effectively equivalent for
656
672
// this case
657
- ( angle + PI ) . rem_euclid ( 2. * PI ) - PI
673
+ let wrapped = ( angle + PI ) . rem_euclid ( 2. * PI ) - PI ;
674
+ if ( wrapped - PI ) . abs ( ) < atol {
675
+ -PI
676
+ } else {
677
+ wrapped
678
+ }
658
679
}
659
680
660
681
fn params_zyz_inner ( mat : ArrayView2 < Complex64 > ) -> [ f64 ; 4 ] {
@@ -722,8 +743,8 @@ fn params_xyx_inner(mat: ArrayView2<Complex64>) -> [f64; 4] {
722
743
] ,
723
744
] ) ;
724
745
let [ theta, phi, lam, phase] = params_zyz_inner ( mat_zyz. view ( ) ) ;
725
- let new_phi = mod_2pi ( phi + PI ) ;
726
- let new_lam = mod_2pi ( lam + PI ) ;
746
+ let new_phi = mod_2pi ( phi + PI , 0. ) ;
747
+ let new_lam = mod_2pi ( lam + PI , 0. ) ;
727
748
[
728
749
theta,
729
750
new_phi,
0 commit comments