pub const K_BOLTZMANN: f64 = 1.380_649e-23_f64;
pub const LN2: f64 = core::f64::consts::LN_2;
pub const T_ROOM_K: f32 = 290.0;
pub const LANDAUER_ROOM_J: f64 = K_BOLTZMANN * 290.0 * LN_2_F64;
const LN_2_F64: f64 = core::f64::consts::LN_2;
#[inline]
pub fn thermal_noise_power(temp_k: f32, bandwidth_hz: f32) -> f32 {
(K_BOLTZMANN as f32) * temp_k * bandwidth_hz
}
#[inline]
pub fn gaussian_entropy_nats(sigma_sq: f32) -> f32 {
use crate::math::ln_f32;
let two_pi_e: f32 = 2.0 * core::f32::consts::PI * core::f32::consts::E;
0.5 * ln_f32(two_pi_e * sigma_sq.max(1e-30))
}
#[inline]
pub fn excess_entropy_nats(obs_sigma_sq: f32, thermal_sigma_sq: f32) -> f32 {
let h_obs = gaussian_entropy_nats(obs_sigma_sq);
let h_th = gaussian_entropy_nats(thermal_sigma_sq.max(1e-30));
(h_obs - h_th).max(0.0)
}
#[inline]
pub fn structural_energy_joules(
obs_sigma_sq: f32,
thermal_sigma_sq: f32,
temp_k: f32,
) -> f32 {
let h_excess = excess_entropy_nats(obs_sigma_sq, thermal_sigma_sq);
(K_BOLTZMANN as f32) * temp_k * h_excess
}
#[inline]
pub fn structural_power_watts(
obs_sigma_sq: f32,
thermal_sigma_sq: f32,
temp_k: f32,
fs_hz: f32,
) -> f32 {
structural_energy_joules(obs_sigma_sq, thermal_sigma_sq, temp_k) * fs_hz
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LandauerClass {
SubThermal,
Thermal,
MildBurden,
ModerateBurden,
SevereBurden,
}
#[derive(Debug, Clone, Copy)]
pub struct LandauerAudit {
pub obs_sigma_sq: f32,
pub thermal_sigma_sq: f32,
pub excess_nats: f32,
pub energy_joules: f32,
pub power_watts: f32,
pub class: LandauerClass,
pub entropy_ratio: f32,
}
impl LandauerAudit {
pub const DISCLAIMER: &'static str =
"Landauer bound is a thermodynamic minimum; actual DSP power \
exceeds this by ~10^20. Use as relative comparative metric only.";
}
pub fn landauer_audit(
obs_sigma_sq: f32,
bandwidth_hz: f32,
temp_k: f32,
fs_hz: f32,
) -> LandauerAudit {
let thermal_sigma_sq = thermal_noise_power(temp_k, bandwidth_hz);
let excess_nats = excess_entropy_nats(obs_sigma_sq, thermal_sigma_sq);
let energy_joules = structural_energy_joules(obs_sigma_sq, thermal_sigma_sq, temp_k);
let power_watts = energy_joules * fs_hz;
let entropy_ratio = obs_sigma_sq / thermal_sigma_sq.max(1e-30);
let class = if obs_sigma_sq <= thermal_sigma_sq {
LandauerClass::SubThermal
} else if excess_nats < 0.1 {
LandauerClass::Thermal
} else if excess_nats < 1.0 {
LandauerClass::MildBurden
} else if excess_nats < 5.0 {
LandauerClass::ModerateBurden
} else {
LandauerClass::SevereBurden
};
LandauerAudit {
obs_sigma_sq,
thermal_sigma_sq,
excess_nats,
energy_joules,
power_watts,
class,
entropy_ratio,
}
}
pub fn cumulative_energy(audits: &[LandauerAudit]) -> f32 {
audits.iter().map(|a| a.energy_joules).sum()
}
pub fn peak_power(audits: &[LandauerAudit]) -> f32 {
audits.iter().map(|a| a.power_watts).fold(0.0_f32, f32::max)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn thermal_noise_power_room_temp() {
let p = thermal_noise_power(290.0, 1e6);
assert!(p > 3.5e-15 && p < 4.5e-15,
"thermal noise power at 290K/1MHz: {:.3e}", p);
}
#[test]
fn gaussian_entropy_increases_with_variance() {
let h_small = gaussian_entropy_nats(0.001);
let h_large = gaussian_entropy_nats(0.1);
assert!(h_large > h_small, "entropy must increase with variance");
}
#[test]
fn excess_entropy_zero_at_thermal_floor() {
let sigma_sq = 1e-12_f32;
let excess = excess_entropy_nats(sigma_sq, sigma_sq);
assert!(excess.abs() < 1e-5, "no excess at thermal floor: {}", excess);
}
#[test]
fn excess_entropy_positive_above_floor() {
let thermal = 1e-12_f32;
let obs = 1e-9_f32; let excess = excess_entropy_nats(obs, thermal);
assert!(excess > 3.0, "excess nats >> 0 when obs >> thermal: {}", excess);
}
#[test]
fn structural_energy_increases_with_obs_variance() {
let thermal = 1e-12_f32;
let e_mild = structural_energy_joules(1e-11, thermal, 290.0);
let e_severe = structural_energy_joules(1e-9, thermal, 290.0);
assert!(e_severe > e_mild, "more obs variance → more Landauer burden");
assert!(e_mild > 0.0, "non-zero burden above thermal floor");
}
#[test]
fn sub_thermal_gives_no_energy() {
let thermal = 1e-12_f32;
let obs = 1e-13_f32; let e = structural_energy_joules(obs, thermal, 290.0);
assert_eq!(e, 0.0, "sub-thermal residual must produce zero Landauer energy");
}
#[test]
fn landauer_audit_class_severe_at_high_snr() {
let audit = landauer_audit(1e-9, 1e6, 290.0, 1e6);
assert_eq!(audit.class, LandauerClass::SevereBurden,
"1000x above thermal must be SevereBurden: {:?}", audit.class);
assert!(audit.entropy_ratio > 100.0,
"entropy_ratio must be > 100: {}", audit.entropy_ratio);
}
#[test]
fn landauer_audit_class_thermal_at_floor() {
let audit = landauer_audit(
thermal_noise_power(290.0, 1e6) * 1.01, 1e6, 290.0, 1e6,
);
assert!(
matches!(audit.class, LandauerClass::Thermal | LandauerClass::MildBurden),
"just above floor: {:?}", audit.class
);
}
#[test]
fn cumulative_energy_sums_correctly() {
let a = landauer_audit(1e-10, 1e6, 290.0, 1e6);
let b = landauer_audit(1e-9, 1e6, 290.0, 1e6);
let total = cumulative_energy(&[a, b]);
let diff = (total - a.energy_joules - b.energy_joules).abs();
let scale = a.energy_joules + b.energy_joules;
assert!(diff < scale * 1e-4 + 1e-30,
"cumulative mismatch: diff={:.3e} scale={:.3e}", diff, scale);
}
#[test]
fn peak_power_selects_maximum() {
let a = landauer_audit(1e-10, 1e6, 290.0, 2e6);
let b = landauer_audit(1e-9, 1e6, 290.0, 2e6);
let peak = peak_power(&[a, b]);
assert!((peak - b.power_watts).abs() < b.power_watts * 1e-4 + 1e-30,
"peak must be the higher-variance audit");
}
#[test]
fn landauer_room_constant_reasonable() {
assert!(LANDAUER_ROOM_J > 2.5e-21 && LANDAUER_ROOM_J < 3.2e-21,
"Landauer room constant: {:.3e}", LANDAUER_ROOM_J);
}
}