Skip to main content

FitArtifacts

Struct FitArtifacts 

Source
pub struct FitArtifacts {
    pub pirls: Option<PirlsResult>,
    pub null_space_logdet: Option<f64>,
    pub null_space_dim: Option<usize>,
    pub survival_link_wiggle_knots: Option<Array1<f64>>,
    pub survival_link_wiggle_degree: Option<usize>,
    pub criterion_certificate: Option<OuterCriterionCertificate>,
    pub rho_posterior_certificate: Option<RhoPosteriorCertificate>,
    pub rho_posterior_escalation: Option<RhoPosteriorEscalation>,
    pub rho_covariance: Option<Array2<f64>>,
    pub joint_log_lambdas: Option<Array1<f64>>,
    pub firth_bias_reduction: bool,
    pub covariance_declined: Option<CovarianceDeclined>,
}
Expand description

Post-fit artifacts needed by downstream diagnostics/inference without re-running PIRLS.

Fields§

§pirls: Option<PirlsResult>§null_space_logdet: Option<f64>§null_space_dim: Option<usize>§survival_link_wiggle_knots: Option<Array1<f64>>§survival_link_wiggle_degree: Option<usize>§criterion_certificate: Option<OuterCriterionCertificate>

First-order optimality certificate from the outer smoothing-parameter optimization (#934): gradient-vs-objective FD audit at the returned optimum, Hessian-PD probe, λ-rail flags. None when the outer ran gradient-free or an audit probe could not evaluate.

§rho_posterior_certificate: Option<RhoPosteriorCertificate>

Tier-0 marginal-smoothing (ρ-uncertainty) PSIS certificate (#938): the Pareto- diagnostic that says whether the plug-in + first-order V_ρ correction is adequate or ρ-uncertainty needs a heavier quadrature/NUTS treatment. Computed against the live REML objective at the converged ρ̂ (see RemlState::rho_posterior_inference). None when there are no smoothing parameters or the outer Hessian was unavailable. Re-derivable from the fit, so it is not serialized.

§rho_posterior_escalation: Option<RhoPosteriorEscalation>

Escalation outcome (#938) when the Tier-0 certificate read Escalate: the Tier-1 quadrature mixture (K ≤ 4), the Tier-2 NUTS draws (K ≤ 16), or an honest Unavailable report. None whenever the certificate did not escalate (or is itself absent). Computed at the same live-objective seam as the certificate; re-derivable, not serialized.

§rho_covariance: Option<Array2<f64>>

Regularized inverse REML/LAML outer Hessian over rho = log(lambda), aligned with UnifiedFitResult::lambdas. This is the narrow #740 handoff consumed by estimated-lambda Lawley LR corrections; it is computed from the same path as smoothing-parameter uncertainty and is re-derivable, so it is not serialized.

§joint_log_lambdas: Option<Array1<f64>>

Selected per-component log-smoothing parameters of the full-width JOINT penalty (gam#1587/#561). Families whose smoothing is carried by a joint penalty (the multinomial centered Σ_t λ_t (M ⊗ S_t) metric) leave their per-block penalty lists — and hence UnifiedFitResult::lambdas — empty, so the only place the converged ρ_t survives is here. None for every per-block-only family. Re-derivable from a refit, so not serialized; it is consumed by the multinomial reporting path to reconstruct per-(class,term) λ, per-class EDF, and the influence matrix F = I − H⁻¹ S_λ.

§firth_bias_reduction: bool

Whether the fit optimized the Firth/Jeffreys-adjusted likelihood. Persisted (serialized) so saved-model posterior sampling reconstructs the SAME target the fit optimized — dropping the Jeffreys term Φ(β) from the sampled log-posterior silently samples a different model (#2245 finding 16). false for fits that never engaged Firth.

§covariance_declined: Option<CovarianceDeclined>

Set when this fit could have published a coefficient covariance and deliberately did not (gam#2718). None is the ordinary case and carries NO claim either way: a covariance may be present, or absent because it was never requested. Some is a positive statement that one was withheld, with the reason attached — see CovarianceDeclined.

Serialized, because the reason has to survive to a consumer reading a saved model’s standard errors; that consumer is exactly the one who would otherwise misread the absence.

§This channel is ADVISORY, and that is only safe because absence is honest

Nothing in the type system forces a consumer to read this field. covariance_conditional is an Option whose None was already a common, benign value long before this existed (the exact-interpolation Gaussian boundary, saved models reconstructed without inference, any fit that declined it), so a consumer looking only there still cannot tell “never computed” from “computed and withheld”. Making that distinction structural would mean replacing the Option with a three-state enum across ~185 references in 52 files.

That was not done, and the reason it is safe not to have done it is a MEASURED property rather than an assumption: no consumer substitutes a value for an absent covariance. Every production read propagates the absence — .as_ref().map(..), .filter(..)?, .and_then(..), .clone() into another Option, or an outright Err. None defaults to a zero matrix, an identity, or a zero standard error. Checked over covariance_conditional (185 occurrences) and the paired beta_covariance / beta_standard_errors / beta_covariance_corrected / beta_standard_errors_corrected (114 production reads).

The single fallback in the tree is UnifiedFitResult::beta_covariance_corrected, which returns Vb for Vp when lambdas.is_empty(). It is guarded, documented, and exact — with no smoothing coordinates the correction J Var(rho) Jᵀ is identically zero — and it .flatten()s to None when the conditional covariance is also absent, which is the state a declined fit is in.

§The trigger, as a checkable condition

Run:

git grep -n -E '\.(covariance_conditional|beta_covariance|beta_standard_errors)' -- crates/ src/

and count the sites that meet an absence with unwrap_or, unwrap_or_default, unwrap_or_else, map_or, or a None => arm yielding a matrix or a zero SE, rather than propagating.

Today that count is 0, out of 25 machine-flagged candidates, out of 185 references in 52 files (plus 114 production reads of the paired beta_* fields, also 0). If it is ever greater than 0, this field is insufficient and the Option must become an enum. One command, two integers; no re-derivation of the judgement above.

§What this does NOT establish: the quantity is still RECONSTRUCTIBLE

The sweep above is over consumers of the fields that get cleared. It says nothing about what else on the artifact PRODUCES the same quantity, and something does:

  • UnifiedFitResult::penalized_hessian returns H, and it is non-Option on both FitInference and FitGeometry — neither of which this seam clears — so it survives every declined fit and is persisted onto the saved model;
  • FitInference::dispersion supplies phi.

Vb_naive = phi * H^-1 is therefore one Cholesky away, and beta_covariance_frequentist, coefficient_influence and weighted_gram are further optional producers this seam also leaves alone. A consumer can obtain a coefficient covariance without ever reading this field.

That is accepted rather than fixed, for a stated reason: what is reconstructible is the NAIVE covariance — exactly the object the BMS seam refuses to publish, because it omits the first-stage generated-regressor uncertainty and is therefore too narrow. Withholding it means not SHIPPING it under the name beta_covariance, where it would be indistinguishable from a corrected one. It does not mean, and cannot mean, destroying the curvature every other consumer of the fit needs: penalized_hessian is what EDF accounting, posterior whitening and prediction all read, and it is not Option, so it cannot be withheld without dropping inference and geometry wholesale.

The route that removes this residual entirely is implementing the correction (G_measure, gam#2484), after which nothing is withheld.

§The producer-set trigger, also checkable

One persistence route drops this field: the compact saved-fit constructors in gam-cli (model_build.rs) take beta_covariance and assign inf.penalized_hessian from the geometry, but have no parameter that could carry a declination — so a fit persisted through them would ship curvature with the warning stripped off. No parameter was threaded, because nothing reaches them: this field has exactly ONE producer, and that producer persists whole-UnifiedFitResult through assemble_bernoulli_marginal_slope_payload instead.

That defence is only as good as the producer count, so count it. Run:

git grep -n 'covariance_declined' -- crates/ src/ | grep -E 'covariance_declined\s*='

Today that returns exactly 1bms/block_specs.rs, the BMS Murphy-Topel seam. If a second producer ever appears, check whether it persists through a compact constructor; if it does, the parameter must be threaded and tests/bms_covariance_declined_2718.rs extended to cover that route. The round-trip test there pins the wire, not the routing, so it will not catch a new producer on its own.

Trait Implementations§

Source§

impl Clone for FitArtifacts

Source§

fn clone(&self) -> FitArtifacts

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FitArtifacts

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for FitArtifacts

Source§

fn default() -> FitArtifacts

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for FitArtifacts

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for FitArtifacts

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ByRef<T> for T

Source§

fn by_ref(&self) -> &T

Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DistributionExt for T
where T: ?Sized,

Source§

fn rand<T>(&self, rng: &mut (impl Rng + ?Sized)) -> T
where Self: Distribution<T>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V