gam_solve/model_types.rs
1//! # Shared model-estimation contract types
2//!
3//! Lower-layer types that both the `families` layer (which constructs penalty
4//! and dispersion specifications and propagates estimation errors) and the
5//! `solver` layer (which consumes them) need to name. Hosting them here breaks
6//! the `families → solver::estimate` back-edge that #1135 tracks: families now
7//! import these from `crate::model_types` instead of reaching *up* into
8//! `crate::solver::estimate`.
9//!
10//! ## Layering
11//! These types depend only on lower or sibling layers (`linalg`, `terms`,
12//! `families`' error types) — never on `solver`. `EstimationError` carries
13//! `#[from]` conversions for the family error types it wraps, which is the
14//! allowed downward direction.
15
16use ndarray::{Array1, Array2};
17
18pub use gam_problem::EstimationError;
19
20// ===========================================================================
21// Dispersion
22// ===========================================================================
23
24/// Dispersion contract used by inferential covariance and reference distributions.
25///
26/// This type lives in `gam-problem`; re-exported here so model result APIs and
27/// existing engine code name the same neutral contract.
28pub use gam_problem::Dispersion;
29
30// ===========================================================================
31// Constraint/KKT carriers
32// ===========================================================================
33
34/// Active row block of the joint linear inequality constraint matrix at the
35/// converged inner iterate.
36#[derive(Clone, Debug)]
37pub struct ActiveLinearConstraintBlock {
38 /// `k_active x p` matrix of active constraint rows.
39 pub a: Array2<f64>,
40}
41
42/// Subspace represented by a stored KKT residual.
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum KktResidualSubspace {
45 /// Residual after active-constraint normal components have been stripped:
46 /// `r_A = P_T(Sbeta + Gamma beta - grad ell)`.
47 ActiveProjected,
48 /// Residual additionally projected into the retained identifiable range:
49 /// `r_R = R R^T r_A`.
50 ReducedRange,
51}
52
53/// KKT residual `r = grad_beta L_pen(beta_hat)` at the converged inner
54/// iterate, tagged with the exact represented subspace.
55#[derive(Clone, Debug)]
56pub struct ProjectedKktResidual {
57 /// The residual vector in the full coefficient coordinates. Active and
58 /// reduced-range projection zero out excluded directions rather than
59 /// shortening the vector, so its length remains `p`.
60 pub(crate) residual: Array1<f64>,
61 pub(crate) subspace: KktResidualSubspace,
62 /// The KKT-stationarity tolerance the inner solver compared the residual
63 /// against when the certificate fired.
64 pub(crate) residual_tol: Option<f64>,
65 /// `total_p - active_set_size` at the producing iterate.
66 pub(crate) free_rank: Option<usize>,
67}
68
69impl ProjectedKktResidual {
70 /// Construct from `r_A = P_T(Sbeta + Gamma beta - grad ell)`, with active
71 /// constraint multipliers removed but before any reduced-range projection.
72 pub fn from_active_projected(residual: Array1<f64>) -> Self {
73 Self {
74 residual,
75 subspace: KktResidualSubspace::ActiveProjected,
76 residual_tol: None,
77 free_rank: None,
78 }
79 }
80
81 /// Construct from `r_R = R R^T r_A`, where `R` is the actual reduced
82 /// identifiable basis used by the projected inverse kernel.
83 pub(crate) fn from_reduced_range(residual: Array1<f64>) -> Self {
84 Self {
85 residual,
86 subspace: KktResidualSubspace::ReducedRange,
87 residual_tol: None,
88 free_rank: None,
89 }
90 }
91
92 /// Attach the KKT tolerance and free-subspace rank to a previously
93 /// constructed residual.
94 pub fn with_metadata(mut self, residual_tol: f64, free_rank: usize) -> Self {
95 self.residual_tol = Some(residual_tol);
96 self.free_rank = Some(free_rank);
97 self
98 }
99
100 /// Borrow the underlying free-space residual for the H^-1*r solve and its
101 /// rho-derivatives.
102 pub fn as_array(&self) -> &Array1<f64> {
103 &self.residual
104 }
105
106 pub fn subspace(&self) -> KktResidualSubspace {
107 self.subspace
108 }
109}
110
111// ===========================================================================
112// CoefficientPriorMean + PenaltySpec
113// ===========================================================================
114
115/// Programmatic prior mean for a coefficient penalty block.
116///
117/// This type now lives in the neutral `gam-problem` crate (with its inherent
118/// `evaluate` returning `gam_problem::PriorMeanError`); re-exported here so all
119/// existing `crate::estimate::CoefficientPriorMean` references keep resolving.
120/// Solver-side callers map `PriorMeanError` into `EstimationError::InvalidInput`.
121pub use gam_problem::CoefficientPriorMean;
122
123pub use gam_terms::penalty_spec::PenaltySpec;
124
125pub(crate) mod result_types;
126
127pub(crate) use result_types::CERTIFICATE_RAIL_MARGIN;
128pub use result_types::{
129 AdaptiveRegularizationOptions, BlockRole, CriterionCertificate, ExecutionPath, FitArtifacts,
130 FitGeometry, FitInference, FitOptions, FittedBlock, FittedLinkState, UnifiedFitResult,
131 UnifiedFitResultParts, ensure_finite_scalar, saved_latent_cloglog_state_from_fit,
132 saved_mixture_state_from_fit, saved_sas_state_from_fit, validate_all_finite,
133 validate_dense_hessian_export, validate_explicit_dense_hessian_for_whitening,
134};