use num_complex::Complex;
use crate::error::{QuantumError, QuantumResult};
use crate::gates::hadamard::{gate_h, gate_sdg};
use crate::statevec::apply_1q::{apply_1q_controlled, apply_1q_inplace};
use crate::statevec::state::StateVector;
type Complex32 = Complex<f32>;
fn apply_cswap(sv: &mut StateVector, ctrl: usize, q1: usize, q2: usize) -> QuantumResult<()> {
let n = sv.n_qubits;
for &qq in &[ctrl, q1, q2] {
if qq >= n {
return Err(QuantumError::QubitIndexOutOfRange {
index: qq,
n_qubits: n,
});
}
}
if ctrl == q1 || ctrl == q2 || q1 == q2 {
return Err(QuantumError::InvalidParameter {
name: "ctrl, q1, q2 must be distinct".into(),
});
}
let cm = 1usize << ctrl;
let m1 = 1usize << q1;
let m2 = 1usize << q2;
let dim = sv.amps.len();
for i in 0..dim {
if (i & cm) != 0 && (i & m1) != 0 && (i & m2) == 0 {
let j = (i & !m1) | m2;
sv.amps.swap(i, j);
}
}
Ok(())
}
fn tensor_with_ancilla(psi1: &StateVector, psi2: &StateVector) -> StateVector {
let k = psi1.n_qubits;
let n = 1 + 2 * k;
let dim = 1usize << n;
let mut amps = vec![Complex32::new(0.0, 0.0); dim];
for (i1, &a1) in psi1.amps.iter().enumerate() {
for (i2, &a2) in psi2.amps.iter().enumerate() {
let global = (i1 << 1) | (i2 << (1 + k));
amps[global] = a1 * a2;
}
}
StateVector { amps, n_qubits: n }
}
pub fn swap_test(psi1: &StateVector, psi2: &StateVector) -> QuantumResult<f32> {
if psi1.n_qubits != psi2.n_qubits {
return Err(QuantumError::DimensionMismatch {
expected: psi1.n_qubits,
got: psi2.n_qubits,
});
}
let k = psi1.n_qubits;
if k == 0 {
return Err(QuantumError::InvalidQubitCount { n: 0 });
}
let n = 1 + 2 * k;
if n > 30 {
return Err(QuantumError::InvalidQubitCount { n });
}
let mut sv = tensor_with_ancilla(psi1, psi2);
apply_1q_inplace(&mut sv, 0, &gate_h())?;
for j in 0..k {
apply_cswap(&mut sv, 0, 1 + j, 1 + k + j)?;
}
apply_1q_inplace(&mut sv, 0, &gate_h())?;
let p0 = sv.measure_prob(0, false)?;
Ok((2.0 * p0 - 1.0).clamp(0.0, 1.0))
}
pub fn hadamard_test(
psi: &StateVector,
gate: &[[Complex32; 2]; 2],
imaginary: bool,
) -> QuantumResult<f32> {
if psi.n_qubits != 1 {
return Err(QuantumError::InvalidQubitCount { n: psi.n_qubits });
}
let mut amps = vec![Complex32::new(0.0, 0.0); 4];
amps[0] = psi.amps[0]; amps[2] = psi.amps[1]; let mut sv = StateVector { amps, n_qubits: 2 };
apply_1q_inplace(&mut sv, 0, &gate_h())?;
if imaginary {
apply_1q_inplace(&mut sv, 0, &gate_sdg())?;
}
apply_1q_controlled(&mut sv, 0, 1, gate)?;
apply_1q_inplace(&mut sv, 0, &gate_h())?;
let p0 = sv.measure_prob(0, false)?;
Ok(2.0 * p0 - 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gates::controlled::apply_cnot;
use crate::gates::hadamard::gate_s;
use crate::gates::pauli::{gate_x, gate_z};
fn ket_zero(n: usize) -> StateVector {
StateVector::new_zero_state(n).expect("valid n-qubit zero state")
}
fn ket_one() -> StateVector {
let mut sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
apply_1q_inplace(&mut sv, 0, &gate_x()).expect("X gate applied to qubit 0");
sv
}
fn ket_plus() -> StateVector {
let mut sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
apply_1q_inplace(&mut sv, 0, &gate_h()).expect("H gate applied to qubit 0");
sv
}
#[test]
fn swap_test_identical_states_overlap_one() {
let a = ket_zero(1);
let b = ket_zero(1);
let ov = swap_test(&a, &b).expect("swap test on identical 1-qubit states");
assert!((ov - 1.0).abs() < 1e-5, "overlap={ov}");
}
#[test]
fn swap_test_orthogonal_states_overlap_zero() {
let a = ket_zero(1);
let b = ket_one();
let ov = swap_test(&a, &b).expect("swap test on orthogonal 1-qubit states");
assert!(ov.abs() < 1e-5, "overlap={ov}");
}
#[test]
fn swap_test_zero_and_plus_overlap_half() {
let a = ket_zero(1);
let b = ket_plus();
let ov = swap_test(&a, &b).expect("swap test on |0⟩ and |+⟩ states");
assert!((ov - 0.5).abs() < 1e-5, "overlap={ov}");
}
#[test]
fn swap_test_two_qubit_identical_overlap_one() {
let mut a = StateVector::new_zero_state(2).expect("valid 2-qubit zero state");
apply_1q_inplace(&mut a, 0, &gate_h()).expect("H gate applied to qubit 0");
apply_cnot(&mut a, 0, 1).expect("CNOT with control 0 target 1");
let b = a.clone();
let ov = swap_test(&a, &b).expect("swap test on identical 2-qubit Bell states");
assert!((ov - 1.0).abs() < 1e-5, "overlap={ov}");
}
#[test]
fn swap_test_two_qubit_orthogonal_overlap_zero() {
let a = ket_zero(2); let mut b = StateVector::new_zero_state(2).expect("valid 2-qubit zero state");
apply_1q_inplace(&mut b, 1, &gate_x()).expect("X gate applied to qubit 1"); let ov = swap_test(&a, &b).expect("swap test on orthogonal 2-qubit states");
assert!(ov.abs() < 1e-5, "overlap={ov}");
}
#[test]
fn swap_test_mismatched_sizes_error() {
let a = ket_zero(1);
let b = ket_zero(2);
assert!(swap_test(&a, &b).is_err());
}
#[test]
fn hadamard_test_z_on_zero_is_plus_one() {
let psi = ket_zero(1);
let re = hadamard_test(&psi, &gate_z(), false)
.expect("Hadamard test with Z gate on |0⟩, real part");
assert!((re - 1.0).abs() < 1e-5, "re={re}");
}
#[test]
fn hadamard_test_z_on_plus_is_zero() {
let psi = ket_plus();
let re = hadamard_test(&psi, &gate_z(), false)
.expect("Hadamard test with Z gate on |+⟩, real part");
assert!(re.abs() < 1e-5, "re={re}");
}
#[test]
fn hadamard_test_x_on_zero_is_zero() {
let psi = ket_zero(1);
let re = hadamard_test(&psi, &gate_x(), false)
.expect("Hadamard test with X gate on |0⟩, real part");
assert!(re.abs() < 1e-5, "re={re}");
}
#[test]
fn hadamard_test_s_on_plus_real_and_imag() {
let psi = ket_plus();
let re = hadamard_test(&psi, &gate_s(), false)
.expect("Hadamard test with S gate on |+⟩, real part");
let im = hadamard_test(&psi, &gate_s(), true)
.expect("Hadamard test with S gate on |+⟩, imaginary part");
assert!((re - 0.5).abs() < 1e-5, "re={re}");
assert!((im - 0.5).abs() < 1e-5, "im={im}");
}
#[test]
fn hadamard_test_requires_single_qubit() {
let psi = ket_zero(2);
assert!(hadamard_test(&psi, &gate_z(), false).is_err());
}
}