use crate::{num::Float, Array1, Array2};
use ndarray::ArrayViewMut1;
pub trait IrlsReg<F>
where
F: Float,
{
fn likelihood(&self, l: F, regressors: &Array1<F>) -> F;
fn gradient(&self, l: Array1<F>, regressors: &Array1<F>) -> Array1<F>;
fn irls_vec(&self, vec: Array1<F>, regressors: &Array1<F>) -> Array1<F>;
fn irls_mat(&self, mat: Array2<F>, regressors: &Array1<F>) -> Array2<F>;
fn is_null(&self) -> bool;
}
pub struct Null {}
impl<F: Float> IrlsReg<F> for Null {
#[inline]
fn likelihood(&self, l: F, _: &Array1<F>) -> F {
l
}
#[inline]
fn gradient(&self, jac: Array1<F>, _: &Array1<F>) -> Array1<F> {
jac
}
#[inline]
fn irls_vec(&self, vec: Array1<F>, _: &Array1<F>) -> Array1<F> {
vec
}
#[inline]
fn irls_mat(&self, mat: Array2<F>, _: &Array1<F>) -> Array2<F> {
mat
}
fn is_null(&self) -> bool {
true
}
}
pub struct Ridge<F: Float> {
l2_vec: Array1<F>,
}
impl<F: Float> Ridge<F> {
pub fn from_diag(l2: Array1<F>) -> Self {
Self { l2_vec: l2 }
}
}
impl<F: Float> IrlsReg<F> for Ridge<F> {
fn likelihood(&self, l: F, beta: &Array1<F>) -> F {
l - F::from(0.5).unwrap() * (&self.l2_vec * &beta.mapv(|b| b * b)).sum()
}
fn gradient(&self, jac: Array1<F>, beta: &Array1<F>) -> Array1<F> {
jac - (&self.l2_vec * beta)
}
#[inline]
fn irls_vec(&self, vec: Array1<F>, _: &Array1<F>) -> Array1<F> {
vec
}
fn irls_mat(&self, mut mat: Array2<F>, _: &Array1<F>) -> Array2<F> {
let mut mat_diag: ArrayViewMut1<F> = mat.diag_mut();
mat_diag += &self.l2_vec;
mat
}
fn is_null(&self) -> bool {
false
}
}
pub struct LassoSmooth<F: Float> {
l1_vec: Array1<F>,
s: F,
}
impl<F: Float> LassoSmooth<F> {
pub fn from_diag(l1: Array1<F>, s: F) -> Self {
Self { l1_vec: l1, s }
}
fn sech_sq(&self, beta: F) -> F {
let sech = num_traits::Float::cosh(beta / self.s).recip();
sech * sech
}
}
impl<F: Float> IrlsReg<F> for LassoSmooth<F> {
fn likelihood(&self, l: F, beta: &Array1<F>) -> F {
l - (&self.l1_vec
* &beta.mapv(|b| self.s * num_traits::Float::ln(num_traits::Float::cosh(b / self.s))))
.sum()
}
fn gradient(&self, jac: Array1<F>, beta: &Array1<F>) -> Array1<F> {
jac - (&self.l1_vec * &beta.mapv(|b| num_traits::Float::tanh(b / self.s)))
}
fn irls_vec(&self, vec: Array1<F>, beta: &Array1<F>) -> Array1<F> {
vec - &self.l1_vec
* &beta.mapv(|b| num_traits::Float::tanh(b / self.s) - (b / self.s) * self.sech_sq(b))
}
fn irls_mat(&self, mut mat: Array2<F>, beta: &Array1<F>) -> Array2<F> {
let mut mat_diag: ArrayViewMut1<F> = mat.diag_mut();
mat_diag += &(&self.l1_vec * &beta.mapv(|b| self.s.recip() * self.sech_sq(b)));
mat
}
fn is_null(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::array;
#[test]
fn ridge_matrix() {
let l = 1e-4;
let ridge = Ridge::from_diag(array![0., l]);
let mat = array![[0.5, 0.1], [0.1, 0.2]];
let mut target_mat = mat.clone();
target_mat[[1, 1]] += l;
let dummy_beta = array![0., 0.];
assert_eq!(ridge.irls_mat(mat, &dummy_beta), target_mat);
}
}