use crate::{
error::{RegressionError, RegressionResult},
glm::{DispersionType, Glm},
link::Link,
num::Float,
response::Yval,
};
use ndarray::Array1;
use std::marker::PhantomData;
pub struct InvGaussian<L = link::NegRecSq>
where
L: Link<InvGaussian<L>>,
{
_link: PhantomData<L>,
}
impl<L> Yval<InvGaussian<L>> for f32
where
L: Link<InvGaussian<L>>,
{
fn into_float<F: Float>(self) -> RegressionResult<F, F> {
if self <= 0. {
return Err(RegressionError::InvalidY(self.to_string()));
}
F::from(self).ok_or_else(|| RegressionError::InvalidY(self.to_string()))
}
}
impl<L> Yval<InvGaussian<L>> for f64
where
L: Link<InvGaussian<L>>,
{
fn into_float<F: Float>(self) -> RegressionResult<F, F> {
if self <= 0. {
return Err(RegressionError::InvalidY(self.to_string()));
}
F::from(self).ok_or_else(|| RegressionError::InvalidY(self.to_string()))
}
}
impl<L> Glm for InvGaussian<L>
where
L: Link<InvGaussian<L>>,
{
type Link = L;
const DISPERSED: DispersionType = DispersionType::FreeDispersion;
fn log_partition<F: Float>(nat_par: F) -> F {
-num_traits::Float::sqrt(-F::two() * nat_par)
}
fn variance<F: Float>(mean: F) -> F {
mean * mean * mean
}
fn log_like_sat<F: Float>(y: F) -> F {
F::half() * num_traits::Float::recip(y)
}
}
pub mod link {
use super::*;
use crate::link::{Canonical, Link, Transform};
use crate::num::Float;
pub struct NegRecSq {}
impl Canonical for NegRecSq {}
impl Link<InvGaussian<NegRecSq>> for NegRecSq {
fn func<F: Float>(y: F) -> F {
-num_traits::Float::recip(F::two() * y * y)
}
fn func_inv<F: Float>(lin_pred: F) -> F {
num_traits::Float::recip(num_traits::Float::sqrt(-F::two() * lin_pred))
}
}
pub struct Log {}
impl Link<InvGaussian<Log>> for Log {
fn func<F: Float>(y: F) -> F {
num_traits::Float::ln(y)
}
fn func_inv<F: Float>(lin_pred: F) -> F {
num_traits::Float::exp(lin_pred)
}
}
impl Transform for Log {
fn nat_param<F: Float>(lin_pred: Array1<F>) -> Array1<F> {
lin_pred.mapv(|x| -F::half() * num_traits::Float::exp(-F::two() * x))
}
fn d_nat_param<F: Float>(lin_pred: &Array1<F>) -> Array1<F> {
lin_pred.mapv(|x| num_traits::Float::exp(-F::two() * x))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{error::RegressionResult, model::ModelBuilder};
use approx::assert_abs_diff_eq;
use ndarray::array;
#[test]
fn ig_ex() -> RegressionResult<(), f64> {
let beta = array![-0.125, 0.09375];
let data_x = array![[0.], [0.], [1.0], [1.0], [1.0]];
let data_y = array![1.0, 3.0, 2.0, 4.0, 6.0];
let model = ModelBuilder::<InvGaussian>::data(&data_y, &data_x).build()?;
let fit = model.fit()?;
assert_abs_diff_eq!(beta, fit.result, epsilon = 0.5 * f32::EPSILON as f64);
let _cov = fit.covariance()?;
Ok(())
}
#[test]
fn ig_log_link_ex() -> RegressionResult<(), f64> {
let ln2 = f64::ln(2.);
let beta = array![ln2, ln2];
let data_x = array![[0.], [0.], [1.0], [1.0], [1.0]];
let data_y = array![1.0, 3.0, 2.0, 4.0, 6.0];
let model = ModelBuilder::<InvGaussian<link::Log>>::data(&data_y, &data_x).build()?;
let fit = model.fit()?;
assert_abs_diff_eq!(beta, fit.result, epsilon = 0.5 * f32::EPSILON as f64);
let _cov = fit.covariance()?;
Ok(())
}
#[test]
fn neg_rec_sq_closure() {
use super::link::NegRecSq;
use crate::link::TestLink;
let x = array![-360., -12., -5., -1.0, -1e-4];
NegRecSq::check_closure(&x);
let y = array![1e-5, 0.25, 0.8, 2.5, 10., 256.];
NegRecSq::check_closure_y(&y);
}
#[test]
fn log_closure() {
use crate::link::TestLink;
use link::Log;
let mu_test_vals = array![1e-8, 0.01, 0.1, 0.3, 0.9, 1.8, 4.2, 148.];
Log::check_closure_y(&mu_test_vals);
let lin_test_vals = array![1e-8, 0.002, 0.5, 2.4, 15., 120.];
Log::check_closure(&lin_test_vals);
}
#[test]
fn log_nat_par() {
use crate::link::TestLink;
use link::Log;
let lin_test_vals = array![-10., -2., -0.5, 0.0, 0.5, 2., 10.];
Log::check_nat_par::<InvGaussian<link::NegRecSq>>(&lin_test_vals);
Log::check_nat_par_d(&lin_test_vals);
}
}