mod chi_square_test;
mod js_divergence;
mod kl_divergence;
mod ks_test;
mod psi;
pub use chi_square_test::{chi_square_test, ChiSquareResult};
pub use js_divergence::js_divergence;
pub use kl_divergence::kl_divergence;
pub use ks_test::{ks_test, KsTestResult};
pub use psi::psi;
pub use psi::psi_with_epsilon;
pub use {js_divergence::js_divergence_with_epsilon, kl_divergence::kl_divergence_with_epsilon};
use crate::binning::Histogram;
use crate::error::{DriftError, Result};
pub const DEFAULT_EPSILON: f64 = 1e-6;
pub(crate) fn smoothed_pair(
reference: &Histogram,
live: &Histogram,
epsilon: f64,
) -> Result<(Vec<f64>, Vec<f64>)> {
if reference.len() != live.len() {
return Err(DriftError::BinCountMismatch {
reference: reference.len(),
live: live.len(),
});
}
if epsilon < 0.0 || !epsilon.is_finite() {
return Err(DriftError::InvalidConfig(format!(
"epsilon must be finite and non-negative, got {epsilon}"
)));
}
Ok((
smooth(&reference.frequencies(), epsilon),
smooth(&live.frequencies(), epsilon),
))
}
fn smooth(freqs: &[f64], epsilon: f64) -> Vec<f64> {
let n = freqs.len() as f64;
let denom = 1.0 + epsilon * n;
freqs.iter().map(|&f| (f + epsilon) / denom).collect()
}