#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
use crate::{basis::Basis, display::PolynomialDisplay, value::Value, CurveFit};
mod basic;
pub use basic::*;
pub mod shape_constraint;
pub trait ModelScoreProvider<B: Basis<T> + PolynomialDisplay<T>, T: Value>: Send + Sync {
fn minimum_significant_distance(&self) -> Option<usize>;
fn score(
&self,
model: &CurveFit<B, T>,
y: impl Iterator<Item = T>,
y_fit: impl Iterator<Item = T>,
k: T,
) -> T;
}
#[cfg(test)]
mod tests {
use crate::MonomialFit;
use super::*;
#[test]
fn scoring_perfect_fit() {
let model = MonomialFit::new(&[(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)], 2).unwrap();
let y = vec![1.0, 2.0, 3.0, 4.0];
let y_fit = y.clone();
let k = 2.0;
let aic: f64 = Aic.score(&model, y.clone().into_iter(), y_fit.clone().into_iter(), k);
let bic: f64 = Bic.score(&model, y.into_iter(), y_fit.into_iter(), k);
assert!(aic.is_finite());
assert!(bic.is_finite());
}
#[test]
fn scoring_aicc_correction() {
let model = MonomialFit::new(&[(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)], 2).unwrap();
let y = vec![1.0, 2.0, 3.0];
let y_fit = vec![1.8, 2.7, 3.6];
let k = 2.0; let score: f64 = Aic.score(&model, y.into_iter(), y_fit.into_iter(), k);
assert!(score.is_finite());
}
#[test]
fn scoring_higher_error_higher_score() {
let model = MonomialFit::new(&[(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)], 2).unwrap();
let y = vec![1.0, 2.0, 3.0];
let y_fit_good = vec![1.0, 2.0, 3.0];
let y_fit_bad = vec![0.0, 0.0, 0.0];
let k = 2.0;
let score_good = Bic.score(&model, y.clone().into_iter(), y_fit_good.into_iter(), k);
let score_bad = Bic.score(&model, y.into_iter(), y_fit_bad.into_iter(), k);
assert!(score_bad > score_good);
}
#[test]
fn scoring_empty_input_returns_nan() {
let model = MonomialFit::new(&[(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)], 2).unwrap();
let y: Vec<f64> = vec![];
let y_fit: Vec<f64> = vec![];
let k = 2.0;
let aic: f64 = Aic.score(&model, y.clone().into_iter(), y_fit.clone().into_iter(), k);
let bic: f64 = Bic.score(&model, y.into_iter(), y_fit.into_iter(), k);
assert!(aic.is_nan());
assert!(bic.is_nan());
}
#[test]
fn scoring_bic_stricter_than_aic() {
let model = MonomialFit::new(&[(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)], 2).unwrap();
let y = vec![
1.0, 2.0, 3.0, 4.0, 5.0, 1.0, 2.0, 3.0, 4.0, 5.0, 1.0, 2.0, 3.0, 4.0, 5.0, 1.0, 2.0,
3.0, 4.0, 5.0,
];
let y_fit = vec![
1.1, 2.1, 2.9, 4.0, 5.05, 1.1, 2.1, 2.9, 4.0, 5.05, 1.0, 2.0, 3.0, 4.0, 5.0, 1.0, 2.0,
3.0, 4.0, 5.0,
];
let k = 3.0;
let aic: f64 = Aic.score(&model, y.clone().into_iter(), y_fit.clone().into_iter(), k);
let bic: f64 = Bic.score(&model, y.into_iter(), y_fit.into_iter(), k);
assert!(bic >= aic);
}
}