Squashed 'vendor/ruvector/' content from commit b64c2172

git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
commit d803bfe2b1
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,663 @@
//! 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);
}

View File

@@ -0,0 +1,446 @@
//! Tests for ruqu_core::simulator — high-level simulator, circuits, shots, reproducibility.
use ruqu_core::prelude::*;
const EPSILON: f64 = 1e-10;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < EPSILON
}
// ---------------------------------------------------------------------------
// Basic circuit construction
// ---------------------------------------------------------------------------
#[test]
fn test_circuit_new() {
let circuit = QuantumCircuit::new(3);
assert_eq!(circuit.num_qubits(), 3);
assert_eq!(circuit.gate_count(), 0);
}
#[test]
fn test_circuit_add_gates() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).cnot(0, 1);
assert_eq!(circuit.num_qubits(), 2);
assert_eq!(circuit.gate_count(), 2);
}
#[test]
fn test_circuit_chaining() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0).h(1).h(2).cnot(0, 1).cnot(1, 2);
assert_eq!(circuit.gate_count(), 5);
}
#[test]
fn test_circuit_gates_ref() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).x(0);
let gates = circuit.gates();
assert_eq!(gates.len(), 2);
}
#[test]
fn test_circuit_all_single_qubit_gates() {
let mut circuit = QuantumCircuit::new(1);
circuit
.h(0)
.x(0)
.y(0)
.z(0)
.s(0)
.t(0)
.rx(0, 0.5)
.ry(0, 0.5)
.rz(0, 0.5);
assert_eq!(circuit.gate_count(), 9);
}
#[test]
fn test_circuit_two_qubit_gates() {
let mut circuit = QuantumCircuit::new(2);
circuit.cnot(0, 1).cz(0, 1).swap(0, 1).rzz(0, 1, 0.5);
assert_eq!(circuit.gate_count(), 4);
}
#[test]
fn test_circuit_measure() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).cnot(0, 1).measure(0).measure(1);
assert_eq!(circuit.gate_count(), 4);
}
#[test]
fn test_circuit_measure_all() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0).h(1).h(2).measure_all();
// measure_all adds a measure for each qubit
assert!(circuit.gate_count() >= 6);
}
#[test]
fn test_circuit_reset() {
let mut circuit = QuantumCircuit::new(1);
circuit.x(0).reset(0);
assert_eq!(circuit.gate_count(), 2);
}
#[test]
fn test_circuit_barrier() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).barrier().cnot(0, 1);
assert_eq!(circuit.gate_count(), 3);
}
#[test]
fn test_circuit_add_gate_directly() {
let mut circuit = QuantumCircuit::new(2);
circuit.add_gate(Gate::H(0));
circuit.add_gate(Gate::CNOT(0, 1));
assert_eq!(circuit.gate_count(), 2);
}
// ---------------------------------------------------------------------------
// Circuit depth
// ---------------------------------------------------------------------------
#[test]
fn test_circuit_depth_single_gate() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0);
assert_eq!(circuit.depth(), 1);
}
#[test]
fn test_circuit_depth_parallel_gates() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0).h(1).h(2);
// All H gates act on different qubits, so depth = 1
assert!(circuit.depth() >= 1);
}
#[test]
fn test_circuit_depth_sequential() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).x(0).y(0);
// All on same qubit, depth = 3
assert!(circuit.depth() >= 3);
}
#[test]
fn test_circuit_depth_mixed() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0).h(1).h(2).cnot(0, 1).cnot(1, 2);
// H gates parallel (depth 1), then CNOT(0,1) (depth 2), then CNOT(1,2) (depth 3)
assert!(circuit.depth() >= 2);
}
// ---------------------------------------------------------------------------
// Simulator::run
// ---------------------------------------------------------------------------
#[test]
fn test_simulator_basic() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).cnot(0, 1);
let result = Simulator::run(&circuit).unwrap();
assert_eq!(result.metrics.num_qubits, 2);
assert!(result.metrics.gate_count >= 2);
}
#[test]
fn test_simulator_bell_state_probabilities() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).cnot(0, 1);
let result = Simulator::run(&circuit).unwrap();
let probs = result.state.probabilities();
assert!(approx_eq(probs[0], 0.5)); // |00>
assert!(approx_eq(probs[1], 0.0)); // |01>
assert!(approx_eq(probs[2], 0.0)); // |10>
assert!(approx_eq(probs[3], 0.5)); // |11>
}
#[test]
fn test_simulator_identity_circuit() {
// No gates at all: state should remain |0>
let circuit = QuantumCircuit::new(1);
let result = Simulator::run(&circuit).unwrap();
let probs = result.state.probabilities();
assert!(approx_eq(probs[0], 1.0));
assert!(approx_eq(probs[1], 0.0));
}
#[test]
fn test_simulator_x_gate() {
let mut circuit = QuantumCircuit::new(1);
circuit.x(0);
let result = Simulator::run(&circuit).unwrap();
let probs = result.state.probabilities();
assert!(approx_eq(probs[0], 0.0));
assert!(approx_eq(probs[1], 1.0));
}
#[test]
fn test_simulator_ghz() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0).cnot(0, 1).cnot(1, 2);
let result = Simulator::run(&circuit).unwrap();
let probs = result.state.probabilities();
assert!(approx_eq(probs[0], 0.5));
assert!(approx_eq(probs[7], 0.5));
for i in 1..7 {
assert!(approx_eq(probs[i], 0.0));
}
}
#[test]
fn test_simulator_with_measurement() {
let mut circuit = QuantumCircuit::new(1);
circuit.x(0).measure(0);
let result = Simulator::run(&circuit).unwrap();
assert!(!result.measurements.is_empty());
// X|0> = |1>, so measurement should be 1
assert!(result.measurements[0].result);
}
// ---------------------------------------------------------------------------
// Simulator::run_with_config (seeded)
// ---------------------------------------------------------------------------
#[test]
fn test_seeded_reproducibility() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).measure(0);
let config = SimConfig {
seed: Some(42),
noise: None,
shots: None,
};
let r1 = Simulator::run_with_config(&circuit, &config).unwrap();
let r2 = Simulator::run_with_config(&circuit, &config).unwrap();
assert_eq!(r1.measurements.len(), r2.measurements.len());
if !r1.measurements.is_empty() && !r2.measurements.is_empty() {
assert_eq!(r1.measurements[0].result, r2.measurements[0].result);
}
}
#[test]
fn test_different_seeds_may_differ() {
// With different seeds and a probabilistic circuit, results may differ
// (not guaranteed per single run, but validates that config is used)
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).measure(0);
let c1 = SimConfig {
seed: Some(42),
noise: None,
shots: None,
};
let c2 = SimConfig {
seed: Some(99),
noise: None,
shots: None,
};
let _r1 = Simulator::run_with_config(&circuit, &c1).unwrap();
let _r2 = Simulator::run_with_config(&circuit, &c2).unwrap();
// We just verify both complete without error.
}
#[test]
fn test_config_no_seed() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).measure(0);
let config = SimConfig {
seed: None,
noise: None,
shots: None,
};
let result = Simulator::run_with_config(&circuit, &config).unwrap();
assert!(!result.measurements.is_empty());
}
// ---------------------------------------------------------------------------
// Simulator::run_shots
// ---------------------------------------------------------------------------
#[test]
fn test_run_shots_basic() {
let mut circuit = QuantumCircuit::new(1);
circuit.x(0).measure(0);
let result = Simulator::run_shots(&circuit, 100, Some(42)).unwrap();
// X|0> = |1>, every shot should measure 1
let total: usize = result.counts.values().sum();
assert_eq!(total, 100);
// All shots should give outcome |1> -> vec![true]
let count_one = result.counts.get(&vec![true]).copied().unwrap_or(0);
assert_eq!(count_one, 100, "All shots should measure |1>");
}
#[test]
fn test_run_shots_superposition() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).measure(0);
let result = Simulator::run_shots(&circuit, 1000, Some(42)).unwrap();
let total: usize = result.counts.values().sum();
assert_eq!(total, 1000);
let count_zero = result.counts.get(&vec![false]).copied().unwrap_or(0);
let count_one = result.counts.get(&vec![true]).copied().unwrap_or(0);
assert_eq!(count_zero + count_one, 1000);
// Expect roughly 50/50 with tolerance
let ratio = count_zero as f64 / 1000.0;
assert!(
ratio > 0.4 && ratio < 0.6,
"Expected ~50% zeros, got {:.1}%",
ratio * 100.0
);
}
#[test]
fn test_run_shots_bell_state() {
let mut circuit = QuantumCircuit::new(2);
circuit.h(0).cnot(0, 1).measure(0).measure(1);
let result = Simulator::run_shots(&circuit, 500, Some(42)).unwrap();
let total: usize = result.counts.values().sum();
assert_eq!(total, 500);
// Bell state: only |00> and |11> outcomes
let count_00 = result.counts.get(&vec![false, false]).copied().unwrap_or(0);
let count_11 = result.counts.get(&vec![true, true]).copied().unwrap_or(0);
let count_01 = result.counts.get(&vec![false, true]).copied().unwrap_or(0);
let count_10 = result.counts.get(&vec![true, false]).copied().unwrap_or(0);
assert_eq!(
count_01 + count_10,
0,
"Bell state should never produce |01> or |10>"
);
assert_eq!(count_00 + count_11, 500);
}
#[test]
fn test_run_shots_seeded_reproducibility() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).measure(0);
let r1 = Simulator::run_shots(&circuit, 100, Some(42)).unwrap();
let r2 = Simulator::run_shots(&circuit, 100, Some(42)).unwrap();
assert_eq!(r1.counts, r2.counts);
}
#[test]
fn test_run_shots_single_shot() {
let mut circuit = QuantumCircuit::new(1);
circuit.h(0).measure(0);
let result = Simulator::run_shots(&circuit, 1, Some(42)).unwrap();
let total: usize = result.counts.values().sum();
assert_eq!(total, 1);
}
// ---------------------------------------------------------------------------
// Memory estimation
// ---------------------------------------------------------------------------
#[test]
fn test_memory_estimate_1_qubit() {
// 2^1 = 2 complex numbers * 16 bytes (re: f64 + im: f64) = 32
assert_eq!(QuantumState::estimate_memory(1), 32);
}
#[test]
fn test_memory_estimate_10_qubits() {
// 2^10 = 1024 * 16 = 16384
assert_eq!(QuantumState::estimate_memory(10), 16384);
}
#[test]
fn test_memory_estimate_scales_exponentially() {
let m5 = QuantumState::estimate_memory(5);
let m6 = QuantumState::estimate_memory(6);
assert_eq!(m6, m5 * 2);
}
// ---------------------------------------------------------------------------
// Metrics from simulation result
// ---------------------------------------------------------------------------
#[test]
fn test_simulation_metrics_qubit_count() {
let mut circuit = QuantumCircuit::new(5);
circuit.h(0);
let result = Simulator::run(&circuit).unwrap();
assert_eq!(result.metrics.num_qubits, 5);
}
#[test]
fn test_simulation_metrics_gate_count() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0).cnot(0, 1).cnot(1, 2).x(2);
let result = Simulator::run(&circuit).unwrap();
assert!(result.metrics.gate_count >= 4);
}
#[test]
fn test_simulation_result_state_vector_length() {
let mut circuit = QuantumCircuit::new(3);
circuit.h(0);
let result = Simulator::run(&circuit).unwrap();
// 2^3 = 8 probabilities
let probs = result.state.probabilities();
assert_eq!(probs.len(), 8);
}
// ---------------------------------------------------------------------------
// Complex circuits
// ---------------------------------------------------------------------------
#[test]
fn test_qft_like_circuit() {
// Simplified QFT-like pattern: H on each, controlled rotations
let mut circuit = QuantumCircuit::new(3);
circuit
.h(0)
.rz(0, std::f64::consts::FRAC_PI_4)
.rz(0, std::f64::consts::FRAC_PI_2)
.h(1)
.rz(1, std::f64::consts::FRAC_PI_4)
.h(2)
.swap(0, 2);
let result = Simulator::run(&circuit).unwrap();
let total: f64 = result.state.probabilities().iter().sum();
assert!(approx_eq(total, 1.0));
}
#[test]
fn test_many_gate_circuit() {
// Stress test: many gates, verify normalization
let n = 5;
let mut circuit = QuantumCircuit::new(n);
for i in 0..n {
circuit.h(i);
}
for i in 0..(n - 1) {
circuit.cnot(i, i + 1);
}
for i in 0..n {
circuit.rz(i, 0.3 * (i as f64));
}
let result = Simulator::run(&circuit).unwrap();
let total: f64 = result.state.probabilities().iter().sum();
assert!(approx_eq(total, 1.0));
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,424 @@
//! Tests for ruqu_core::types — Complex arithmetic, PauliString, Hamiltonian.
use ruqu_core::types::*;
const EPSILON: f64 = 1e-10;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < EPSILON
}
// ---------------------------------------------------------------------------
// Complex basic construction
// ---------------------------------------------------------------------------
#[test]
fn test_complex_zero() {
let z = Complex { re: 0.0, im: 0.0 };
assert!(approx_eq(z.re, 0.0));
assert!(approx_eq(z.im, 0.0));
}
#[test]
fn test_complex_real_only() {
let z = Complex { re: 3.0, im: 0.0 };
assert!(approx_eq(z.re, 3.0));
assert!(approx_eq(z.im, 0.0));
}
#[test]
fn test_complex_imaginary_only() {
let z = Complex { re: 0.0, im: 4.0 };
assert!(approx_eq(z.re, 0.0));
assert!(approx_eq(z.im, 4.0));
}
// ---------------------------------------------------------------------------
// Complex arithmetic operations
// ---------------------------------------------------------------------------
#[test]
fn test_complex_addition() {
let a = Complex { re: 1.0, im: 2.0 };
let b = Complex { re: 3.0, im: -1.0 };
let c = a + b;
assert!(approx_eq(c.re, 4.0));
assert!(approx_eq(c.im, 1.0));
}
#[test]
fn test_complex_subtraction() {
let a = Complex { re: 5.0, im: 3.0 };
let b = Complex { re: 2.0, im: 7.0 };
let c = a - b;
assert!(approx_eq(c.re, 3.0));
assert!(approx_eq(c.im, -4.0));
}
#[test]
fn test_complex_multiplication() {
// (1+2i)*(3+4i) = 3+4i+6i+8i^2 = (3-8)+(4+6)i = -5+10i
let a = Complex { re: 1.0, im: 2.0 };
let b = Complex { re: 3.0, im: 4.0 };
let c = a * b;
assert!(approx_eq(c.re, -5.0));
assert!(approx_eq(c.im, 10.0));
}
#[test]
fn test_complex_multiplication_real() {
// (2+3i) * (4+0i) = 8+12i
let a = Complex { re: 2.0, im: 3.0 };
let b = Complex { re: 4.0, im: 0.0 };
let c = a * b;
assert!(approx_eq(c.re, 8.0));
assert!(approx_eq(c.im, 12.0));
}
#[test]
fn test_complex_multiplication_imaginary() {
// (0+1i) * (0+1i) = -1+0i
let a = Complex { re: 0.0, im: 1.0 };
let c = a * a;
assert!(approx_eq(c.re, -1.0));
assert!(approx_eq(c.im, 0.0));
}
#[test]
fn test_complex_negation() {
let a = Complex { re: 3.0, im: -4.0 };
let b = -a;
assert!(approx_eq(b.re, -3.0));
assert!(approx_eq(b.im, 4.0));
}
#[test]
fn test_complex_conjugate() {
let a = Complex { re: 3.0, im: 4.0 };
let c = a.conj();
assert!(approx_eq(c.re, 3.0));
assert!(approx_eq(c.im, -4.0));
}
#[test]
fn test_complex_conjugate_real() {
let a = Complex { re: 5.0, im: 0.0 };
let c = a.conj();
assert!(approx_eq(c.re, 5.0));
assert!(approx_eq(c.im, 0.0));
}
// ---------------------------------------------------------------------------
// Complex norm / magnitude
// ---------------------------------------------------------------------------
#[test]
fn test_complex_norm_sq() {
// |3+4i|^2 = 9+16 = 25
let a = Complex { re: 3.0, im: 4.0 };
assert!(approx_eq(a.norm_sq(), 25.0));
}
#[test]
fn test_complex_norm() {
// |3+4i| = 5
let a = Complex { re: 3.0, im: 4.0 };
assert!(approx_eq(a.norm(), 5.0));
}
#[test]
fn test_complex_norm_zero() {
let z = Complex { re: 0.0, im: 0.0 };
assert!(approx_eq(z.norm(), 0.0));
}
#[test]
fn test_complex_unit_norm() {
// e^{i*pi/4} has norm 1
let angle = std::f64::consts::FRAC_PI_4;
let z = Complex {
re: angle.cos(),
im: angle.sin(),
};
assert!(approx_eq(z.norm(), 1.0));
}
// ---------------------------------------------------------------------------
// Complex from_polar
// ---------------------------------------------------------------------------
#[test]
fn test_complex_from_polar_zero_angle() {
let z = Complex::from_polar(2.0, 0.0);
assert!(approx_eq(z.re, 2.0));
assert!(approx_eq(z.im, 0.0));
}
#[test]
fn test_complex_from_polar_pi_half() {
let z = Complex::from_polar(1.0, std::f64::consts::FRAC_PI_2);
assert!(approx_eq(z.re, 0.0));
assert!(approx_eq(z.im, 1.0));
}
#[test]
fn test_complex_from_polar_pi() {
let z = Complex::from_polar(1.0, std::f64::consts::PI);
assert!(approx_eq(z.re, -1.0));
assert!(approx_eq(z.im, 0.0));
}
#[test]
fn test_complex_from_polar_three_pi_half() {
let z = Complex::from_polar(1.0, 3.0 * std::f64::consts::FRAC_PI_2);
assert!(approx_eq(z.re, 0.0));
assert!(approx_eq(z.im, -1.0));
}
#[test]
fn test_complex_from_polar_roundtrip() {
let r = 3.5;
let theta = 1.23;
let z = Complex::from_polar(r, theta);
assert!(approx_eq(z.norm(), r));
}
// ---------------------------------------------------------------------------
// Complex algebraic identities
// ---------------------------------------------------------------------------
#[test]
fn test_complex_mul_conjugate_is_norm_sq() {
// z * conj(z) = |z|^2 (real)
let z = Complex { re: 2.0, im: -7.0 };
let product = z * z.conj();
assert!(approx_eq(product.re, z.norm_sq()));
assert!(approx_eq(product.im, 0.0));
}
#[test]
fn test_complex_addition_commutativity() {
let a = Complex { re: 1.5, im: -2.3 };
let b = Complex { re: -0.7, im: 4.1 };
let ab = a + b;
let ba = b + a;
assert!(approx_eq(ab.re, ba.re));
assert!(approx_eq(ab.im, ba.im));
}
#[test]
fn test_complex_multiplication_commutativity() {
let a = Complex { re: 1.5, im: -2.3 };
let b = Complex { re: -0.7, im: 4.1 };
let ab = a * b;
let ba = b * a;
assert!(approx_eq(ab.re, ba.re));
assert!(approx_eq(ab.im, ba.im));
}
#[test]
fn test_complex_distributivity() {
// a*(b+c) = a*b + a*c
let a = Complex { re: 1.0, im: 2.0 };
let b = Complex { re: 3.0, im: -1.0 };
let c = Complex { re: -2.0, im: 0.5 };
let lhs = a * (b + c);
let rhs = a * b + a * c;
assert!(approx_eq(lhs.re, rhs.re));
assert!(approx_eq(lhs.im, rhs.im));
}
// ---------------------------------------------------------------------------
// PauliOp
// ---------------------------------------------------------------------------
#[test]
fn test_pauli_op_variants_exist() {
// Ensure all four Pauli operators can be constructed.
let _i = PauliOp::I;
let _x = PauliOp::X;
let _y = PauliOp::Y;
let _z = PauliOp::Z;
}
// ---------------------------------------------------------------------------
// PauliString
// ---------------------------------------------------------------------------
#[test]
fn test_pauli_string_single_z() {
let ps = PauliString {
ops: vec![(0, PauliOp::Z)],
};
assert_eq!(ps.ops.len(), 1);
assert_eq!(ps.ops[0].0, 0);
}
#[test]
fn test_pauli_string_zz() {
let ps = PauliString {
ops: vec![(0, PauliOp::Z), (1, PauliOp::Z)],
};
assert_eq!(ps.ops.len(), 2);
}
#[test]
fn test_pauli_string_xx() {
let ps = PauliString {
ops: vec![(0, PauliOp::X), (1, PauliOp::X)],
};
assert_eq!(ps.ops.len(), 2);
}
#[test]
fn test_pauli_string_mixed() {
let ps = PauliString {
ops: vec![
(0, PauliOp::X),
(1, PauliOp::Y),
(2, PauliOp::Z),
(3, PauliOp::I),
],
};
assert_eq!(ps.ops.len(), 4);
}
#[test]
fn test_pauli_string_empty() {
// An empty Pauli string acts as the identity on all qubits.
let ps = PauliString { ops: vec![] };
assert_eq!(ps.ops.len(), 0);
}
#[test]
fn test_pauli_string_high_qubit_index() {
let ps = PauliString {
ops: vec![(15, PauliOp::Z)],
};
assert_eq!(ps.ops[0].0, 15);
}
// ---------------------------------------------------------------------------
// Hamiltonian
// ---------------------------------------------------------------------------
#[test]
fn test_hamiltonian_single_term() {
let h = Hamiltonian {
terms: vec![(
1.0,
PauliString {
ops: vec![(0, PauliOp::Z)],
},
)],
num_qubits: 1,
};
assert_eq!(h.terms.len(), 1);
assert_eq!(h.num_qubits, 1);
}
#[test]
fn test_hamiltonian_multiple_terms() {
// H = 0.5*Z0 + 0.3*Z1 + 0.2*Z0Z1
let h = Hamiltonian {
terms: vec![
(
0.5,
PauliString {
ops: vec![(0, PauliOp::Z)],
},
),
(
0.3,
PauliString {
ops: vec![(1, PauliOp::Z)],
},
),
(
0.2,
PauliString {
ops: vec![(0, PauliOp::Z), (1, PauliOp::Z)],
},
),
],
num_qubits: 2,
};
assert_eq!(h.terms.len(), 3);
assert_eq!(h.num_qubits, 2);
}
#[test]
fn test_hamiltonian_with_negative_coefficients() {
let h = Hamiltonian {
terms: vec![
(
-0.5,
PauliString {
ops: vec![(0, PauliOp::Z)],
},
),
(
1.2,
PauliString {
ops: vec![(0, PauliOp::X), (1, PauliOp::X)],
},
),
],
num_qubits: 2,
};
assert!(approx_eq(h.terms[0].0, -0.5));
assert!(approx_eq(h.terms[1].0, 1.2));
}
#[test]
fn test_hamiltonian_empty() {
let h = Hamiltonian {
terms: vec![],
num_qubits: 0,
};
assert_eq!(h.terms.len(), 0);
}
#[test]
fn test_hamiltonian_ising_model() {
// Simple 3-qubit Ising: H = -J * sum(Z_i Z_{i+1}) - h * sum(X_i)
let j = 1.0_f64;
let field = 0.5_f64;
let h = Hamiltonian {
terms: vec![
(
-j,
PauliString {
ops: vec![(0, PauliOp::Z), (1, PauliOp::Z)],
},
),
(
-j,
PauliString {
ops: vec![(1, PauliOp::Z), (2, PauliOp::Z)],
},
),
(
-field,
PauliString {
ops: vec![(0, PauliOp::X)],
},
),
(
-field,
PauliString {
ops: vec![(1, PauliOp::X)],
},
),
(
-field,
PauliString {
ops: vec![(2, PauliOp::X)],
},
),
],
num_qubits: 3,
};
assert_eq!(h.terms.len(), 5);
assert_eq!(h.num_qubits, 3);
}