pub fn log_gradient_resolution() -> f64 {
0.5 * f64::EPSILON.ln()
}
pub fn precision_box() -> (f64, f64) {
(log_gradient_resolution(), -log_gradient_resolution())
}
pub const LOG_STRENGTH_MIN: f64 = -700.0;
pub const LOG_STRENGTH_MAX: f64 = 700.0;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LogStrengthDomainError {
pub value: f64,
}
impl std::fmt::Display for LogStrengthDomainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"log strength must be finite and in [{LOG_STRENGTH_MIN}, {LOG_STRENGTH_MAX}]; got {}",
self.value
)
}
}
impl std::error::Error for LogStrengthDomainError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PhysicalStrengthDomainError {
pub value: f64,
}
impl std::fmt::Display for PhysicalStrengthDomainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"physical strength must be positive and finite with its logarithm in [{LOG_STRENGTH_MIN}, {LOG_STRENGTH_MAX}]; got {}",
self.value
)
}
}
impl std::error::Error for PhysicalStrengthDomainError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct IndexedLogStrengthDomainError {
pub coordinate: usize,
pub value: f64,
}
impl std::fmt::Display for IndexedLogStrengthDomainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"log strength coordinate {} must be finite and in [{LOG_STRENGTH_MIN}, {LOG_STRENGTH_MAX}]; got {}",
self.coordinate, self.value
)
}
}
impl std::error::Error for IndexedLogStrengthDomainError {}
impl From<IndexedLogStrengthDomainError> for crate::EstimationError {
fn from(error: IndexedLogStrengthDomainError) -> Self {
Self::LogStrengthDomainViolation {
coordinate: error.coordinate,
value: error.value,
lower: LOG_STRENGTH_MIN,
upper: LOG_STRENGTH_MAX,
}
}
}
#[inline]
pub fn validate_log_strength(log_strength: f64) -> Result<(), LogStrengthDomainError> {
if log_strength.is_finite() && (LOG_STRENGTH_MIN..=LOG_STRENGTH_MAX).contains(&log_strength) {
Ok(())
} else {
Err(LogStrengthDomainError {
value: log_strength,
})
}
}
pub fn validate_log_strengths(
values: impl IntoIterator<Item = f64>,
) -> Result<(), IndexedLogStrengthDomainError> {
for (coordinate, value) in values.into_iter().enumerate() {
validate_log_strength(value)
.map_err(|_| IndexedLogStrengthDomainError { coordinate, value })?;
}
Ok(())
}
pub fn checked_exp_log_strengths(
values: impl IntoIterator<Item = f64>,
) -> Result<Vec<f64>, IndexedLogStrengthDomainError> {
let mut strengths = Vec::new();
for (coordinate, value) in values.into_iter().enumerate() {
strengths.push(
checked_exp_log_strength(value)
.map_err(|_| IndexedLogStrengthDomainError { coordinate, value })?,
);
}
Ok(strengths)
}
#[inline]
pub fn checked_exp_log_strength(log_strength: f64) -> Result<f64, LogStrengthDomainError> {
validate_log_strength(log_strength)?;
Ok(log_strength.exp())
}
pub fn checked_log_strength(strength: f64) -> Result<f64, PhysicalStrengthDomainError> {
if !(strength.is_finite() && strength > 0.0) {
return Err(PhysicalStrengthDomainError { value: strength });
}
let log_strength = strength.ln();
validate_log_strength(log_strength)
.map_err(|_| PhysicalStrengthDomainError { value: strength })?;
Ok(log_strength)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exact_closed_domain_accepts_both_endpoints_without_saturation() {
for endpoint in [LOG_STRENGTH_MIN, LOG_STRENGTH_MAX] {
let strength = checked_exp_log_strength(endpoint).expect("closed endpoint");
assert_eq!(strength.to_bits(), endpoint.exp().to_bits());
assert!(strength.is_finite() && strength > 0.0);
}
}
#[test]
fn exact_closed_domain_rejects_unsupported_and_nonfinite_values() {
for value in [
LOG_STRENGTH_MIN - 1.0,
LOG_STRENGTH_MAX + 1.0,
f64::NEG_INFINITY,
f64::INFINITY,
f64::NAN,
] {
assert_eq!(
checked_exp_log_strength(value).unwrap_err().value.to_bits(),
value.to_bits()
);
}
}
#[test]
fn vector_validation_reports_the_smallest_bad_coordinate_atomically() {
let values = [0.0, LOG_STRENGTH_MAX + 1.0, f64::NAN];
let error = checked_exp_log_strengths(values).unwrap_err();
assert_eq!(error.coordinate, 1);
assert_eq!(error.value, LOG_STRENGTH_MAX + 1.0);
}
#[test]
fn physical_strength_conversion_refuses_floor_and_ceiling_cases() {
for value in [0.0, -1.0, f64::INFINITY, f64::NAN] {
assert!(checked_log_strength(value).is_err());
}
for endpoint in [LOG_STRENGTH_MIN, LOG_STRENGTH_MAX] {
assert!(checked_log_strength(endpoint.exp()).is_ok());
}
}
}