use gam_math::jet_scalar::JetScalar;
use gam_problem::InverseLink;
#[inline]
pub(crate) fn binomial_location_scale_nll_tower(
y: f64,
weight: f64,
eta_t: f64,
eta_ls: f64,
q_value: f64,
mu: f64,
dmu_dq: f64,
d2mu_dq2: f64,
d3mu_dq3: f64,
link_kind: &InverseLink,
include_fourth: bool,
) -> Result<gam_math::jet_tower::Tower4<2>, String> {
use gam_math::jet_tower::Tower4;
super::binomial_location_scale_nll_generic::<Tower4<2>>(
y,
weight,
eta_t,
eta_ls,
q_value,
mu,
dmu_dq,
d2mu_dq2,
d3mu_dq3,
link_kind,
include_fourth,
true,
|x, axis| Tower4::<2>::variable(x, axis),
)
}
#[inline]
pub(crate) fn dispersion_tweedie_nll_generic<S: JetScalar<2>>(
yi: f64,
eta_mu: f64,
eta_d: f64,
p: f64,
wi: f64,
) -> S {
let one_minus_p = 1.0 - p;
let two_minus_p = 2.0 - p;
let mu = S::variable(eta_mu, 0).exp();
let phi = S::variable(eta_d, 1).scale(-1.0).exp();
if yi > 0.0 {
let dev = mu
.powf(two_minus_p)
.scale(1.0 / two_minus_p)
.sub(&mu.powf(one_minus_p).scale(yi / one_minus_p))
.add(&S::constant(
yi.powf(two_minus_p) / (one_minus_p * two_minus_p),
))
.scale(2.0);
let loglik = dev
.mul(&phi.recip().scale(-0.5))
.sub(&phi.scale(2.0 * std::f64::consts::PI).ln().scale(0.5))
.sub(&S::constant(0.5 * p * yi.ln()));
loglik.scale(-wi)
} else {
let c = mu.powf(two_minus_p).scale(1.0 / two_minus_p);
let loglik = c.mul(&phi.recip()).scale(-1.0);
loglik.scale(-wi)
}
}