use crate::types::{Real, Size};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PolynomialType {
Monomial,
}
fn monomial(order: Size, x: Real) -> Real {
let mut ret = 1.0;
for _ in 0..order {
ret *= x;
}
ret
}
pub struct LsmBasisSystem;
impl LsmBasisSystem {
pub fn path_basis_system(
order: Size,
poly_type: PolynomialType,
) -> Vec<Box<dyn Fn(Real) -> Real>> {
match poly_type {
PolynomialType::Monomial => (0..=order)
.map(|i| Box::new(move |x: Real| monomial(i, x)) as Box<dyn Fn(Real) -> Real>)
.collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn monomials_of_order_three_evaluate_to_the_powers() {
let basis = LsmBasisSystem::path_basis_system(3, PolynomialType::Monomial);
assert_eq!(basis.len(), 4, "order n yields n+1 functions");
for x in [0.5, 1.7, -2.0] {
let expected = [1.0, x, x * x, x * x * x];
for (i, (f, want)) in basis.iter().zip(expected).enumerate() {
assert!(
(f(x) - want).abs() <= 1e-15 * want.abs(),
"basis[{i}]({x}) = {}, expected {want}",
f(x)
);
}
}
}
#[test]
fn order_zero_is_the_constant_one() {
let basis = LsmBasisSystem::path_basis_system(0, PolynomialType::Monomial);
assert_eq!(basis.len(), 1);
for x in [0.0, 0.5, -3.25] {
assert_eq!(basis[0](x), 1.0);
}
}
#[test]
fn the_basis_drives_a_least_squares_fit() {
use crate::math::generallinearleastsquares::GeneralLinearLeastSquares;
let basis = LsmBasisSystem::path_basis_system(2, PolynomialType::Monomial);
let x = [-2.0, -1.0, 0.0, 1.0, 2.0, 3.0];
let y: Vec<Real> = x.iter().map(|x| 3.0 - 2.0 * x + 0.5 * x * x).collect();
let fit = GeneralLinearLeastSquares::new(&x, &y, &basis).unwrap();
let c = fit.coefficients();
assert!((c[0] - 3.0).abs() < 1e-12, "constant term {}", c[0]);
assert!((c[1] + 2.0).abs() < 1e-12, "linear term {}", c[1]);
assert!((c[2] - 0.5).abs() < 1e-12, "quadratic term {}", c[2]);
}
}