use crate::{
Gate, Measurement, QuantumChannel, QuantumState,
errors::{MeasurementError, StateError},
};
use ndarray::{Array2, arr2};
use num_complex::Complex64;
pub struct B92Result {
pub raw_length: usize,
pub conclusive_count: usize,
pub check_errors: usize,
pub qber: f64,
pub eve_detected_count: usize,
pub established_key: Vec<bool>,
pub alice_bits: Vec<bool>,
pub bob_results: Vec<i8>,
}
pub fn run(
num_qubits: usize,
channel: &QuantumChannel,
measurement: &Measurement,
eve_ratio: f64,
check_ratio: f64,
) -> Result<B92Result, StateError> {
let bob_device = measurement;
let mut alice_bits = Vec::with_capacity(num_qubits);
let mut bob_results = Vec::with_capacity(num_qubits);
let mut eve_intercepted_count = 0;
for _ in 0..num_qubits {
let a_bit = crate::rng::random_bool(0.5);
let mut state = QuantumState::new(1);
if a_bit {
state.apply(&Gate::h(), &[0])?;
}
state.apply_channel(channel, &[0])?;
if eve_ratio > 1e-12 && crate::rng::random_bool(eve_ratio) {
eve_intercepted_count += 1;
let e_basis = crate::rng::random_bool(0.5);
let m = if e_basis {
Measurement::x_basis()
} else {
Measurement::z_basis()
};
let _ = state.measure(&m, &[0])?;
}
let res = state.measure(bob_device, &[0])?;
let inferred_bit_opt = match res.index {
0 => Some(true), 1 => Some(false), _ => None, };
alice_bits.push(a_bit);
let res_code = match inferred_bit_opt {
Some(true) => 0,
Some(false) => 1,
None => -1,
};
bob_results.push(res_code);
}
let mut conclusive_indices: Vec<usize> = bob_results
.iter()
.enumerate()
.filter_map(|(i, &res)| if res != -1 { Some(i) } else { None })
.collect();
let total_conclusive = conclusive_indices.len();
crate::rng::shuffle_slice(&mut conclusive_indices);
let num_check = (total_conclusive as f64 * check_ratio).round() as usize;
let (check_indices, key_indices) = conclusive_indices.split_at(num_check);
let mut check_errors = 0;
for &idx in check_indices {
let b_val = bob_results[idx] == 0; if alice_bits[idx] != b_val {
check_errors += 1;
}
}
let qber = if num_check > 0 {
check_errors as f64 / num_check as f64
} else {
0.0
};
let mut established_key = Vec::with_capacity(key_indices.len());
for &idx in key_indices {
let b_val = bob_results[idx] == 0;
established_key.push(b_val);
}
Ok(B92Result {
raw_length: num_qubits,
conclusive_count: total_conclusive,
check_errors,
qber,
eve_detected_count: eve_intercepted_count,
established_key,
alice_bits,
bob_results,
})
}
pub fn build_optimal_povm_b92() -> Result<Measurement, MeasurementError> {
let zero = Complex64::new(0.0, 0.0);
let sqrt2 = 2.0_f64.sqrt();
let a_val = sqrt2 / (1.0 + sqrt2);
let a = Complex64::new(a_val, 0.0);
let e1 = arr2(&[[zero, zero], [zero, a]]);
let half_a = Complex64::new(a_val * 0.5, 0.0);
let e2 = arr2(&[[half_a, -half_a], [-half_a, half_a]]);
let identity = Array2::<Complex64>::eye(2);
let e3 = identity - &e1 - &e2;
Measurement::from_povm(vec![e1, e2, e3], vec![1.0, 0.0, -1.0])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_b92_noiseless() {
let channel = QuantumChannel::bit_flip(0.0);
let measurement = build_optimal_povm_b92().unwrap();
let result = run(100, &channel, &measurement, 0.0, 0.5).unwrap();
assert_eq!(result.raw_length, 100);
assert_eq!(result.check_errors, 0);
assert_eq!(result.qber, 0.0);
assert_eq!(result.eve_detected_count, 0);
}
#[test]
fn test_b92_eve() {
let channel = QuantumChannel::bit_flip(0.0);
let measurement = build_optimal_povm_b92().unwrap();
let result = run(100, &channel, &measurement, 1.0, 0.5).unwrap();
assert!(result.eve_detected_count > 0);
assert!(result.qber > 0.0);
}
#[test]
fn test_b92_zero_check() {
let channel = QuantumChannel::bit_flip(0.0);
let measurement = build_optimal_povm_b92().unwrap();
let result = run(100, &channel, &measurement, 0.0, 0.0).unwrap();
assert_eq!(result.qber, 0.0);
}
}