pub struct Profile {
pub values: Vec<f64>,
pub log_scale: f64,
}
impl Profile {
pub fn new(values: Vec<f64>, log_scale: f64) -> Self {
Self { values, log_scale }
}
pub fn scale(mut self) -> Self {
let max_val = self.values.iter().copied().fold(f64::NEG_INFINITY, f64::max);
if max_val <= 0.0 {
return self;
}
let scale_factor = max_val;
for val in &mut self.values {
*val /= scale_factor;
}
self.log_scale += scale_factor.ln();
self
}
pub fn total_log_likelihood(&self) -> f64 {
let sum: f64 = self.values.iter().sum();
if sum <= 0.0 {
f64::NEG_INFINITY
} else {
sum.ln() + self.log_scale
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scaling() {
let vals = vec![1e-20, 2e-20, 3e-20];
let profile = Profile::new(vals, 0.0).scale();
assert!((profile.values[0] - 1.0 / 3.0).abs() < 1e-15);
assert!((profile.values[1] - 2.0 / 3.0).abs() < 1e-15);
assert!(profile.values[2] - 1.0 < 1e-15);
assert!((profile.log_scale - 3e-20f64.ln()).abs() < 1e-10);
}
#[test]
fn test_log_likelihood() {
let vals = vec![0.1, 0.2, 0.3, 0.4];
let scale = 10.0f64.ln();
let profile = Profile::new(vals, scale);
assert!((profile.total_log_likelihood() - scale).abs() < 1e-10);
}
}