use crate::numerical_derivative::derivator::DerivatorMultiVariable;
use crate::utils::error_codes::CalcError;
#[derive(Debug, Clone, Copy)]
pub struct LinearApproximation<const NUM_VARS: usize> {
point: [f64; NUM_VARS],
value: f64,
gradient: [f64; NUM_VARS],
}
#[derive(Debug, Clone, Copy)]
pub struct LinearApproximationPredictionMetrics {
pub mean_absolute_error: f64,
pub mean_squared_error: f64,
pub root_mean_squared_error: f64,
pub r_squared: f64,
pub adjusted_r_squared: f64,
}
impl<const NUM_VARS: usize> LinearApproximation<NUM_VARS> {
pub fn predict(&self, x: &[f64; NUM_VARS]) -> f64 {
let mut result = self.value;
for ((&g, &xi), &pi) in self.gradient.iter().zip(x).zip(&self.point) {
result += g * (xi - pi);
}
result
}
pub fn point(&self) -> &[f64; NUM_VARS] {
&self.point
}
pub fn coefficients(&self) -> &[f64; NUM_VARS] {
&self.gradient
}
pub fn intercept(&self) -> f64 {
let mut intercept = self.value;
for i in 0..NUM_VARS {
intercept -= self.gradient[i] * self.point[i];
}
intercept
}
pub fn get_prediction_metrics<O: Fn(&[f64; NUM_VARS]) -> f64, const NUM_POINTS: usize>(
&self,
points: &[[f64; NUM_VARS]; NUM_POINTS],
original_function: &O,
) -> LinearApproximationPredictionMetrics {
let (mae, mse, rmse, r_squared, adjusted_r_squared) = crate::approximation::compute_metrics(
|x| self.predict(x),
points,
original_function,
NUM_VARS, );
LinearApproximationPredictionMetrics {
mean_absolute_error: mae,
mean_squared_error: mse,
root_mean_squared_error: rmse,
r_squared,
adjusted_r_squared,
}
}
}
pub struct LinearApproximator<D: DerivatorMultiVariable> {
derivator: D,
}
impl<D: DerivatorMultiVariable + Default> Default for LinearApproximator<D> {
fn default() -> Self {
LinearApproximator {
derivator: D::default(),
}
}
}
impl<D: DerivatorMultiVariable> LinearApproximator<D> {
pub fn from_derivator(derivator: D) -> Self {
LinearApproximator { derivator }
}
pub fn get<F: Fn(&[f64; NUM_VARS]) -> f64, const NUM_VARS: usize>(
&self,
function: &F,
point: &[f64; NUM_VARS],
) -> Result<LinearApproximation<NUM_VARS>, CalcError> {
let value = function(point);
let mut gradient = [0.0; NUM_VARS];
for (i, slot) in gradient.iter_mut().enumerate() {
*slot = self.derivator.get_single_partial(function, i, point)?;
}
Ok(LinearApproximation {
point: *point,
value,
gradient,
})
}
}