pub struct GlmLikelihoodSpec {
pub spec: LikelihoodSpec,
pub scale: LikelihoodScaleMetadata,
}Expand description
Explicit GLM likelihood specification: response/link spec plus scale semantics.
spec is the canonical (ResponseFamily, InverseLink) selector. scale
records how the scale parameter is handled (profiled Gaussian sigma, fixed
dispersion, fixed/estimated Gamma shape). The Gamma shape is mutated in
place during PIRLS via with_gamma_shape; preserving that field on this
struct is what lets the inner solver thread the estimated shape into
deviance / log-likelihood / weight evaluation without a separate side
channel.
Fields§
§spec: LikelihoodSpec§scale: LikelihoodScaleMetadataImplementations§
Source§impl GlmLikelihoodSpec
impl GlmLikelihoodSpec
Sourcepub fn canonical(spec: LikelihoodSpec) -> GlmLikelihoodSpec
pub fn canonical(spec: LikelihoodSpec) -> GlmLikelihoodSpec
Build a GlmLikelihoodSpec from a LikelihoodSpec, deriving the
canonical default scale metadata for the response family.
pub fn link_function(&self) -> LinkFunction
pub fn fixed_phi(&self) -> Option<f64>
Sourcepub fn coefficient_covariance_scale(&self, profiled_gaussian_phi: f64) -> f64
pub fn coefficient_covariance_scale(&self, profiled_gaussian_phi: f64) -> f64
Multiplier converting the stored unscaled inverse penalized Hessian
H⁻¹ into the reported coefficient covariance Vb = H⁻¹ · scale.
§Invariant
Vb is the inverse of the Hessian of the actual penalized objective the
inner solver minimizes. The stored Hessian is always assembled as
H = XᵀWX + S_λ, with the penalty S_λ added unscaled (see
pirls::penalty::add_to_hessian). Whether H is already that true
objective Hessian — and hence whether any post-hoc dispersion multiply is
warranted — is decided entirely by what the IRLS working weight W
carries:
-
Working weight already carries the reciprocal dispersion / full Fisher information. Then
H = Xᵀ(W_sf/φ)X + S_λalready equals the true penalized Hessian (e.g. mgcv’sXᵀW_sfX/φ + S_λfor Gamma), soVb = H⁻¹and the scale is exactly1.0. This is the case for Gamma (W = prior·shape = prior/φ), Tweedie (W = prior·μ^{2−p}/φ), Beta and Negative-Binomial (the working weight is the complete fixed-scale Fisher information), and the fixed-scale exponential families Poisson/Binomial (φ ≡ 1). MultiplyingH⁻¹by the dispersion again for any of these double-counts it and shrinks every SE by√dispersion. -
Working weight is scale-free (
W = priorweights, the profiled Gaussian convention). Then the data term carries an implicit unit scale andH = XᵀPX + S_λis the Hessian of½·(scaled deviance)·σ²⁻¹without theσ². The correct covariance restores it:Vb = H⁻¹ · σ̂². Only this branch returns a non-unit scale.
profiled_gaussian_phi is the profiled residual variance σ̂² and is
consulted only for the scale-free profiled-Gaussian branch; every
other family ignores it. This deliberately does NOT touch
dispersion() / dispersion_from_likelihood, which still report the
response-level observation noise (1/shape for Gamma, 1/(1+φ) for
Beta, …) used by predictive-interval construction — a distinct quantity
from the coefficient-covariance scale defined here.
pub fn gamma_shape(&self) -> Option<f64>
Sourcepub fn with_gamma_shape(self, shape: f64) -> GlmLikelihoodSpec
pub fn with_gamma_shape(self, shape: f64) -> GlmLikelihoodSpec
Mutate the Gamma shape parameter in place while preserving the rest of the spec. The shape only takes effect for Gamma families; for other families the scale metadata is left untouched.
Sourcepub fn beta_phi_is_estimated(&self) -> bool
pub fn beta_phi_is_estimated(&self) -> bool
Whether the Beta-regression precision phi is estimated from data.
Sourcepub fn with_beta_phi(self, phi: f64) -> GlmLikelihoodSpec
pub fn with_beta_phi(self, phi: f64) -> GlmLikelihoodSpec
Mutate the Beta precision phi in place, on BOTH the family variant
(where every PIRLS weight / deviance / log-likelihood expression reads it
via ResponseFamily::Beta { phi }) and the scale metadata (the
estimated-vs-fixed contract). No-op for non-Beta families. The inner
solver calls this once per inner solve after a moment estimate of phi
from the working residuals, so the IRLS weights Var(y)=mu(1-mu)/(1+phi)
reflect the true precision rather than the phi=1 seed (issue #567).
Sourcepub fn tweedie_phi_is_estimated(&self) -> bool
pub fn tweedie_phi_is_estimated(&self) -> bool
Whether the Tweedie exponential-dispersion phi is estimated from data.
Sourcepub fn with_tweedie_phi(self, phi: f64) -> GlmLikelihoodSpec
pub fn with_tweedie_phi(self, phi: f64) -> GlmLikelihoodSpec
Mutate the Tweedie dispersion phi in place. Unlike Beta, the Tweedie
power p (not phi) is what is carried on the ResponseFamily::Tweedie
variant; the dispersion lives purely in the scale metadata and is read by
the IRLS weight (prior·μ^{2−p}/phi) through fixed_phi(). So updating
the metadata here is sufficient to thread the estimated phi into every
weight / covariance expression. No-op for non-Tweedie families (issue
#771).
Sourcepub fn negbin_theta_is_estimated(&self) -> bool
pub fn negbin_theta_is_estimated(&self) -> bool
Whether the Negative-Binomial overdispersion theta is estimated from
data (issue #802).
Sourcepub fn with_negbin_theta(self, theta: f64) -> GlmLikelihoodSpec
pub fn with_negbin_theta(self, theta: f64) -> GlmLikelihoodSpec
Mutate the Negative-Binomial overdispersion theta in place, on BOTH the
family variant (where every PIRLS weight / deviance / log-likelihood
expression reads it via ResponseFamily::NegativeBinomial { theta }) and
the scale metadata (the estimated-vs-fixed contract). No-op for non-NB
families. The inner solver calls this once per inner solve after a
maximum-likelihood estimate of theta from the working residuals, so the
IRLS weight W = μθ/(θ+μ) and the variance Var(y)=mu+mu^2/theta reflect
the data’s overdispersion rather than the seed theta (issue #802). This
mirrors with_beta_phi exactly — both keep the family variant and the
scale metadata as two synchronized views of one estimated parameter.
No-op for a user-fixed theta (theta_fixed = true /
FixedNegBinTheta, issue #983): the held value is the contract, and
this mutator must never let an estimation path overwrite it — the
PIRLS refresh gate (negbin_theta_is_estimated()) already skips the
call, this enforces the same invariant at the data itself.
Sourcepub fn negbin_theta(&self) -> Option<f64>
pub fn negbin_theta(&self) -> Option<f64>
The estimated Negative-Binomial theta, read from the family variant
(the canonical store), or None for non-NB families.
Sourcepub fn with_negbin_theta_frozen_for_search(
self,
theta: f64,
) -> GlmLikelihoodSpec
pub fn with_negbin_theta_frozen_for_search( self, theta: f64, ) -> GlmLikelihoodSpec
Produce a copy of this spec with the Negative-Binomial overdispersion
theta PINNED at theta for the duration of the smoothing-parameter
(λ) search (#1082). Converts an EstimatedNegBinTheta spec into the
statistically-identical FixedNegBinTheta form (theta_fixed = true),
which gates off the per-inner-solve ML refresh in
GamWorkingModel::update_with_curvature (its guard is
negbin_theta_is_estimated()).
Rationale: with θ estimated, the inner solver re-derives θ from each
outer iterate’s warm-start η, so θ — and hence the NB working response,
deviance and penalty-logdet that feed the REML criterion — drifts every
outer evaluation. The outer optimizer then chases a moving target and the
projected-gradient convergence test never trips, grinding the loop to
max_iter (the #1082 negative-binomial tensor timeout). Holding θ fixed
across the λ-search makes the REML objective F(ρ) = REML(ρ, θ_frozen) a
genuine stationary function of ρ, so the loop converges in a handful of
iterations — and θ is still ML-refreshed at the single final, reported fit
(the refine_dispersion_at_converged_eta = true accept-fit), exactly as
the function-level docs require (“estimate the scale at the converged fit,
not inside the λ search; mgcv likewise”). No-op for non-NB families and
for an already user-fixed θ.
Trait Implementations§
Source§impl Clone for GlmLikelihoodSpec
impl Clone for GlmLikelihoodSpec
Source§fn clone(&self) -> GlmLikelihoodSpec
fn clone(&self) -> GlmLikelihoodSpec
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for GlmLikelihoodSpec
impl Debug for GlmLikelihoodSpec
Source§impl<'de> Deserialize<'de> for GlmLikelihoodSpec
impl<'de> Deserialize<'de> for GlmLikelihoodSpec
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<GlmLikelihoodSpec, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<GlmLikelihoodSpec, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for GlmLikelihoodSpec
impl PartialEq for GlmLikelihoodSpec
Source§fn eq(&self, other: &GlmLikelihoodSpec) -> bool
fn eq(&self, other: &GlmLikelihoodSpec) -> bool
self and other values to be equal, and is used by ==.Source§impl Serialize for GlmLikelihoodSpec
impl Serialize for GlmLikelihoodSpec
Source§fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
impl StructuralPartialEq for GlmLikelihoodSpec
Auto Trait Implementations§
impl Freeze for GlmLikelihoodSpec
impl RefUnwindSafe for GlmLikelihoodSpec
impl Send for GlmLikelihoodSpec
impl Sync for GlmLikelihoodSpec
impl Unpin for GlmLikelihoodSpec
impl UnsafeUnpin for GlmLikelihoodSpec
impl UnwindSafe for GlmLikelihoodSpec
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T, U> Imply<T> for U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
impl<T> Scalar for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.