use crate::entanglement::bell_inequality::ChshTest;
use crate::entanglement::entanglement_measures::binary_entropy;
use crate::entanglement::spdc::SpdcSource;
const FIBER_LOSS_DB_PER_KM: f64 = 0.2;
#[derive(Debug, Clone)]
pub struct Bb84Protocol {
pub key_length: usize,
pub qber: f64,
pub detection_efficiency: f64,
pub dark_count_rate: f64,
pub channel_loss_db: f64,
}
impl Bb84Protocol {
pub fn new(qber: f64, channel_loss_db: f64, efficiency: f64) -> Self {
Self {
key_length: 1_000_000,
qber: qber.clamp(0.0, 0.5),
detection_efficiency: efficiency.clamp(0.0, 1.0),
dark_count_rate: 1e-6,
channel_loss_db,
}
}
fn channel_transmittance(&self) -> f64 {
10.0_f64.powf(-self.channel_loss_db / 10.0)
}
pub fn raw_key_rate_hz(&self, pulse_rate_mhz: f64) -> f64 {
let r_pulse = pulse_rate_mhz * 1e6; let t = self.channel_transmittance();
let eta = self.detection_efficiency;
let sift = 0.5; r_pulse * t * eta * sift
}
pub fn expected_qber(&self, mean_photon_number: f64) -> f64 {
let t = self.channel_transmittance();
let eta = self.detection_efficiency;
let d = self.dark_count_rate;
let signal_rate = eta * t * mean_photon_number;
let qber_dark = if signal_rate + d > 0.0 {
d / (2.0 * (signal_rate + d))
} else {
0.0
};
(self.qber + qber_dark).clamp(0.0, 0.5)
}
pub fn secret_key_fraction(&self) -> f64 {
let r = 1.0 - 2.0 * binary_entropy(self.qber);
r.max(0.0)
}
pub fn max_tolerable_qber() -> f64 {
0.110_028 }
pub fn secure_key_rate_bps(&self, pulse_rate_mhz: f64) -> f64 {
let r_sifted = self.raw_key_rate_hz(pulse_rate_mhz);
let r_secret = self.secret_key_fraction();
r_sifted * r_secret
}
pub fn key_rate_at_distance_km(&self, distance_km: f64, pulse_rate_mhz: f64) -> f64 {
let loss_db = distance_km * FIBER_LOSS_DB_PER_KM;
let modified = Self {
channel_loss_db: loss_db,
..self.clone()
};
modified.secure_key_rate_bps(pulse_rate_mhz)
}
pub fn max_secure_distance_km(&self, pulse_rate_mhz: f64) -> f64 {
let threshold_bps = 1.0_f64; let mut lo = 0.0_f64;
let mut hi = 500.0_f64;
if self.key_rate_at_distance_km(lo, pulse_rate_mhz) < threshold_bps {
return 0.0;
}
for _ in 0..60 {
let mid = (lo + hi) / 2.0;
let rate = self.key_rate_at_distance_km(mid, pulse_rate_mhz);
if rate > threshold_bps {
lo = mid;
} else {
hi = mid;
}
}
lo
}
}
#[derive(Debug, Clone)]
pub struct E91Protocol {
pub pair_source: SpdcSource,
pub channel_loss_db_per_km: f64,
pub distance_km: f64,
pub qber: f64,
}
impl E91Protocol {
pub fn new(source: SpdcSource, loss_db_km: f64, distance_km: f64) -> Self {
Self {
pair_source: source,
channel_loss_db_per_km: loss_db_km,
distance_km,
qber: 0.03,
}
}
fn total_transmittance(&self) -> f64 {
let loss_db_total = self.channel_loss_db_per_km * self.distance_km;
10.0_f64.powf(-loss_db_total / 10.0)
}
pub fn pair_detection_rate(&self) -> f64 {
let r_pairs = self.pair_source.pair_rate_per_second();
let t = self.total_transmittance();
let eta_det = 0.8_f64;
r_pairs * t * eta_det * eta_det
}
pub fn chsh_violation(&self) -> f64 {
let f_bell = (1.0 - 2.0 * self.qber).clamp(0.0, 1.0);
ChshTest::expected_s_for_fidelity(f_bell)
}
pub fn secret_key_rate_bps(&self) -> f64 {
let r_coinc = self.pair_detection_rate();
let sift = 1.0 / 3.0;
let r_secret = (1.0 - 2.0 * binary_entropy(self.qber)).max(0.0);
r_coinc * sift * r_secret
}
pub fn eavesdropper_detectable(&self) -> bool {
self.chsh_violation() > 2.0
}
}
#[derive(Debug, Clone)]
pub struct CvQkd {
pub modulation_variance: f64,
pub channel_transmittance: f64,
pub excess_noise: f64,
pub reconciliation_efficiency: f64,
}
impl CvQkd {
pub fn new(v_a: f64, distance_km: f64, loss_db_per_km: f64, excess_noise: f64) -> Self {
let loss_db = loss_db_per_km * distance_km;
let t = 10.0_f64.powf(-loss_db / 10.0);
Self {
modulation_variance: v_a.max(0.01),
channel_transmittance: t.clamp(1e-10, 1.0),
excess_noise: excess_noise.max(0.0),
reconciliation_efficiency: 0.95,
}
}
pub fn channel_noise(&self) -> f64 {
let t = self.channel_transmittance;
(1.0 - t) / t + self.excess_noise / t
}
fn snr(&self) -> f64 {
let t = self.channel_transmittance;
let v_a = self.modulation_variance;
let chi = self.channel_noise();
t * v_a / (1.0 + t * chi).max(1e-30)
}
pub fn mutual_information_ab(&self) -> f64 {
0.5 * (1.0 + self.snr()).max(1.0).log2()
}
fn g_entropy(v: f64) -> f64 {
let v = v.max(1.0); let vp = (v + 1.0) / 2.0;
let vm = (v - 1.0) / 2.0;
let term_plus = vp * vp.log2();
let term_minus = if vm < 1e-15 { 0.0 } else { vm * vm.log2() };
term_plus - term_minus
}
pub fn holevo_information_be(&self) -> f64 {
let v_a = self.modulation_variance;
let t = self.channel_transmittance;
let xi = self.excess_noise;
let v = v_a + 1.0;
let chi_line = (1.0 - t) / t + xi;
let b_bob = t * (v + chi_line);
let c_sq = t * (v * v - 1.0);
let delta_cm = v * v + b_bob * b_bob - 2.0 * t * (v * v - 1.0);
let det_ab = v * b_bob - c_sq;
let det_ab_sq = det_ab * det_ab;
let disc = (delta_cm * delta_cm - 4.0 * det_ab_sq).max(0.0).sqrt();
let nu1 = ((delta_cm + disc) / 2.0).max(1.0).sqrt();
let nu2 = ((delta_cm - disc) / 2.0).max(1.0).sqrt();
let nu3 = (v - c_sq / b_bob.max(1e-30)).max(1.0).sqrt();
let chi_be = Self::g_entropy(nu1) + Self::g_entropy(nu2) - Self::g_entropy(nu3);
chi_be.max(0.0)
}
pub fn secret_key_rate_bits_per_mode(&self) -> f64 {
let i_ab = self.mutual_information_ab();
let chi_be = self.holevo_information_be();
(self.reconciliation_efficiency * i_ab - chi_be).max(0.0)
}
pub fn max_secure_distance_km(&self) -> f64 {
let v_a = self.modulation_variance;
let xi = self.excess_noise;
let beta = self.reconciliation_efficiency;
let loss_db_per_km = FIBER_LOSS_DB_PER_KM;
let at_zero = CvQkd {
modulation_variance: v_a,
channel_transmittance: 1.0,
excess_noise: xi,
reconciliation_efficiency: beta,
};
if at_zero.secret_key_rate_bits_per_mode() <= 0.0 {
return 0.0;
}
let mut lo = 0.0_f64;
let mut hi = 300.0_f64;
for _ in 0..80 {
let mid = (lo + hi) / 2.0;
let t_mid = 10.0_f64.powf(-loss_db_per_km * mid / 10.0);
let candidate = CvQkd {
modulation_variance: v_a,
channel_transmittance: t_mid,
excess_noise: xi,
reconciliation_efficiency: beta,
};
if candidate.secret_key_rate_bits_per_mode() > 0.0 {
lo = mid;
} else {
hi = mid;
}
}
lo
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bb84_secret_key_fraction_low_qber() {
let bb84 = Bb84Protocol::new(0.02, 3.0, 0.85);
let r = bb84.secret_key_fraction();
assert!(
r > 0.5 && r < 1.0,
"Secret key fraction at 2% QBER should be ~0.7, got {r}"
);
}
#[test]
fn test_bb84_zero_rate_above_max_qber() {
let bb84 = Bb84Protocol::new(0.12, 3.0, 0.85);
let r = bb84.secret_key_fraction();
assert_eq!(r, 0.0, "Secret key fraction should be 0 at QBER > 11%");
}
#[test]
fn test_bb84_max_tolerable_qber() {
let e_max = Bb84Protocol::max_tolerable_qber();
let h = binary_entropy(e_max);
assert!((h - 0.5).abs() < 0.01, "h(e_max) ≈ 0.5, got h={h}");
}
#[test]
fn test_bb84_key_rate_positive() {
let bb84 = Bb84Protocol::new(0.03, 10.0, 0.85);
let rate = bb84.secure_key_rate_bps(100.0);
assert!(
rate > 0.0,
"Secure key rate should be positive at low QBER and loss"
);
}
#[test]
fn test_bb84_max_distance_positive() {
let bb84 = Bb84Protocol::new(0.03, 0.0, 0.85);
let d_max = bb84.max_secure_distance_km(1000.0);
assert!(
d_max > 100.0 && d_max < 500.0,
"Max distance should be 100–500 km, got {d_max}"
);
}
#[test]
fn test_e91_chsh_violation() {
let source = SpdcSource::new_ppktp_1550(1.0, 10.0);
let e91 = E91Protocol::new(source, 0.2, 10.0);
let s = e91.chsh_violation();
assert!(s > 2.0, "E91 should violate CHSH at 3% QBER, S={s}");
}
#[test]
fn test_e91_eavesdropper_detectable() {
let source = SpdcSource::new_ppktp_1550(1.0, 10.0);
let mut e91 = E91Protocol::new(source, 0.2, 10.0);
e91.qber = 0.03;
assert!(
e91.eavesdropper_detectable(),
"E91 with low QBER should detect eavesdroppers"
);
}
#[test]
fn test_cv_qkd_channel_noise() {
let cv = CvQkd {
modulation_variance: 10.0,
channel_transmittance: 1.0,
excess_noise: 0.0,
reconciliation_efficiency: 0.95,
};
let chi = cv.channel_noise();
assert!(chi.abs() < 1e-10, "No-loss channel noise = 0, got {chi}");
}
#[test]
fn test_cv_qkd_positive_key_rate_short_distance() {
let cv = CvQkd::new(10.0, 0.1, 0.2, 0.001);
let k = cv.secret_key_rate_bits_per_mode();
assert!(
k > 0.0,
"CV-QKD should have positive key rate at 100 m, got {k}"
);
}
#[test]
fn test_cv_qkd_max_distance_sensible() {
let cv = CvQkd::new(10.0, 0.0, 0.2, 0.01);
let d_max = cv.max_secure_distance_km();
assert!(
d_max > 0.5 && d_max < 50.0,
"CV-QKD max distance should be 0.5–50 km for these params, got {d_max}"
);
}
}