use crate::estimate::Estimate;
use crate::landauer::LandauerCost;
use crate::landauer::H_BAR;
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ResourceTriple {
pub energy: Estimate<f64>,
pub time: Estimate<f64>,
pub space: Estimate<f64>,
}
impl ResourceTriple {
#[must_use]
pub fn validate_physics(&self) -> Vec<PhysicsViolation> {
let mut v: Vec<PhysicsViolation> = Vec::new();
if self.energy.point < 0.0 {
v.push(PhysicsViolation::NegativeEnergy);
}
if self.time.point <= 0.0 {
v.push(PhysicsViolation::NonPositiveTime);
}
if self.space.point < 0.0 {
v.push(PhysicsViolation::NegativeSpace);
}
if self.energy.point > 0.0 {
let t_min = std::f64::consts::PI * H_BAR / (2.0 * self.energy.point);
if self.time.point < t_min {
v.push(PhysicsViolation::MargolusLevitinViolated {
actual_s: self.time.point,
minimum_s: t_min,
});
}
}
v
}
#[must_use]
pub fn thermodynamic_waste(&self, landauer: &LandauerCost) -> Estimate<f64> {
Estimate {
point: self.energy.point - landauer.point,
lower: self.energy.lower - landauer.upper,
upper: self.energy.upper - landauer.lower,
}
}
}
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum PhysicsViolation {
#[error("energy is negative — violates conservation of energy")]
NegativeEnergy,
#[error("time is non-positive — violates causality")]
NonPositiveTime,
#[error("space is negative — physically impossible")]
NegativeSpace,
#[error("Margolus–Levitin violated: T={actual_s:.3e} s < T_min={minimum_s:.3e} s")]
MargolusLevitinViolated {
actual_s: f64,
minimum_s: f64,
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::estimate::Estimate;
fn valid_triple() -> ResourceTriple {
ResourceTriple {
energy: Estimate::exact(1e-19),
time: Estimate::exact(1e-9),
space: Estimate::exact(128.0),
}
}
#[test]
fn valid_triple_has_no_violations() {
assert!(valid_triple().validate_physics().is_empty());
}
#[test]
fn negative_energy_is_flagged() {
let mut t = valid_triple();
t.energy = Estimate {
point: -1.0,
lower: -2.0,
upper: 0.0,
};
let v = t.validate_physics();
assert!(v
.iter()
.any(|e| matches!(e, PhysicsViolation::NegativeEnergy)));
}
#[test]
fn zero_time_is_flagged() {
let mut t = valid_triple();
t.time = Estimate::exact(0.0);
let v = t.validate_physics();
assert!(v
.iter()
.any(|e| matches!(e, PhysicsViolation::NonPositiveTime)));
}
#[test]
fn negative_space_is_flagged() {
let mut t = valid_triple();
t.space = Estimate {
point: -1.0,
lower: -2.0,
upper: 0.0,
};
let v = t.validate_physics();
assert!(v
.iter()
.any(|e| matches!(e, PhysicsViolation::NegativeSpace)));
}
#[test]
fn thermodynamic_waste_propagates_uncertainty() {
let triple = ResourceTriple {
energy: Estimate::new(1e-19, 0.8e-19, 1.2e-19).unwrap(),
time: Estimate::exact(1e-9),
space: Estimate::exact(0.0),
};
let lambda = Estimate::new(1e-20, 0.5e-20, 2.0e-20).unwrap();
let waste = triple.thermodynamic_waste(&lambda);
assert!((waste.point - (1e-19 - 1e-20)).abs() < 1e-30);
assert!((waste.lower - (0.8e-19 - 2.0e-20)).abs() < 1e-30);
assert!((waste.upper - (1.2e-19 - 0.5e-20)).abs() < 1e-30);
}
}