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-k̂ 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: boolWhether 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_hessianreturnsH, and it is non-Optionon bothFitInferenceandFitGeometry— neither of which this seam clears — so it survives every declined fit and is persisted onto the saved model;FitInference::dispersionsuppliesphi.
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 1 — bms/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
impl Clone for FitArtifacts
Source§fn clone(&self) -> FitArtifacts
fn clone(&self) -> FitArtifacts
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 FitArtifacts
impl Debug for FitArtifacts
Source§impl Default for FitArtifacts
impl Default for FitArtifacts
Source§fn default() -> FitArtifacts
fn default() -> FitArtifacts
Source§impl<'de> Deserialize<'de> for FitArtifacts
impl<'de> Deserialize<'de> for FitArtifacts
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl !RefUnwindSafe for FitArtifacts
impl !UnwindSafe for FitArtifacts
impl Freeze for FitArtifacts
impl Send for FitArtifacts
impl Sync for FitArtifacts
impl Unpin for FitArtifacts
impl UnsafeUnpin for FitArtifacts
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,
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.