use std::f64::consts::PI;
const C_LIGHT: f64 = 2.997_924_58e8;
const _EPS0: f64 = 8.854_187_817e-12;
#[derive(Debug, Clone)]
pub struct JaynesCummings {
pub omega_cavity: f64,
pub omega_atom: f64,
pub coupling_g: f64,
pub kappa: f64,
pub gamma: f64,
}
impl JaynesCummings {
#[inline]
pub fn vacuum_rabi_splitting(&self) -> f64 {
2.0 * self.coupling_g
}
#[inline]
pub fn detuning(&self) -> f64 {
self.omega_atom - self.omega_cavity
}
pub fn is_strong_coupling(&self) -> bool {
let max_rate = self.kappa.max(self.gamma);
self.coupling_g > max_rate / 2.0
}
pub fn dressed_state_energies(&self, n: usize) -> (f64, f64) {
let delta = self.detuning();
let generalized_rabi = (self.coupling_g * self.coupling_g * (n + 1) as f64
+ delta * delta / 4.0)
.sqrt();
let center = (n as f64 + 0.5) * self.omega_cavity + self.omega_atom / 2.0;
(center - generalized_rabi, center + generalized_rabi)
}
pub fn n1_splitting(&self) -> f64 {
let delta = self.detuning();
2.0 * (self.coupling_g * self.coupling_g + delta * delta / 4.0).sqrt()
}
pub fn purcell_factor(
&self,
q_factor: f64,
mode_volume_m3: f64,
n_medium: f64,
wavelength_m: f64,
) -> f64 {
if mode_volume_m3 <= 0.0 || n_medium <= 0.0 || wavelength_m <= 0.0 {
return 0.0;
}
let lambda_n = wavelength_m / n_medium;
let cubic_wavelength = lambda_n.powi(3);
(3.0 / (4.0 * PI * PI)) * (cubic_wavelength / mode_volume_m3) * q_factor
}
pub fn enhanced_decay_rate(&self, purcell_factor: f64, gamma0: f64) -> f64 {
purcell_factor * gamma0
}
pub fn cooperativity(&self) -> f64 {
if self.kappa * self.gamma < f64::EPSILON {
return f64::INFINITY;
}
self.coupling_g * self.coupling_g / (self.kappa * self.gamma)
}
pub fn transmission_spectrum(
&self,
omega_range: (f64, f64),
n_points: usize,
) -> Vec<(f64, f64)> {
let n = n_points.max(2);
let (e_minus, e_plus) = self.dressed_state_energies(0);
let half_kappa = self.kappa / 2.0;
let decay_sum = half_kappa + self.gamma / 4.0;
let mut result = Vec::with_capacity(n);
let mut t_max = 0.0_f64;
for i in 0..n {
let omega = omega_range.0
+ (omega_range.1 - omega_range.0) * (i as f64) / ((n - 1) as f64);
let lorentz = |omega_res: f64| -> f64 {
let den = (omega - omega_res).powi(2) + decay_sum.powi(2);
half_kappa * half_kappa / den
};
let t = lorentz(e_minus) + lorentz(e_plus);
result.push((omega, t));
if t > t_max {
t_max = t;
}
}
if t_max > 0.0 {
for entry in &mut result {
entry.1 /= t_max;
}
}
result
}
pub fn has_photon_blockade(&self) -> bool {
self.coupling_g > self.kappa
}
pub fn energy_ladder(&self, n_max: usize) -> Vec<(f64, f64)> {
(0..=n_max)
.map(|n| self.dressed_state_energies(n))
.collect()
}
pub fn vacuum_rabi_oscillation(&self, t_s: f64) -> f64 {
let delta = self.detuning();
if delta.abs() < f64::EPSILON * self.omega_cavity {
(self.coupling_g * t_s).cos().powi(2)
} else {
let omega_gen = (self.coupling_g * self.coupling_g + delta * delta / 4.0).sqrt();
(omega_gen * t_s).cos().powi(2)
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CavityType {
FabryPerot,
Microsphere,
PhotonicCrystal,
Microtoroid,
SuperconductingCircuit,
}
#[derive(Debug, Clone)]
pub struct CavityQedSystem {
pub system_type: CavityType,
pub omega_0: f64,
pub g_coupling: f64,
pub kappa: f64,
pub gamma: f64,
pub q_factor: f64,
pub mode_volume_lambda3: f64,
}
impl CavityType {
pub fn typical_parameters(&self) -> CavityQedSystem {
let two_pi = 2.0 * PI;
match self {
CavityType::FabryPerot => {
let omega_0 = two_pi * 3.5e14; CavityQedSystem {
system_type: CavityType::FabryPerot,
omega_0,
g_coupling: two_pi * 16e6,
kappa: two_pi * 4e6,
gamma: two_pi * 2.6e6,
q_factor: omega_0 / (two_pi * 4e6),
mode_volume_lambda3: 1e6, }
}
CavityType::Microsphere => {
let omega_0 = two_pi * C_LIGHT / 1.3e-6;
let kappa = omega_0 / 1e8;
CavityQedSystem {
system_type: CavityType::Microsphere,
omega_0,
g_coupling: two_pi * 1e6,
kappa,
gamma: two_pi * 1e6,
q_factor: 1e8,
mode_volume_lambda3: 1e3,
}
}
CavityType::PhotonicCrystal => {
let omega_0 = two_pi * C_LIGHT / 920e-9; let kappa = omega_0 / 1e4;
CavityQedSystem {
system_type: CavityType::PhotonicCrystal,
omega_0,
g_coupling: two_pi * 40e9,
kappa,
gamma: two_pi * 1e9,
q_factor: 1e4,
mode_volume_lambda3: 0.4, }
}
CavityType::Microtoroid => {
let omega_0 = two_pi * C_LIGHT / 780e-9; let kappa = omega_0 / 1e8;
CavityQedSystem {
system_type: CavityType::Microtoroid,
omega_0,
g_coupling: two_pi * 70e6,
kappa,
gamma: two_pi * 3e6,
q_factor: 1e8,
mode_volume_lambda3: 2e3,
}
}
CavityType::SuperconductingCircuit => {
let omega_0 = two_pi * 6e9; let kappa = two_pi * 1e6; CavityQedSystem {
system_type: CavityType::SuperconductingCircuit,
omega_0,
g_coupling: two_pi * 200e6,
kappa,
gamma: two_pi * 1e4, q_factor: omega_0 / kappa,
mode_volume_lambda3: f64::NAN, }
}
}
}
pub fn as_jaynes_cummings(&self) -> JaynesCummings {
let p = self.typical_parameters();
JaynesCummings {
omega_cavity: p.omega_0,
omega_atom: p.omega_0, coupling_g: p.g_coupling,
kappa: p.kappa,
gamma: p.gamma,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn phc_jc() -> JaynesCummings {
let two_pi = 2.0 * PI;
let omega_c = two_pi * C_LIGHT / 920e-9;
let q = 1e4_f64;
JaynesCummings {
omega_cavity: omega_c,
omega_atom: omega_c, coupling_g: two_pi * 40e9,
kappa: omega_c / q,
gamma: two_pi * 1e9,
}
}
#[test]
fn jaynes_cummings_strong_coupling() {
let jc = phc_jc();
assert!(jc.is_strong_coupling(), "PhC nanocavity should be in strong coupling");
assert!(
jc.cooperativity() > 10.0,
"Cooperativity should be >10, got {}",
jc.cooperativity()
);
}
#[test]
fn vacuum_rabi_splitting_is_2g() {
let jc = phc_jc();
let vrs = jc.vacuum_rabi_splitting();
assert!((vrs - 2.0 * jc.coupling_g).abs() < 1e-6);
}
#[test]
fn dressed_state_resonant_n0_splitting() {
let jc = phc_jc();
let (e_minus, e_plus) = jc.dressed_state_energies(0);
let splitting = e_plus - e_minus;
assert!(
(splitting - 2.0 * jc.coupling_g).abs() / jc.coupling_g < 1e-10,
"n=0 splitting should be 2g, got {}",
splitting
);
}
#[test]
fn cooperativity_superconducting_circuit() {
let jc = CavityType::SuperconductingCircuit.as_jaynes_cummings();
let c = jc.cooperativity();
assert!(c > 100.0, "SC circuit cooperativity should be >100, got {}", c);
}
#[test]
fn photon_blockade_strong_coupling() {
let jc = phc_jc();
assert!(jc.has_photon_blockade(), "PhC should exhibit photon blockade");
}
#[test]
fn vacuum_rabi_oscillation_resonant() {
let two_pi = 2.0 * PI;
let omega_c = 2.0e15;
let jc = JaynesCummings {
omega_cavity: omega_c,
omega_atom: omega_c, coupling_g: two_pi * 1e9, kappa: two_pi * 1e6,
gamma: two_pi * 1e6,
};
let p0 = jc.vacuum_rabi_oscillation(0.0);
assert!((p0 - 1.0).abs() < 1e-10, "P_e(0) should be 1, got {}", p0);
let t_pi_half = PI / (2.0 * two_pi * 1e9);
let p_half = jc.vacuum_rabi_oscillation(t_pi_half);
assert!(p_half.abs() < 1e-6, "P_e(π/2g) should be ~0, got {}", p_half);
}
#[test]
fn energy_ladder_anharmonicity() {
let jc = phc_jc();
let ladder = jc.energy_ladder(3);
assert_eq!(ladder.len(), 4); for (n, (e_minus, e_plus)) in ladder.iter().enumerate() {
assert!(e_plus > e_minus, "E+ <= E- at n={}", n);
}
let split_0 = ladder[0].1 - ladder[0].0;
let split_1 = ladder[1].1 - ladder[1].0;
let ratio = split_1 / split_0;
assert!(
(ratio - 2.0_f64.sqrt()).abs() < 1e-6,
"JC anharmonicity ratio should be √2 at resonance, got {}",
ratio
);
}
#[test]
fn transmission_spectrum_two_peaks() {
let jc = phc_jc();
let omega_c = jc.omega_cavity;
let span = 10.0 * jc.vacuum_rabi_splitting();
let spectrum = jc.transmission_spectrum((omega_c - span, omega_c + span), 2000);
let mut peaks = 0usize;
for i in 1..spectrum.len() - 1 {
let (_, t_prev) = spectrum[i - 1];
let (_, t_curr) = spectrum[i];
let (_, t_next) = spectrum[i + 1];
if t_curr > t_prev && t_curr > t_next && t_curr > 0.3 {
peaks += 1;
}
}
assert_eq!(peaks, 2, "Expected 2 transmission peaks (vacuum Rabi doublet), found {}", peaks);
}
}