use ndarray::{Array1, Array2};
use robust_rs_core::error::RobustError;
use robust_rs_core::rho::RhoFunction;
use robust_rs_core::scale::ScaleEstimator;
use robust_rs_core::solver::Control;
use crate::estimator::RegressionFit;
pub struct MEstimator<R, S> {
pub rho: R,
pub scale: S,
pub control: Control,
}
impl<R, S> MEstimator<R, S>
where
R: RhoFunction + Clone + 'static,
S: ScaleEstimator,
{
pub fn new(rho: R, scale: S) -> Self {
Self {
rho,
scale,
control: Control::default(),
}
}
pub fn fit(&self, x: &Array2<f64>, y: &Array1<f64>) -> Result<RegressionFit, RobustError> {
use crate::wls::weighted_least_squares;
let n = x.nrows();
let ones = Array1::from_elem(n, 1.0);
let mut beta = weighted_least_squares(x, y, &ones)?;
let mut resid = y - &x.dot(&beta);
let scale_est = self.scale.scale(resid.as_slice().expect("contiguous"))?;
let s: f64 = scale_est.get();
let mut converged = false;
for _ in 0..self.control.max_iter {
let w = resid.mapv(|r| self.rho.weight(r / s));
let next = weighted_least_squares(x, y, &w)?;
let diff = &next - β
let rel = diff.dot(&diff).sqrt() / (next.dot(&next).sqrt() + 1e-12);
beta = next;
resid = y - &x.dot(&beta);
if rel <= self.control.tol {
converged = true;
break;
}
}
if !converged {
return Err(RobustError::NonConvergence {
iters: self.control.max_iter,
});
}
let weights = resid.mapv(|r| self.rho.weight(r / s));
Ok(RegressionFit {
coefficients: beta,
scale: scale_est,
residuals: resid,
weights,
rho: Box::new(self.rho.clone()), breakdown_point: 0.0, })
}
}