use crate::error::{QuantumError, Result};
use crate::state::QuantumState;
use moonlab_sys::{
bell_get_optimal_settings, bell_measurement_settings_t, bell_test_chsh,
bell_test_mermin_ghz, bell_test_mermin_klyshko, bell_test_result_t,
bell_state_type_t, create_bell_state as ffi_create_bell_state,
quantum_entropy_ctx_create_hw, quantum_entropy_ctx_destroy,
quantum_entropy_ctx_t,
};
use std::ptr;
struct EntropyGuard {
ctx: *mut quantum_entropy_ctx_t,
}
impl EntropyGuard {
fn new() -> Result<Self> {
let ctx = unsafe { quantum_entropy_ctx_create_hw() };
if ctx.is_null() {
return Err(QuantumError::Ffi(
"quantum_entropy_ctx_create_hw returned NULL".to_string(),
));
}
Ok(Self { ctx })
}
}
impl Drop for EntropyGuard {
fn drop(&mut self) {
if !self.ctx.is_null() {
unsafe { quantum_entropy_ctx_destroy(self.ctx) };
self.ctx = ptr::null_mut();
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub enum BellState {
PhiPlus = 0,
PhiMinus = 1,
PsiPlus = 2,
PsiMinus = 3,
}
#[derive(Debug, Clone)]
pub struct BellTestResult {
pub chsh_value: f64,
pub correlation_ab: f64,
pub correlation_ab_prime: f64,
pub correlation_a_prime_b: f64,
pub correlation_a_prime_b_prime: f64,
pub classical_bound: f64,
pub quantum_bound: f64,
pub p_value: f64,
pub standard_error: f64,
pub measurements: usize,
pub violates_classical: bool,
pub confirms_quantum: bool,
pub statistically_significant: bool,
}
impl BellTestResult {
fn from_c(r: bell_test_result_t) -> Self {
Self {
chsh_value: r.chsh_value,
correlation_ab: r.correlation_ab,
correlation_ab_prime: r.correlation_ab_prime,
correlation_a_prime_b: r.correlation_a_prime_b,
correlation_a_prime_b_prime: r.correlation_a_prime_b_prime,
classical_bound: r.classical_bound,
quantum_bound: r.quantum_bound,
p_value: r.p_value,
standard_error: r.standard_error,
measurements: r.measurements,
violates_classical: r.violates_classical != 0,
confirms_quantum: r.confirms_quantum != 0,
statistically_significant: r.statistically_significant != 0,
}
}
}
pub fn create_bell_state(
state: &mut QuantumState,
qubit1: i32,
qubit2: i32,
bell: BellState,
) -> Result<()> {
let rc = unsafe {
ffi_create_bell_state(
state.as_ptr(),
qubit1,
qubit2,
bell as bell_state_type_t,
)
};
if rc != 0 {
Err(QuantumError::Ffi(format!(
"create_bell_state rc={rc}"
)))
} else {
Ok(())
}
}
pub fn chsh_test(
state: &mut QuantumState,
qubit_a: i32,
qubit_b: i32,
num_measurements: usize,
) -> Result<BellTestResult> {
let entropy = EntropyGuard::new()?;
let mut settings = bell_measurement_settings_t {
angle_a1: 0.0,
angle_a2: 0.0,
angle_b1: 0.0,
angle_b2: 0.0,
};
unsafe { bell_get_optimal_settings(&mut settings) };
let r = unsafe {
bell_test_chsh(
state.as_ptr(),
qubit_a,
qubit_b,
num_measurements,
&settings,
entropy.ctx,
)
};
drop(entropy);
Ok(BellTestResult::from_c(r))
}
pub fn mermin_ghz_test(
state: &mut QuantumState,
qubit_a: i32,
qubit_b: i32,
qubit_c: i32,
num_measurements: usize,
) -> Result<BellTestResult> {
let entropy = EntropyGuard::new()?;
let r = unsafe {
bell_test_mermin_ghz(
state.as_ptr(),
qubit_a,
qubit_b,
qubit_c,
num_measurements,
entropy.ctx,
)
};
drop(entropy);
Ok(BellTestResult::from_c(r))
}
pub fn mermin_klyshko_test(
state: &mut QuantumState,
num_qubits: usize,
num_measurements: usize,
) -> Result<f64> {
let v = unsafe {
bell_test_mermin_klyshko(
state.as_ptr(),
num_qubits,
num_measurements,
ptr::null_mut(),
)
};
Ok(v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_bell_state_phi_plus_collapses_to_aligned_probs() {
let mut state = QuantumState::new(2).unwrap();
create_bell_state(&mut state, 0, 1, BellState::PhiPlus).unwrap();
let p = state.probabilities();
assert!((p[0] - 0.5).abs() < 1e-10);
assert!(p[1].abs() < 1e-10);
assert!(p[2].abs() < 1e-10);
assert!((p[3] - 0.5).abs() < 1e-10);
}
#[test]
fn chsh_test_phi_plus_violates_classical() {
let mut state = QuantumState::new(2).unwrap();
create_bell_state(&mut state, 0, 1, BellState::PhiPlus).unwrap();
let result = chsh_test(&mut state, 0, 1, 4000).unwrap();
assert!(
result.chsh_value > 2.4,
"CHSH S = {} on |Phi+>; expected > 2.4 (classical = 2.0)",
result.chsh_value
);
assert!(result.violates_classical);
assert_eq!(result.classical_bound, 2.0);
assert!((result.quantum_bound - 2.0 * 2f64.sqrt()).abs() < 1e-12);
}
#[test]
fn mermin_ghz_three_qubit_violates_classical() {
let mut state = QuantumState::new(3).unwrap();
state.h(0).cnot(0, 1).cnot(0, 2);
let result = mermin_ghz_test(&mut state, 0, 1, 2, 4000).unwrap();
assert_eq!(result.classical_bound, 2.0);
assert_eq!(result.quantum_bound, 4.0);
assert!(
result.chsh_value.abs() > 2.5,
"|M| = {} on |GHZ_3>, expected > 2.5",
result.chsh_value
);
}
#[test]
fn mermin_klyshko_three_qubit_clears_classical_bound() {
let mut state = QuantumState::new(3).unwrap();
state.h(0).cnot(0, 1).cnot(0, 2);
let mn = mermin_klyshko_test(&mut state, 3, 4000).unwrap();
assert!(
mn > 1.1,
"|M_N| = {} on |GHZ_3>, expected > 1.1 (classical = 1.0)",
mn
);
}
}