Skip to content

Commit 3a1dc6e

Browse files
authored
Organize two_qubit_decompose code (#15880)
* organize two_qubit_decompose code * move back consts and functions from common. add comments * minor change order
1 parent eb47a1f commit 3a1dc6e

4 files changed

Lines changed: 99 additions & 102 deletions

File tree

crates/synthesis/src/two_qubit_decompose/basis_decomposer.rs

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use approx::{abs_diff_eq, relative_eq};
1414
use num_complex::{Complex64, ComplexFloat};
1515
use num_traits::Zero;
1616
use smallvec::{SmallVec, smallvec};
17-
use std::f64::consts::{FRAC_1_SQRT_2, PI};
17+
use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, PI, TAU};
1818

1919
use ndarray::linalg::kron;
2020
use ndarray::prelude::*;
@@ -24,7 +24,9 @@ use numpy::PyReadonlyArray2;
2424
use pyo3::exceptions::PyValueError;
2525
use pyo3::prelude::*;
2626

27-
use super::common::{DEFAULT_FIDELITY, TraceToFidelity, rx_matrix, rz_matrix, transpose_conjugate};
27+
use super::common::{
28+
DEFAULT_FIDELITY, IPZ, TraceToFidelity, rx_matrix, rz_matrix, transpose_conjugate,
29+
};
2830
use super::gate_sequence::{TwoQubitGateSequence, TwoQubitSequenceVec};
2931
use super::weyl_decomposition::{__num_basis_gates, _num_basis_gates, TwoQubitWeylDecomposition};
3032

@@ -46,11 +48,8 @@ use qiskit_circuit::operations::{Operation, OperationRef, Param, StandardGate};
4648
use qiskit_circuit::packed_instruction::PackedOperation;
4749
use qiskit_circuit::{NoBlocks, Qubit};
4850
use qiskit_util::alias::GateArray1Q;
49-
use qiskit_util::complex::{C_M_ONE, C_ONE, C_ZERO, IM, M_IM, c64};
51+
use qiskit_util::complex::{C_M_ONE, C_ONE, IM, M_IM, c64};
5052

51-
const PI2: f64 = PI / 2.;
52-
const PI4: f64 = PI / 4.;
53-
const TWO_PI: f64 = 2.0 * PI;
5453
// Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q gate
5554
// We might overallocate a bit if the euler basis is different but
5655
// the worst case is just 16 extra elements with just a String and 2 smallvecs
@@ -59,7 +58,16 @@ const TWO_PI: f64 = 2.0 * PI;
5958
// Python space.
6059
const TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY: usize = 21;
6160

62-
static IPZ: GateArray1Q = [[IM, C_ZERO], [C_ZERO, M_IM]];
61+
// constant matrices
62+
static K12R_ARR: GateArray1Q = [
63+
[c64(0., FRAC_1_SQRT_2), c64(FRAC_1_SQRT_2, 0.)],
64+
[c64(-FRAC_1_SQRT_2, 0.), c64(0., -FRAC_1_SQRT_2)],
65+
];
66+
67+
static K12L_ARR: GateArray1Q = [
68+
[c64(0.5, 0.5), c64(0.5, 0.5)],
69+
[c64(-0.5, 0.5), c64(0.5, -0.5)],
70+
];
6371

6472
#[derive(Clone, Debug)]
6573
#[allow(non_snake_case)]
@@ -193,7 +201,8 @@ impl TwoQubitBasisDecomposer {
193201
})
194202
.collect();
195203
let mut euler_matrix_q0 = rx_matrix(euler_q0[0][1]).dot(&rz_matrix(euler_q0[0][0]));
196-
euler_matrix_q0 = rz_matrix(euler_q0[0][2] + euler_q0[1][0] + PI2).dot(&euler_matrix_q0);
204+
euler_matrix_q0 =
205+
rz_matrix(euler_q0[0][2] + euler_q0[1][0] + FRAC_PI_2).dot(&euler_matrix_q0);
197206
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q0.view(), 0);
198207
let mut euler_matrix_q1 = rz_matrix(euler_q1[0][1]).dot(&rx_matrix(euler_q1[0][0]));
199208
euler_matrix_q1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]).dot(&euler_matrix_q1);
@@ -220,10 +229,10 @@ impl TwoQubitBasisDecomposer {
220229
smallvec![euler_q1[1][1]],
221230
smallvec![1],
222231
));
223-
global_phase += PI2;
232+
global_phase += FRAC_PI_2;
224233
gates.push((StandardGate::CX.into(), smallvec![], smallvec![0, 1]));
225234
let mut euler_matrix_q0 =
226-
rx_matrix(euler_q0[2][1]).dot(&rz_matrix(euler_q0[1][2] + euler_q0[2][0] + PI2));
235+
rx_matrix(euler_q0[2][1]).dot(&rz_matrix(euler_q0[1][2] + euler_q0[2][0] + FRAC_PI_2));
227236
euler_matrix_q0 = rz_matrix(euler_q0[2][2]).dot(&euler_matrix_q0);
228237
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q0.view(), 0);
229238
let mut euler_matrix_q1 =
@@ -253,7 +262,7 @@ impl TwoQubitBasisDecomposer {
253262
let mut gates = Vec::new();
254263
let mut global_phase = target_decomposed.global_phase;
255264
global_phase -= 3. * self.basis_decomposer.global_phase;
256-
global_phase = global_phase.rem_euclid(TWO_PI);
265+
global_phase = global_phase.rem_euclid(TAU);
257266
let atol = 1e-10; // absolute tolerance for floats
258267
// Decompose source unitaries to zxz
259268
let euler_q0: Vec<[f64; 3]> = decomposition
@@ -287,7 +296,7 @@ impl TwoQubitBasisDecomposer {
287296
x12_phase = PI * x12.cos();
288297
}
289298
let x02_add = x12 - euler_q0[1][0];
290-
let x12_is_half_pi = abs_diff_eq!(x12, PI2, epsilon = atol);
299+
let x12_is_half_pi = abs_diff_eq!(x12, FRAC_PI_2, epsilon = atol);
291300

292301
let mut euler_matrix_q0 = rx_matrix(euler_q0[0][1]).dot(&rz_matrix(euler_q0[0][0]));
293302
if x12_is_non_zero && x12_is_pi_mult {
@@ -330,17 +339,17 @@ impl TwoQubitBasisDecomposer {
330339
}
331340
if x12_is_half_pi {
332341
gates.push((StandardGate::SX.into(), smallvec![], smallvec![0]));
333-
global_phase -= PI4;
342+
global_phase -= FRAC_PI_4;
334343
} else if x12_is_non_zero && !x12_is_pi_mult {
335344
if self.pulse_optimize.is_none() {
336345
self.append_1q_sequence(&mut gates, &mut global_phase, rx_matrix(x12).view(), 0);
337346
} else {
338347
return None;
339348
}
340349
}
341-
if abs_diff_eq!(euler_q1[1][1], PI2, epsilon = atol) {
350+
if abs_diff_eq!(euler_q1[1][1], FRAC_PI_2, epsilon = atol) {
342351
gates.push((StandardGate::SX.into(), smallvec![], smallvec![1]));
343-
global_phase -= PI4
352+
global_phase -= FRAC_PI_4
344353
} else if self.pulse_optimize.is_none() {
345354
self.append_1q_sequence(
346355
&mut gates,
@@ -362,9 +371,9 @@ impl TwoQubitBasisDecomposer {
362371
smallvec![euler_q0[2][1]],
363372
smallvec![0],
364373
));
365-
if abs_diff_eq!(euler_q1[2][1], PI2, epsilon = atol) {
374+
if abs_diff_eq!(euler_q1[2][1], FRAC_PI_2, epsilon = atol) {
366375
gates.push((StandardGate::SX.into(), smallvec![], smallvec![1]));
367-
global_phase -= PI4;
376+
global_phase -= FRAC_PI_4;
368377
} else if self.pulse_optimize.is_none() {
369378
self.append_1q_sequence(
370379
&mut gates,
@@ -492,7 +501,7 @@ impl TwoQubitBasisDecomposer {
492501
let ipz: ArrayView2<Complex64> = aview2(&IPZ);
493502
let basis_decomposer =
494503
TwoQubitWeylDecomposition::new_inner(gate_matrix, Some(DEFAULT_FIDELITY), None)?;
495-
let super_controlled = relative_eq!(basis_decomposer.a, PI4, max_relative = 1e-09)
504+
let super_controlled = relative_eq!(basis_decomposer.a, FRAC_PI_4, max_relative = 1e-09)
496505
&& relative_eq!(basis_decomposer.c, 0.0, max_relative = 1e-09);
497506

498507
// Create some useful matrices U1, U2, U3 are equivalent to the basis,
@@ -716,16 +725,6 @@ impl TwoQubitBasisDecomposer {
716725
}
717726
}
718727

719-
static K12R_ARR: GateArray1Q = [
720-
[c64(0., FRAC_1_SQRT_2), c64(FRAC_1_SQRT_2, 0.)],
721-
[c64(-FRAC_1_SQRT_2, 0.), c64(0., -FRAC_1_SQRT_2)],
722-
];
723-
724-
static K12L_ARR: GateArray1Q = [
725-
[c64(0.5, 0.5), c64(0.5, 0.5)],
726-
[c64(-0.5, 0.5), c64(0.5, -0.5)],
727-
];
728-
729728
fn decomp0_inner(target: &TwoQubitWeylDecomposition) -> SmallVec<[Array2<Complex64>; 8]> {
730729
smallvec![target.K1r.dot(&target.K2r), target.K1l.dot(&target.K2l),]
731730
}
@@ -794,10 +793,10 @@ impl TwoQubitBasisDecomposer {
794793
target.a.sin() * target.b.sin() * target.c.sin(),
795794
),
796795
4. * c64(
797-
(PI4 - target.a).cos()
796+
(FRAC_PI_4 - target.a).cos()
798797
* (self.basis_decomposer.b - target.b).cos()
799798
* target.c.cos(),
800-
(PI4 - target.a).sin()
799+
(FRAC_PI_4 - target.a).sin()
801800
* (self.basis_decomposer.b - target.b).sin()
802801
* target.c.sin(),
803802
),
@@ -966,6 +965,8 @@ impl TwoQubitBasisDecomposer {
966965
}
967966
}
968967

968+
/// Helper functions for two_qubit_decompose_up_to_diagonal
969+
/// Convert a 4x4 unitary matrix into a unitary matrix with determinant 1
969970
fn u4_to_su4(u4: ArrayView2<Complex64>) -> (Array2<Complex64>, f64) {
970971
let det_u = ndarray_to_faer(u4).determinant();
971972
let phase_factor = det_u.powf(-0.25).conj();
@@ -1036,6 +1037,7 @@ pub fn two_qubit_decompose_up_to_diagonal(
10361037
Ok((real_map, circ))
10371038
}
10381039

1040+
/// Helper function for TwoQubitBasisDecomposer with rz, sx, cx gates
10391041
fn compute_unitary(sequence: &TwoQubitSequenceVec, global_phase: f64) -> Array2<Complex64> {
10401042
let identity = aview2(&ONE_QUBIT_IDENTITY);
10411043
let phase = c64(0., global_phase).exp();

crates/synthesis/src/two_qubit_decompose/common.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ use numpy::{IntoPyArray, PyArray2, PyReadonlyArray1, PyReadonlyArray2};
1818
use pyo3::prelude::*;
1919

2020
use crate::linalg::ndarray_to_faer;
21-
use qiskit_util::alias::GateArray2Q;
22-
use qiskit_util::complex::{C_ZERO, IM, M_IM, c64};
23-
21+
use qiskit_util::alias::{GateArray1Q, GateArray2Q};
22+
use qiskit_util::complex::{C_M_ONE, C_ONE, C_ZERO, IM, M_IM, c64};
2423
pub(super) const DEFAULT_FIDELITY: f64 = 1.0 - 1.0e-9;
2524

2625
pub trait TraceToFidelity {
@@ -98,6 +97,10 @@ static MAGIC_DAGGER: GateArray2Q = [
9897
],
9998
];
10099

100+
pub static IPZ: GateArray1Q = [[IM, C_ZERO], [C_ZERO, M_IM]];
101+
pub static IPY: GateArray1Q = [[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]];
102+
pub static IPX: GateArray1Q = [[C_ZERO, IM], [IM, C_ZERO]];
103+
101104
#[inline(always)]
102105
pub(crate) fn transpose_conjugate(mat: ArrayView2<Complex64>) -> Array2<Complex64> {
103106
mat.t().mapv(|x| x.conj())

crates/synthesis/src/two_qubit_decompose/controlled_u_decomposer.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use approx::abs_diff_eq;
1414
use num_complex::Complex64;
1515
use smallvec::{SmallVec, smallvec};
16-
use std::f64::consts::PI;
16+
use std::f64::consts::FRAC_PI_2;
1717

1818
use ndarray::prelude::*;
1919
use numpy::PyReadonlyArray2;
@@ -38,8 +38,6 @@ use super::common::DEFAULT_FIDELITY;
3838
use super::gate_sequence::TwoQubitGateSequence;
3939
use super::weyl_decomposition::{Specialization, TwoQubitWeylDecomposition};
4040

41-
const PI2: f64 = PI / 2.;
42-
4341
/// invert 1q gate sequence
4442
fn invert_1q_gate(
4543
gate: (StandardGate, SmallVec<[f64; 3]>),
@@ -458,7 +456,7 @@ impl TwoQubitControlledUDecomposer {
458456
/// Initialize the KAK decomposition.
459457
pub fn new_inner(rxx_equivalent_gate: RXXEquivalent, euler_basis: &str) -> PyResult<Self> {
460458
let atol = DEFAULT_ATOL;
461-
let test_angles = [0.2, 0.3, PI2];
459+
let test_angles = [0.2, 0.3, FRAC_PI_2];
462460

463461
let scales: PyResult<Vec<f64>> = test_angles
464462
.into_iter()

0 commit comments

Comments
 (0)