1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
//! Defines traits for link functions use crate::{glm::Glm, num::Float}; use ndarray::Array1; /// Describes the functions to map to and from the linear predictors and the /// expectation of the response. It is constrained mathematically by the /// response distribution and the transformation of the linear predictor. // TODO: The link function and its inverse are independent of the response // distribution. This could be refactored to separate the function itself from // the transformation that works with the distribution. pub trait Link<M: Glm>: Transform { /// Maps the expectation value of the response variable to the linear /// predictor. In general this is determined by a composition of the inverse /// natural parameter transformation and the canonical link function. fn func<F: Float>(y: F) -> F; // fn func<F: Float>(y: Array1<F>) -> Array1<F>; /// Maps the linear predictor to the expectation value of the response. // TODO: There may not be a point in using Array versions of these functions // since clones are necessary anyway. Perhaps we could simply define the // scalar function and use mapv(). fn func_inv<F: Float>(lin_pred: F) -> F; // fn func_inv<F: Float>(lin_pred: Array1<F>) -> Array1<F>; } pub trait Transform { /// The natural parameter(s) of the response distribution as a function /// of the linear predictor. For canonical link functions this is the /// identity. It must be monotonic, invertible, and twice-differentiable. /// For link function g and canonical link function g_0 it is equal to /// g_0 ( g^{-1}(lin_pred) ) . fn nat_param<F: Float>(lin_pred: Array1<F>) -> Array1<F>; /// The derivative of the transformation to the natural parameter. If it is /// zero in a region that the IRLS is in the algorithm may have difficulty /// converging. fn d_nat_param<F: Float>(lin_pred: &Array1<F>) -> Array1<F>; /// Adjust the error and variance terms of the likelihood function based on /// the first and second derivatives of the transformation. The adjustment /// is performed simultaneously. The linear predictor must be /// un-transformed, i.e. it must be X*beta without the transformation /// applied. fn adjust_errors_variance<F: Float>( errors: Array1<F>, variance: Array1<F>, lin_pred: &Array1<F>, ) -> (Array1<F>, Array1<F>) { let eta_d = Self::d_nat_param(lin_pred); let err_adj = &eta_d * &errors; // The second-derivative term in the variance matrix can lead it to not // be positive-definite. In fact, the second term should vanish when // taking the expecation of Y to give the Fisher information. // let var_adj = &eta_d * &variance * eta_d - eta_dd * errors; let var_adj = &eta_d * &variance * eta_d; (err_adj, var_adj) } } /// The canonical transformation by definition equates the linear predictor with /// the natural parameter of the response distribution. Implementing this trait /// for a link function automatically defines the trivial transformation /// functions. pub trait Canonical {} impl<T> Transform for T where T: Canonical, { /// By defintion this function is the identity function for canonical links. #[inline] fn nat_param<F: Float>(lin_pred: Array1<F>) -> Array1<F> { lin_pred } #[inline] fn d_nat_param<F: Float>(lin_pred: &Array1<F>) -> Array1<F> { Array1::<F>::ones(lin_pred.len()) } /// The canonical link function requires no transformation of the error and variance terms. #[inline] fn adjust_errors_variance<F: Float>( errors: Array1<F>, variance: Array1<F>, _lin_pred: &Array1<F>, ) -> (Array1<F>, Array1<F>) { (errors, variance) } }