git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
664 lines
19 KiB
Rust
664 lines
19 KiB
Rust
//! Tests for ruqu_core::gate — gate matrix correctness and unitarity.
|
|
|
|
use ruqu_core::gate::Gate;
|
|
use ruqu_core::types::Complex;
|
|
|
|
const EPSILON: f64 = 1e-10;
|
|
|
|
fn approx_eq(a: f64, b: f64) -> bool {
|
|
(a - b).abs() < EPSILON
|
|
}
|
|
|
|
fn complex_approx_eq(a: &Complex, b: &Complex) -> bool {
|
|
approx_eq(a.re, b.re) && approx_eq(a.im, b.im)
|
|
}
|
|
|
|
/// Convenience: build a Complex value.
|
|
fn c(re: f64, im: f64) -> Complex {
|
|
Complex { re, im }
|
|
}
|
|
|
|
/// Check that a 2x2 matrix satisfies U^dag * U = I (unitarity).
|
|
fn assert_unitary_2x2(m: &[[Complex; 2]; 2]) {
|
|
// Flatten to make indexing easier: flat[i*2+j] = m[i][j]
|
|
let flat = [m[0][0], m[0][1], m[1][0], m[1][1]];
|
|
// U^dag * U should equal identity
|
|
let udu = [
|
|
// (0,0)
|
|
flat[0].conj() * flat[0] + flat[2].conj() * flat[2],
|
|
// (0,1)
|
|
flat[0].conj() * flat[1] + flat[2].conj() * flat[3],
|
|
// (1,0)
|
|
flat[1].conj() * flat[0] + flat[3].conj() * flat[2],
|
|
// (1,1)
|
|
flat[1].conj() * flat[1] + flat[3].conj() * flat[3],
|
|
];
|
|
assert!(
|
|
complex_approx_eq(&udu[0], &c(1.0, 0.0)),
|
|
"U^dag U [0,0] = {:?}, expected 1",
|
|
udu[0]
|
|
);
|
|
assert!(
|
|
complex_approx_eq(&udu[1], &c(0.0, 0.0)),
|
|
"U^dag U [0,1] = {:?}, expected 0",
|
|
udu[1]
|
|
);
|
|
assert!(
|
|
complex_approx_eq(&udu[2], &c(0.0, 0.0)),
|
|
"U^dag U [1,0] = {:?}, expected 0",
|
|
udu[2]
|
|
);
|
|
assert!(
|
|
complex_approx_eq(&udu[3], &c(1.0, 0.0)),
|
|
"U^dag U [1,1] = {:?}, expected 1",
|
|
udu[3]
|
|
);
|
|
}
|
|
|
|
/// Check unitarity for a 4x4 matrix.
|
|
fn assert_unitary_4x4(m: &[[Complex; 4]; 4]) {
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
let mut sum = c(0.0, 0.0);
|
|
for k in 0..4 {
|
|
// (U^dag)_{ik} = conj(U_{ki})
|
|
let u_dag_ik = m[k][i].conj();
|
|
let u_kj = m[k][j];
|
|
sum = sum + u_dag_ik * u_kj;
|
|
}
|
|
let expected = if i == j { c(1.0, 0.0) } else { c(0.0, 0.0) };
|
|
assert!(
|
|
complex_approx_eq(&sum, &expected),
|
|
"U^dag U [{},{}] = ({}, {}), expected ({}, {})",
|
|
i,
|
|
j,
|
|
sum.re,
|
|
sum.im,
|
|
expected.re,
|
|
expected.im
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Hadamard gate: H = 1/sqrt(2) * [[1, 1], [1, -1]]
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_hadamard_matrix() {
|
|
let matrix = Gate::H(0).matrix_1q().expect("H should have a 2x2 matrix");
|
|
let s = std::f64::consts::FRAC_1_SQRT_2;
|
|
|
|
assert!(complex_approx_eq(&matrix[0][0], &c(s, 0.0))); // [0,0]
|
|
assert!(complex_approx_eq(&matrix[0][1], &c(s, 0.0))); // [0,1]
|
|
assert!(complex_approx_eq(&matrix[1][0], &c(s, 0.0))); // [1,0]
|
|
assert!(complex_approx_eq(&matrix[1][1], &c(-s, 0.0))); // [1,1]
|
|
}
|
|
|
|
#[test]
|
|
fn test_hadamard_is_self_inverse() {
|
|
// H * H = I
|
|
let m = Gate::H(0).matrix_1q().unwrap();
|
|
// Multiply m * m
|
|
let prod = [
|
|
m[0][0] * m[0][0] + m[0][1] * m[1][0],
|
|
m[0][0] * m[0][1] + m[0][1] * m[1][1],
|
|
m[1][0] * m[0][0] + m[1][1] * m[1][0],
|
|
m[1][0] * m[0][1] + m[1][1] * m[1][1],
|
|
];
|
|
assert!(complex_approx_eq(&prod[0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&prod[1], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&prod[2], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&prod[3], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_hadamard_unitarity() {
|
|
let m = Gate::H(0).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Pauli-X gate: [[0, 1], [1, 0]]
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_pauli_x_matrix() {
|
|
let m = Gate::X(0).matrix_1q().expect("X should have a 2x2 matrix");
|
|
assert!(complex_approx_eq(&m[0][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(0.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_pauli_x_is_self_inverse() {
|
|
let m = Gate::X(0).matrix_1q().unwrap();
|
|
let prod = [
|
|
m[0][0] * m[0][0] + m[0][1] * m[1][0],
|
|
m[0][0] * m[0][1] + m[0][1] * m[1][1],
|
|
m[1][0] * m[0][0] + m[1][1] * m[1][0],
|
|
m[1][0] * m[0][1] + m[1][1] * m[1][1],
|
|
];
|
|
assert!(complex_approx_eq(&prod[0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&prod[3], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_pauli_x_unitarity() {
|
|
let m = Gate::X(0).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Pauli-Y gate: [[0, -i], [i, 0]]
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_pauli_y_matrix() {
|
|
let m = Gate::Y(0).matrix_1q().expect("Y should have a 2x2 matrix");
|
|
assert!(complex_approx_eq(&m[0][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, -1.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, 1.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(0.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_pauli_y_is_self_inverse() {
|
|
let m = Gate::Y(0).matrix_1q().unwrap();
|
|
let prod = [
|
|
m[0][0] * m[0][0] + m[0][1] * m[1][0],
|
|
m[0][0] * m[0][1] + m[0][1] * m[1][1],
|
|
m[1][0] * m[0][0] + m[1][1] * m[1][0],
|
|
m[1][0] * m[0][1] + m[1][1] * m[1][1],
|
|
];
|
|
assert!(complex_approx_eq(&prod[0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&prod[3], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_pauli_y_unitarity() {
|
|
let m = Gate::Y(0).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Pauli-Z gate: [[1, 0], [0, -1]]
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_pauli_z_matrix() {
|
|
let m = Gate::Z(0).matrix_1q().expect("Z should have a 2x2 matrix");
|
|
assert!(complex_approx_eq(&m[0][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(-1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_pauli_z_is_self_inverse() {
|
|
let m = Gate::Z(0).matrix_1q().unwrap();
|
|
let prod = [
|
|
m[0][0] * m[0][0] + m[0][1] * m[1][0],
|
|
m[0][0] * m[0][1] + m[0][1] * m[1][1],
|
|
m[1][0] * m[0][0] + m[1][1] * m[1][0],
|
|
m[1][0] * m[0][1] + m[1][1] * m[1][1],
|
|
];
|
|
assert!(complex_approx_eq(&prod[0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&prod[3], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_pauli_z_unitarity() {
|
|
let m = Gate::Z(0).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Pauli algebra: X*Y = iZ, Y*Z = iX, Z*X = iY
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_pauli_xy_equals_iz() {
|
|
let x = Gate::X(0).matrix_1q().unwrap();
|
|
let y = Gate::Y(0).matrix_1q().unwrap();
|
|
let z = Gate::Z(0).matrix_1q().unwrap();
|
|
|
|
// X * Y (2x2 matrix multiply)
|
|
let xy = [
|
|
[
|
|
x[0][0] * y[0][0] + x[0][1] * y[1][0],
|
|
x[0][0] * y[0][1] + x[0][1] * y[1][1],
|
|
],
|
|
[
|
|
x[1][0] * y[0][0] + x[1][1] * y[1][0],
|
|
x[1][0] * y[0][1] + x[1][1] * y[1][1],
|
|
],
|
|
];
|
|
// i * Z
|
|
let iz = [
|
|
[c(0.0, 1.0) * z[0][0], c(0.0, 1.0) * z[0][1]],
|
|
[c(0.0, 1.0) * z[1][0], c(0.0, 1.0) * z[1][1]],
|
|
];
|
|
for i in 0..2 {
|
|
for j in 0..2 {
|
|
assert!(
|
|
complex_approx_eq(&xy[i][j], &iz[i][j]),
|
|
"XY[{},{}] = ({}, {}), iZ[{},{}] = ({}, {})",
|
|
i,
|
|
j,
|
|
xy[i][j].re,
|
|
xy[i][j].im,
|
|
i,
|
|
j,
|
|
iz[i][j].re,
|
|
iz[i][j].im
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// S gate: [[1, 0], [0, i]]
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_s_gate_matrix() {
|
|
let m = Gate::S(0).matrix_1q().expect("S should have a 2x2 matrix");
|
|
assert!(complex_approx_eq(&m[0][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(0.0, 1.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_s_gate_unitarity() {
|
|
let m = Gate::S(0).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_s_squared_is_z() {
|
|
// S^2 = Z
|
|
let s = Gate::S(0).matrix_1q().unwrap();
|
|
let z = Gate::Z(0).matrix_1q().unwrap();
|
|
let s2 = [
|
|
[
|
|
s[0][0] * s[0][0] + s[0][1] * s[1][0],
|
|
s[0][0] * s[0][1] + s[0][1] * s[1][1],
|
|
],
|
|
[
|
|
s[1][0] * s[0][0] + s[1][1] * s[1][0],
|
|
s[1][0] * s[0][1] + s[1][1] * s[1][1],
|
|
],
|
|
];
|
|
for i in 0..2 {
|
|
for j in 0..2 {
|
|
assert!(
|
|
complex_approx_eq(&s2[i][j], &z[i][j]),
|
|
"S^2[{},{}] != Z[{},{}]",
|
|
i,
|
|
j,
|
|
i,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// T gate: [[1, 0], [0, e^{i*pi/4}]]
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_t_gate_matrix() {
|
|
let m = Gate::T(0).matrix_1q().expect("T should have a 2x2 matrix");
|
|
let phase = std::f64::consts::FRAC_PI_4;
|
|
assert!(complex_approx_eq(&m[0][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(phase.cos(), phase.sin())));
|
|
}
|
|
|
|
#[test]
|
|
fn test_t_gate_unitarity() {
|
|
let m = Gate::T(0).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_t_squared_is_s() {
|
|
// T^2 = S
|
|
let t = Gate::T(0).matrix_1q().unwrap();
|
|
let s = Gate::S(0).matrix_1q().unwrap();
|
|
let t2 = [
|
|
[
|
|
t[0][0] * t[0][0] + t[0][1] * t[1][0],
|
|
t[0][0] * t[0][1] + t[0][1] * t[1][1],
|
|
],
|
|
[
|
|
t[1][0] * t[0][0] + t[1][1] * t[1][0],
|
|
t[1][0] * t[0][1] + t[1][1] * t[1][1],
|
|
],
|
|
];
|
|
for i in 0..2 {
|
|
for j in 0..2 {
|
|
assert!(
|
|
complex_approx_eq(&t2[i][j], &s[i][j]),
|
|
"T^2[{},{}] != S[{},{}]",
|
|
i,
|
|
j,
|
|
i,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Rotation gates: Rx, Ry, Rz
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_rx_zero_is_identity() {
|
|
let m = Gate::Rx(0, 0.0).matrix_1q().unwrap();
|
|
assert!(complex_approx_eq(&m[0][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_rx_pi_matrix() {
|
|
// Rx(pi) = [[cos(pi/2), -i*sin(pi/2)], [-i*sin(pi/2), cos(pi/2)]]
|
|
// = [[0, -i], [-i, 0]]
|
|
let m = Gate::Rx(0, std::f64::consts::PI).matrix_1q().unwrap();
|
|
assert!(complex_approx_eq(&m[0][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, -1.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, -1.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(0.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_rx_unitarity() {
|
|
let m = Gate::Rx(0, 1.234).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_ry_zero_is_identity() {
|
|
let m = Gate::Ry(0, 0.0).matrix_1q().unwrap();
|
|
assert!(complex_approx_eq(&m[0][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_ry_pi_matrix() {
|
|
// Ry(pi) = [[cos(pi/2), -sin(pi/2)], [sin(pi/2), cos(pi/2)]]
|
|
// = [[0, -1], [1, 0]]
|
|
let m = Gate::Ry(0, std::f64::consts::PI).matrix_1q().unwrap();
|
|
assert!(complex_approx_eq(&m[0][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(-1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(0.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_ry_unitarity() {
|
|
let m = Gate::Ry(0, 2.718).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rz_zero_is_identity() {
|
|
let m = Gate::Rz(0, 0.0).matrix_1q().unwrap();
|
|
assert!(complex_approx_eq(&m[0][0], &c(1.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(1.0, 0.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_rz_pi_matrix() {
|
|
// Rz(pi) = [[e^{-i*pi/2}, 0], [0, e^{i*pi/2}]] = [[-i, 0], [0, i]]
|
|
let m = Gate::Rz(0, std::f64::consts::PI).matrix_1q().unwrap();
|
|
assert!(complex_approx_eq(&m[0][0], &c(0.0, -1.0)));
|
|
assert!(complex_approx_eq(&m[0][1], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][0], &c(0.0, 0.0)));
|
|
assert!(complex_approx_eq(&m[1][1], &c(0.0, 1.0)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_rz_unitarity() {
|
|
let m = Gate::Rz(0, 0.789).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rotation_gates_various_angles_unitary() {
|
|
let angles = [
|
|
0.0,
|
|
0.1,
|
|
0.5,
|
|
1.0,
|
|
std::f64::consts::PI,
|
|
2.0 * std::f64::consts::PI,
|
|
-0.7,
|
|
];
|
|
for &theta in &angles {
|
|
let rx = Gate::Rx(0, theta).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&rx);
|
|
|
|
let ry = Gate::Ry(0, theta).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&ry);
|
|
|
|
let rz = Gate::Rz(0, theta).matrix_1q().unwrap();
|
|
assert_unitary_2x2(&rz);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CNOT gate: 4x4 matrix
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_cnot_matrix() {
|
|
// CNOT = [[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]
|
|
let m = Gate::CNOT(0, 1)
|
|
.matrix_2q()
|
|
.expect("CNOT should have a 4x4 matrix");
|
|
|
|
let expected: [[Complex; 4]; 4] = [
|
|
[c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(0.0, 0.0)], // row 0
|
|
[c(0.0, 0.0), c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0)], // row 1
|
|
[c(0.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(1.0, 0.0)], // row 2
|
|
[c(0.0, 0.0), c(0.0, 0.0), c(1.0, 0.0), c(0.0, 0.0)], // row 3
|
|
];
|
|
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
assert!(
|
|
complex_approx_eq(&m[i][j], &expected[i][j]),
|
|
"CNOT matrix[{}][{}]: got ({}, {}), expected ({}, {})",
|
|
i,
|
|
j,
|
|
m[i][j].re,
|
|
m[i][j].im,
|
|
expected[i][j].re,
|
|
expected[i][j].im
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_cnot_unitarity() {
|
|
let m = Gate::CNOT(0, 1).matrix_2q().unwrap();
|
|
assert_unitary_4x4(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cnot_is_self_inverse() {
|
|
// CNOT * CNOT = I
|
|
let m = Gate::CNOT(0, 1).matrix_2q().unwrap();
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
let mut sum = c(0.0, 0.0);
|
|
for k in 0..4 {
|
|
sum = sum + m[i][k] * m[k][j];
|
|
}
|
|
let expected = if i == j { c(1.0, 0.0) } else { c(0.0, 0.0) };
|
|
assert!(
|
|
complex_approx_eq(&sum, &expected),
|
|
"CNOT^2 [{},{}] = ({}, {}), expected ({}, {})",
|
|
i,
|
|
j,
|
|
sum.re,
|
|
sum.im,
|
|
expected.re,
|
|
expected.im
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CZ gate: 4x4 matrix diag(1, 1, 1, -1)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_cz_matrix() {
|
|
let m = Gate::CZ(0, 1)
|
|
.matrix_2q()
|
|
.expect("CZ should have a 4x4 matrix");
|
|
|
|
// CZ = diag(1, 1, 1, -1)
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
let expected = if i == j {
|
|
if i == 3 {
|
|
c(-1.0, 0.0)
|
|
} else {
|
|
c(1.0, 0.0)
|
|
}
|
|
} else {
|
|
c(0.0, 0.0)
|
|
};
|
|
assert!(
|
|
complex_approx_eq(&m[i][j], &expected),
|
|
"CZ[{},{}] mismatch",
|
|
i,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_cz_unitarity() {
|
|
let m = Gate::CZ(0, 1).matrix_2q().unwrap();
|
|
assert_unitary_4x4(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cz_is_symmetric() {
|
|
// CZ(0,1) should equal CZ(1,0) — the gate is symmetric in control/target
|
|
let m01 = Gate::CZ(0, 1).matrix_2q().unwrap();
|
|
let m10 = Gate::CZ(1, 0).matrix_2q().unwrap();
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
assert!(
|
|
complex_approx_eq(&m01[i][j], &m10[i][j]),
|
|
"CZ symmetry mismatch at [{},{}]",
|
|
i,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SWAP gate: 4x4 matrix
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_swap_matrix() {
|
|
// SWAP = [[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]
|
|
let m = Gate::SWAP(0, 1)
|
|
.matrix_2q()
|
|
.expect("SWAP should have a 4x4 matrix");
|
|
|
|
let expected: [[Complex; 4]; 4] = [
|
|
[c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(0.0, 0.0)], // row 0
|
|
[c(0.0, 0.0), c(0.0, 0.0), c(1.0, 0.0), c(0.0, 0.0)], // row 1
|
|
[c(0.0, 0.0), c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0)], // row 2
|
|
[c(0.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(1.0, 0.0)], // row 3
|
|
];
|
|
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
assert!(
|
|
complex_approx_eq(&m[i][j], &expected[i][j]),
|
|
"SWAP matrix[{}][{}] mismatch",
|
|
i,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_swap_unitarity() {
|
|
let m = Gate::SWAP(0, 1).matrix_2q().unwrap();
|
|
assert_unitary_4x4(&m);
|
|
}
|
|
|
|
#[test]
|
|
fn test_swap_is_self_inverse() {
|
|
// SWAP * SWAP = I
|
|
let m = Gate::SWAP(0, 1).matrix_2q().unwrap();
|
|
for i in 0..4 {
|
|
for j in 0..4 {
|
|
let mut sum = c(0.0, 0.0);
|
|
for k in 0..4 {
|
|
sum = sum + m[i][k] * m[k][j];
|
|
}
|
|
let expected = if i == j { c(1.0, 0.0) } else { c(0.0, 0.0) };
|
|
assert!(
|
|
complex_approx_eq(&sum, &expected),
|
|
"SWAP^2 [{},{}] mismatch",
|
|
i,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Gate qubit index extraction
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_gate_qubit_indices() {
|
|
// Single-qubit gates should report their target qubit via qubits().
|
|
assert_eq!(Gate::H(3).qubits(), vec![3]);
|
|
assert_eq!(Gate::X(0).qubits(), vec![0]);
|
|
assert_eq!(Gate::Y(7).qubits(), vec![7]);
|
|
assert_eq!(Gate::Z(15).qubits(), vec![15]);
|
|
assert_eq!(Gate::Rx(2, 0.5).qubits(), vec![2]);
|
|
assert_eq!(Gate::Ry(4, 1.0).qubits(), vec![4]);
|
|
assert_eq!(Gate::Rz(6, 0.3).qubits(), vec![6]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_two_qubit_gate_indices() {
|
|
// Two-qubit gates should report both qubits.
|
|
let qubits = Gate::CNOT(0, 1).qubits();
|
|
assert_eq!(qubits.len(), 2);
|
|
assert_eq!(qubits[0], 0);
|
|
assert_eq!(qubits[1], 1);
|
|
|
|
let qubits2 = Gate::CNOT(5, 3).qubits();
|
|
assert_eq!(qubits2[0], 5);
|
|
assert_eq!(qubits2[1], 3);
|
|
}
|