#[derive(Debug, Clone)]
pub struct HomInterferometer {
pub reflectivity: f64,
pub mode_overlap: f64,
}
impl HomInterferometer {
pub fn new() -> Self {
Self {
reflectivity: 0.5,
mode_overlap: 1.0,
}
}
#[inline]
pub fn transmissivity(&self) -> f64 {
1.0 - self.reflectivity
}
pub fn coincidence_probability(&self, time_delay_s: f64, coherence_time_s: f64) -> f64 {
let r = self.reflectivity;
let t = self.transmissivity();
let v = self.mode_overlap;
let g2: f64 = if coherence_time_s > 1e-30 {
(-time_delay_s.abs() / coherence_time_s).exp()
} else {
if time_delay_s.abs() < 1e-30 {
1.0
} else {
0.0
}
};
r * r + t * t - 2.0 * r * t * v * g2
}
pub fn dip_visibility(&self) -> f64 {
let r = self.reflectivity;
let t = self.transmissivity();
let denom = r * r + t * t;
if denom < f64::EPSILON {
return 0.0;
}
2.0 * r * t * self.mode_overlap / denom
}
pub fn bunching_probability(&self) -> f64 {
(1.0 + self.mode_overlap) / 2.0
}
pub fn antibunching_probability(&self) -> f64 {
(1.0 - self.mode_overlap) / 2.0
}
pub fn indistinguishability_from_visibility(visibility: f64) -> f64 {
visibility.clamp(0.0, 1.0)
}
pub fn coherence_length_m(coherence_time_s: f64) -> f64 {
2.997_924_58e8 * coherence_time_s
}
pub fn hom_dip_hwhm_s(coherence_time_s: f64) -> f64 {
coherence_time_s * 2.0_f64.ln()
}
}
impl Default for HomInterferometer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct MultiPhotonHom {
pub n_photons: usize,
pub n_modes: usize,
pub indistinguishability: f64,
}
impl MultiPhotonHom {
pub fn new(n: usize, m: usize, indist: f64) -> Self {
Self {
n_photons: n,
n_modes: m,
indistinguishability: indist.clamp(0.0, 1.0),
}
}
pub fn n_fold_coincidence_prob(&self) -> f64 {
let n = self.n_photons;
let m = self.n_modes;
if n == 0 || m < n {
return 0.0;
}
if n == 1 {
return 1.0 / m as f64;
}
let pairs = (n * (n - 1) / 2) as f64;
let suppression = (1.0 - self.indistinguishability).powf(pairs);
let comb = combinatorial_n_choose_k(m, n) as f64;
if comb < f64::EPSILON {
return 0.0;
}
suppression / comb
}
pub fn bunching_prob(&self) -> f64 {
let n = self.n_photons;
if n == 0 {
return 0.0;
}
if n == 1 {
return 1.0 / self.n_modes as f64;
}
let pairs = (n * (n - 1) / 2) as f64;
self.indistinguishability.powf(pairs) / self.n_modes as f64
}
}
fn combinatorial_n_choose_k(n: usize, k: usize) -> u64 {
if k > n {
return 0;
}
let k = k.min(n - k);
let mut result = 1u64;
for i in 0..k {
result = result.saturating_mul(n as u64 - i as u64) / (i as u64 + 1);
}
result
}
#[derive(Debug, Clone)]
pub struct IndistinguishabilityMeasurement {
pub raw_visibility: f64,
pub classical_visibility: f64,
}
impl IndistinguishabilityMeasurement {
pub fn new(raw_vis: f64, classical_vis: f64) -> Self {
Self {
raw_visibility: raw_vis.clamp(0.0, 1.0),
classical_visibility: classical_vis.clamp(f64::EPSILON, 1.0),
}
}
pub fn indistinguishability(&self) -> f64 {
(self.raw_visibility / self.classical_visibility).clamp(0.0, 1.0)
}
pub fn from_g2(g2_zero: f64, raw_visibility: f64) -> Self {
let classical_vis = 1.0 - g2_zero / 2.0;
Self::new(raw_visibility, classical_vis.max(f64::EPSILON))
}
pub fn single_photon_purity(g2_zero: f64) -> f64 {
(1.0 - g2_zero).clamp(0.0, 1.0)
}
pub fn effective_indistinguishability(&self, g2_zero: f64) -> f64 {
let m = self.indistinguishability();
let purity = Self::single_photon_purity(g2_zero);
(m * purity).clamp(0.0, 1.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq(a: f64, b: f64, tol: f64) -> bool {
(a - b).abs() < tol
}
#[test]
fn test_hom_ideal_zero_delay() {
let hom = HomInterferometer::new();
let p = hom.coincidence_probability(0.0, 1e-12);
assert!(approx_eq(p, 0.0, 1e-12), "ideal HOM: P={p} ≠ 0");
}
#[test]
fn test_hom_large_delay() {
let hom = HomInterferometer::new();
let p = hom.coincidence_probability(1e6, 1e-12);
assert!(approx_eq(p, 0.5, 1e-12));
}
#[test]
fn test_hom_visibility_balanced_perfect() {
let hom = HomInterferometer::new();
assert!(approx_eq(hom.dip_visibility(), 1.0, 1e-12));
}
#[test]
fn test_hom_visibility_partial_indist() {
let hom = HomInterferometer {
reflectivity: 0.5,
mode_overlap: 0.8,
};
assert!(approx_eq(hom.dip_visibility(), 0.8, 1e-12));
}
#[test]
fn test_bunching_antibunching_sum_to_one() {
let hom = HomInterferometer {
reflectivity: 0.5,
mode_overlap: 0.9,
};
let sum = hom.bunching_probability() + hom.antibunching_probability();
assert!(approx_eq(sum, 1.0, 1e-12));
}
#[test]
fn test_indistinguishability_from_visibility_trivial() {
let v = HomInterferometer::indistinguishability_from_visibility(0.95);
assert!(approx_eq(v, 0.95, 1e-12));
}
#[test]
fn test_multi_photon_hom_two_photons_full_indist() {
let mhom = MultiPhotonHom::new(2, 2, 1.0);
assert!(approx_eq(mhom.bunching_prob(), 0.5, 1e-12));
assert!(mhom.n_fold_coincidence_prob() < 1e-12);
}
#[test]
fn test_multi_photon_hom_distinguishable() {
let mhom = MultiPhotonHom::new(2, 2, 0.0);
assert!(mhom.n_fold_coincidence_prob() > 0.0);
}
#[test]
fn test_indistinguishability_measurement_correction() {
let meas = IndistinguishabilityMeasurement::new(0.90, 0.95);
let m = meas.indistinguishability();
assert!(approx_eq(m, 0.90 / 0.95, 1e-10));
}
#[test]
fn test_indistinguishability_from_g2() {
let meas = IndistinguishabilityMeasurement::from_g2(0.05, 0.90);
let m = meas.indistinguishability();
assert!(approx_eq(m, 0.90 / 0.975, 1e-10));
}
#[test]
fn test_hom_dip_hwhm() {
let tau_c = 1e-12; let hwhm = HomInterferometer::hom_dip_hwhm_s(tau_c);
assert!(approx_eq(hwhm, tau_c * 2.0_f64.ln(), 1e-20));
}
}