use ndarray::prelude::*;
use num_complex::Complex;
use std::f64::consts::FRAC_1_SQRT_2;
use std::sync::LazyLock;
const ZERO: Complex<f64> = Complex::new(0., 0.);
const ONE: Complex<f64> = Complex::new(1., 0.);
const I: Complex<f64> = Complex::new(0., 1.);
const EXP_I_PI_4: Complex<f64> = Complex::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2);
const EXP_NEG_I_PI_4: Complex<f64> = Complex::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2);
pub static H_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ONE], [ONE, -ONE]] * FRAC_1_SQRT_2);
pub static I_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ZERO], [ZERO, ONE]]);
pub static ISWAP_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| {
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ZERO, I, ZERO],
[ZERO, I, ZERO, ZERO],
[ZERO, ZERO, ZERO, ONE]
]
});
pub static S_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ZERO], [ZERO, I]]);
pub static SDG_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ZERO], [ZERO, -I]]);
pub static SWAP_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| {
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ZERO, ONE, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, ZERO, ONE]
]
});
pub static T_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ZERO], [ZERO, EXP_I_PI_4]]);
pub static TDG_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ZERO], [ZERO, EXP_NEG_I_PI_4]]);
pub static X_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ZERO, ONE], [ONE, ZERO]]);
pub static X2P_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, -I], [-I, ONE]] * FRAC_1_SQRT_2);
pub static X2M_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, I], [I, ONE]] * FRAC_1_SQRT_2);
pub static Y_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| array![[ZERO, -I], [I, ZERO]]);
pub static Y2P_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, -ONE], [ONE, ONE]] * FRAC_1_SQRT_2);
pub static Y2M_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ONE], [-ONE, ONE]] * FRAC_1_SQRT_2);
pub static Z_GATE: LazyLock<Array2<Complex<f64>>> =
LazyLock::new(|| array![[ONE, ZERO], [ZERO, -ONE]]);
pub static CX_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| {
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, ZERO, ONE],
[ZERO, ZERO, ONE, ZERO]
]
});
pub static CCX_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| {
let mut m = Array2::eye(8);
m[[6, 6]] = ZERO;
m[[6, 7]] = ONE;
m[[7, 6]] = ONE;
m[[7, 7]] = ZERO;
m
});
pub static CY_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| {
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, ZERO, -I],
[ZERO, ZERO, I, ZERO]
]
});
pub static CZ_GATE: LazyLock<Array2<Complex<f64>>> = LazyLock::new(|| {
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, ONE, ZERO],
[ZERO, ZERO, ZERO, -ONE]
]
});
#[inline]
pub fn rx_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let neg_i_sin = Complex::new(0.0, -sin_val);
array![[cos, neg_i_sin], [neg_i_sin, cos]]
}
#[inline]
pub fn rxx_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let neg_i_sin = Complex::new(0.0, -sin_val);
array![
[cos, ZERO, ZERO, neg_i_sin],
[ZERO, cos, neg_i_sin, ZERO],
[ZERO, neg_i_sin, cos, ZERO],
[neg_i_sin, ZERO, ZERO, cos]
]
}
#[inline]
pub fn rxy_gate(theta: f64, phi: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let sin = Complex::new(sin_val, 0.0);
let (phi_sin, phi_cos) = phi.sin_cos();
let exp_neg_i_phi = Complex::new(phi_cos, -phi_sin);
let exp_i_phi = Complex::new(phi_cos, phi_sin);
let term1 = -I * exp_neg_i_phi * sin;
let term2 = -I * exp_i_phi * sin;
array![[cos, term1], [term2, cos]]
}
#[inline]
pub fn ry_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let sin = Complex::new(sin_val, 0.0);
let neg_sin = -sin;
array![[cos, neg_sin], [sin, cos]]
}
#[inline]
pub fn ryy_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let i_sin = Complex::new(0.0, sin_val);
let neg_i_sin = -i_sin;
array![
[cos, ZERO, ZERO, i_sin],
[ZERO, cos, neg_i_sin, ZERO],
[ZERO, neg_i_sin, cos, ZERO],
[i_sin, ZERO, ZERO, cos]
]
}
#[inline]
pub fn rz_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let exp_neg = Complex::new(cos_val, -sin_val);
let exp_pos = Complex::new(cos_val, sin_val);
array![[exp_neg, ZERO], [ZERO, exp_pos]]
}
#[inline]
pub fn rzx_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let i_sin = Complex::new(0.0, sin_val);
let neg_i_sin = -i_sin;
array![
[cos, neg_i_sin, ZERO, ZERO],
[neg_i_sin, cos, ZERO, ZERO],
[ZERO, ZERO, cos, i_sin],
[ZERO, ZERO, i_sin, cos]
]
}
#[inline]
pub fn rzz_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let exp_neg = Complex::new(cos_val, -sin_val);
let exp_pos = Complex::new(cos_val, sin_val);
array![
[exp_neg, ZERO, ZERO, ZERO],
[ZERO, exp_pos, ZERO, ZERO],
[ZERO, ZERO, exp_pos, ZERO],
[ZERO, ZERO, ZERO, exp_neg]
]
}
#[inline]
pub fn phase_gate(lambda: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = lambda.sin_cos();
let exp_i_lambda = Complex::new(cos_val, sin_val);
array![[ONE, ZERO], [ZERO, exp_i_lambda]]
}
#[inline]
pub fn global_phase_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = theta.sin_cos();
let exp_i_theta = Complex::new(cos_val, sin_val);
array![[exp_i_theta, ZERO], [ZERO, exp_i_theta]]
}
#[inline]
pub fn u_gate(theta: f64, phi: f64, lambda: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let sin = Complex::new(sin_val, 0.0);
let (phi_sin, phi_cos) = phi.sin_cos();
let exp_i_phi = Complex::new(phi_cos, phi_sin);
let (lam_sin, lam_cos) = lambda.sin_cos();
let exp_i_lambda = Complex::new(lam_cos, lam_sin);
let (pl_sin, pl_cos) = (phi + lambda).sin_cos();
let exp_i_phi_lambda = Complex::new(pl_cos, pl_sin);
array![
[cos, -exp_i_lambda * sin],
[exp_i_phi * sin, exp_i_phi_lambda * cos]
]
}
#[inline]
pub fn xy_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = theta.sin_cos();
let exp_i_theta = Complex::new(cos_val, sin_val);
let exp_neg_i_theta = Complex::new(cos_val, -sin_val);
let term1 = -I * exp_neg_i_theta;
let term2 = -I * exp_i_theta;
array![[ZERO, term1], [term2, ZERO]]
}
#[inline]
pub fn xy2p_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = theta.sin_cos();
let exp_i_theta = Complex::new(cos_val, sin_val);
let exp_neg_i_theta = Complex::new(cos_val, -sin_val);
let term1 = -I * exp_neg_i_theta * FRAC_1_SQRT_2;
let term2 = -I * exp_i_theta * FRAC_1_SQRT_2;
let diag = Complex::new(FRAC_1_SQRT_2, 0.0);
array![[diag, term1], [term2, diag]]
}
#[inline]
pub fn xy2m_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = theta.sin_cos();
let exp_i_theta = Complex::new(cos_val, sin_val);
let exp_neg_i_theta = Complex::new(cos_val, -sin_val);
let term1 = I * exp_neg_i_theta * FRAC_1_SQRT_2;
let term2 = I * exp_i_theta * FRAC_1_SQRT_2;
let diag = Complex::new(FRAC_1_SQRT_2, 0.0);
array![[diag, term1], [term2, diag]]
}
#[inline]
pub fn crx_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let neg_i_sin = Complex::new(0.0, -sin_val);
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, cos, neg_i_sin],
[ZERO, ZERO, neg_i_sin, cos]
]
}
#[inline]
pub fn cry_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let cos = Complex::new(cos_val, 0.0);
let sin = Complex::new(sin_val, 0.0);
let neg_sin = -sin;
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, cos, neg_sin],
[ZERO, ZERO, sin, cos]
]
}
#[inline]
pub fn crz_gate(theta: f64) -> Array2<Complex<f64>> {
let (sin_val, cos_val) = (theta / 2.0).sin_cos();
let exp_neg = Complex::new(cos_val, -sin_val);
let exp_pos = Complex::new(cos_val, sin_val);
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, ONE, ZERO, ZERO],
[ZERO, ZERO, exp_neg, ZERO],
[ZERO, ZERO, ZERO, exp_pos]
]
}
#[inline]
pub fn fsim_gate(theta: f64, phi: f64) -> Array2<Complex<f64>> {
let (sin_theta, cos_theta) = theta.sin_cos();
let cos_t = Complex::new(cos_theta, 0.0);
let neg_i_sin_t = Complex::new(0.0, -sin_theta);
let (sin_phi, cos_phi) = phi.sin_cos();
let exp_neg_i_phi = Complex::new(cos_phi, -sin_phi);
array![
[ONE, ZERO, ZERO, ZERO],
[ZERO, cos_t, neg_i_sin_t, ZERO],
[ZERO, neg_i_sin_t, cos_t, ZERO],
[ZERO, ZERO, ZERO, exp_neg_i_phi]
]
}
pub fn control_matrix(
base_matrix: &Array2<Complex<f64>>,
num_ctrls: usize,
) -> Array2<Complex<f64>> {
if num_ctrls == 0 {
return base_matrix.clone();
}
let base_dim = base_matrix.nrows();
let total_dim = base_dim << num_ctrls;
let mut mat = Array2::eye(total_dim);
let start_idx = total_dim - base_dim;
for i in 0..base_dim {
for j in 0..base_dim {
mat[[start_idx + i, start_idx + j]] = base_matrix[[i, j]];
}
}
mat
}
#[cfg(test)]
#[path = "./gate_matrix_test.rs"]
mod gate_matrix_test;