use nalgebra::DMatrix;
use num_traits::ToPrimitive;
use r2rs_nmath::{distribution::TBuilder, traits::Distribution};
use strafe_type::{Alpha64, FloatConstraint, Rational64, Real64};
use crate::funcs::residual_sum_of_squares;
pub fn predict(x: &DMatrix<f64>, b: &DMatrix<f64>) -> DMatrix<f64> {
x * b
}
fn interval_inner<A: Into<Alpha64>>(
x: &DMatrix<f64>,
y: &DMatrix<f64>,
b: &DMatrix<f64>,
w: &DMatrix<f64>,
alpha: A,
scale: Option<f64>,
df: Option<Rational64>,
) -> (DMatrix<f64>, Real64, f64) {
let mut sqrt_w = w.to_owned();
sqrt_w
.column_iter_mut()
.for_each(|mut r| r.iter_mut().for_each(|w| *w = w.sqrt()));
let mut weighted_x = x.clone();
weighted_x
.column_iter_mut()
.for_each(|mut row| row.component_mul_assign(&sqrt_w));
let degrees_of_freedom = x.shape().0 - b.shape().0;
let residual_variance = if let Some(scale) = scale {
scale.powi(2)
} else {
let residual_sum_square = residual_sum_of_squares(x, y, b);
residual_sum_square / degrees_of_freedom as f64
};
let mut xr_inverse = x.clone().qr().q();
let ip =
xr_inverse.component_mul(&xr_inverse) * DMatrix::repeat(x.ncols(), 1, residual_variance);
let mut t_builder = TBuilder::new();
if let Some(df) = df {
t_builder.with_df(df.to_f64().unwrap()).unwrap();
} else {
t_builder.with_df(degrees_of_freedom).unwrap();
}
let t = t_builder.build();
let t_fraction = t.quantile(alpha.into().unwrap() / 2.0, true);
(ip, t_fraction, residual_variance)
}
pub fn confidence_interval<A: Into<Alpha64>>(
x: &DMatrix<f64>,
y: &DMatrix<f64>,
b: &DMatrix<f64>,
w: &DMatrix<f64>,
alpha: A,
scale: Option<f64>,
df: Option<Rational64>,
) -> (DMatrix<f64>, DMatrix<f64>) {
let (mut ip, t_fraction, _) = interval_inner(x, y, b, w, alpha, scale, df);
let predict = predict(x, b);
(
DMatrix::from_iterator(
predict.len(),
1,
predict
.iter()
.zip(ip.iter())
.map(|(base, ip)| base + (t_fraction.unwrap() * ip.sqrt())),
),
DMatrix::from_iterator(
predict.len(),
1,
predict
.iter()
.zip(ip.iter())
.map(|(base, ip)| base - (t_fraction.unwrap() * ip.sqrt())),
),
)
}
pub fn prediction_interval<A: Into<Alpha64>>(
x: &DMatrix<f64>,
y: &DMatrix<f64>,
b: &DMatrix<f64>,
w: &DMatrix<f64>,
alpha: A,
scale: Option<f64>,
df: Option<Rational64>,
) -> (DMatrix<f64>, DMatrix<f64>) {
let (mut ip, t_fraction, residual_variance) = interval_inner(x, y, b, w, alpha, scale, df);
let predict = predict(x, b);
(
DMatrix::from_iterator(
predict.len(),
1,
predict
.iter()
.zip(ip.iter())
.map(|(base, ip)| base + (t_fraction.unwrap() * (*ip + residual_variance).sqrt())),
),
DMatrix::from_iterator(
predict.len(),
1,
predict
.iter()
.zip(ip.iter())
.map(|(base, ip)| base - (t_fraction.unwrap() * (*ip + residual_variance).sqrt())),
),
)
}