gam_linalg/types.rs
1use serde::{Deserialize, Serialize};
2
3/// How ridge-adjusted determinants should be evaluated for outer criteria.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5pub enum RidgeDeterminantMode {
6 /// Use exact full logdet.
7 Auto,
8 /// Use full log-determinant of the ridged matrix (requires SPD in practice).
9 Full,
10 /// Use positive-part pseudo-determinant (sum log ev for ev > floor).
11 PositivePart,
12}
13
14/// Global policy governing how a stabilization ridge participates in objectives.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16pub struct RidgePolicy {
17 /// Must remain independent of smoothing parameters (`rho`) for smooth outer derivatives.
18 pub rho_independent: bool,
19 /// Include ridge in quadratic penalty term: `0.5 * delta * ||beta||^2`.
20 pub include_quadratic_penalty: bool,
21 /// Include ridge in penalty determinant term (e.g. `log|S_lambda + delta I|`).
22 pub include_penalty_logdet: bool,
23 /// Include ridge in Hessian used by Laplace term / implicit differentiation.
24 pub include_laplacehessian: bool,
25 /// Determinant evaluation mode when ridge participates in logdet terms.
26 pub determinant_mode: RidgeDeterminantMode,
27}
28
29impl RidgePolicy {
30 /// Default policy used by PIRLS/REML path:
31 /// treat stabilization ridge as an explicit `delta I` prior contribution
32 /// with adaptive logdet evaluation.
33 pub const fn explicit_stabilization_full() -> Self {
34 Self {
35 rho_independent: true,
36 include_quadratic_penalty: true,
37 include_penalty_logdet: true,
38 include_laplacehessian: true,
39 determinant_mode: RidgeDeterminantMode::Auto,
40 }
41 }
42
43 pub const fn explicit_stabilization_full_exact() -> Self {
44 Self {
45 rho_independent: true,
46 include_quadratic_penalty: true,
47 include_penalty_logdet: true,
48 include_laplacehessian: true,
49 determinant_mode: RidgeDeterminantMode::Full,
50 }
51 }
52
53 /// Variant used when pseudo-determinants are required for indefinite matrices.
54 pub const fn explicit_stabilization_pospart() -> Self {
55 Self {
56 rho_independent: true,
57 include_quadratic_penalty: true,
58 include_penalty_logdet: true,
59 include_laplacehessian: true,
60 determinant_mode: RidgeDeterminantMode::PositivePart,
61 }
62 }
63
64 /// Solver-only stabilization: the ridge `δI` stabilizes the inner linear
65 /// solve (it bounds the Newton step `(H+δI)⁻¹∇`) but is **excluded** from
66 /// the REML/LAML objective — no `½·δ·‖β‖²` quadratic-penalty term, no
67 /// `δ`-shift of the penalty log-determinant, no `δ`-shift of the Laplace
68 /// Hessian. Use this when a numerical floor is needed purely to keep the
69 /// linear algebra finite during screening and must NOT bias the
70 /// smoothing-parameter selection or shrink identified coefficients off the
71 /// MLE. With every `include_*` false the optimized objective equals the
72 /// true penalized REML criterion, so the value surface and its analytic
73 /// gradient describe the same objective (gam#747/#748).
74 pub const fn solver_only() -> Self {
75 Self {
76 rho_independent: true,
77 include_quadratic_penalty: false,
78 include_penalty_logdet: false,
79 include_laplacehessian: false,
80 determinant_mode: RidgeDeterminantMode::PositivePart,
81 }
82 }
83}