Skip to main content

fdars_core/boosting_regression/
mod.rs

1//! Component-wise gradient boosting and Bayesian regression for functional responses.
2//!
3//! Implements REG-06: FDboost-style penalized functional base-learner boosting,
4//! GAMLSS distributional boosting, conjugate Gibbs Bayesian FOSR, and stability selection.
5//!
6//! # Methods
7//!
8//! - [`boost_fosr`] — Component-wise boosted function-on-scalar regression (REG-06-01)
9//! - [`boost_fofr`] — Component-wise boosted function-on-function regression (REG-06-02)
10//! - [`gamlss_fosr`] — GAMLSS distributional boosting: location + scale (REG-06-03)
11//! - [`bayesian_fosr`] — Bayesian FOSR via conjugate Gibbs sampler (REG-06-04)
12//! - [`stability_selection`] — FDboost-style stability selection (REG-06-05)
13//!
14//! # References
15//!
16//! Hothorn et al. (2010). Model-Based Boosting. *Journal of Statistical Software*.
17//! Hofner et al. (2016). gamboostLSS. *Journal of Statistical Software*, 74(1).
18//! Jiang et al. (2025). arXiv:2505.05633 (Bayesian FoSR).
19//! Meinshausen & Bühlmann (2010). Stability Selection. *JRSS-B*, 72(4).
20//!
21//! Divergences from R baselines (FDboost 1.1-4, refund, stabs) documented per function.
22
23use crate::matrix::FdMatrix;
24
25pub mod bayesian;
26pub mod boost_fofr;
27pub mod boost_fosr;
28pub mod gamlss;
29pub mod stability;
30
31// ---------------------------------------------------------------------------
32// Config structs
33// ---------------------------------------------------------------------------
34
35/// Configuration for component-wise gradient boosting (FOSR, FoFR, GAMLSS, stability).
36///
37/// All base-learners use the same `nbasis`, `order`, `lfd_order`, and `lambda`
38/// to ensure equal effective degrees of freedom, preventing selection bias toward
39/// more flexible learners (see Pitfall 4 in RESEARCH.md).
40///
41/// **Divergence from FDboost:** Fixed `nu` and `mstop` rather than CV-based early stopping;
42/// the GCV path is tracked for diagnostic purposes but not used for stopping.
43#[derive(Debug, Clone, PartialEq)]
44pub struct BoostingConfig {
45    /// Number of boosting iterations (must be ≥ 1).
46    pub mstop: usize,
47    /// Learning rate ν ∈ (0, 1] (FDboost default: 0.1).
48    pub nu: f64,
49    /// Number of B-spline basis functions per base-learner (must be ≥ 4).
50    ///
51    /// The actual number of basis functions is `nknots + order` where
52    /// `nknots = nbasis - order`. With `order = 4` (cubic), `nbasis = 10`
53    /// gives 6 interior knots.
54    pub nbasis: usize,
55    /// B-spline order (typically 4 for cubic splines).
56    pub order: usize,
57    /// Penalty derivative order (typically 2 for roughness).
58    pub lfd_order: usize,
59    /// Smoothing parameter λ > 0 for penalized base-learners.
60    pub lambda: f64,
61    /// Number of predictor FPC components for FoFR base-learners (REG-06-02).
62    pub ncomp_x: usize,
63    /// RNG seed (used by stability selection and future extensions; unused in pure boosting).
64    pub seed: u64,
65}
66
67/// Configuration for the Bayesian FOSR Gibbs sampler (REG-06-04).
68///
69/// Uses conjugate Normal-Inverse-Gamma priors. Defaults match the weakly-informative
70/// settings recommended by Jiang et al. (2025): `τ² = 100`, `IG(0.001, 0.001)`.
71///
72/// **Divergence from refund:** refund's Bayesian FOSR uses spline basis priors;
73/// this implementation uses FPCA score compression via `fdata_to_pc_1d` for
74/// simplicity and zero new dependencies.
75#[derive(Debug, Clone, PartialEq)]
76pub struct BayesianConfig {
77    /// Number of FPC components for score compression (must be ≥ 1).
78    pub ncomp: usize,
79    /// Prior variance τ² on FPC-space coefficients (default: 100.0).
80    ///
81    /// Large `τ²` gives a weakly informative prior; very small values are dogmatic.
82    pub tau2: f64,
83    /// Inverse-Gamma prior shape a₀ (default: 0.001 — weakly informative).
84    pub ig_a0: f64,
85    /// Inverse-Gamma prior rate b₀ (default: 0.001 — weakly informative).
86    pub ig_b0: f64,
87    /// Number of Gibbs iterations retained after burn-in (must be ≥ 1).
88    pub n_iter: usize,
89    /// Burn-in iterations discarded (must be < `n_iter` iterations will run).
90    pub burn_in: usize,
91    /// Thinning interval — keep every `thin`-th draw (must be ≥ 1).
92    pub thin: usize,
93    /// RNG seed — chain is fully deterministic: `StdRng::seed_from_u64(seed)`.
94    pub seed: u64,
95}
96
97/// Configuration for FDboost-style stability selection (REG-06-05).
98///
99/// Implements the Meinshausen-Bühlmann subsampling scheme with ⌊n/2⌋ rows
100/// per replicate. The PFER bound `E[V] ≤ q² / ((2·π_thr − 1)·p)` is reported
101/// as an informational diagnostic.
102#[derive(Debug, Clone, PartialEq)]
103pub struct StabilityConfig {
104    /// Number of resamples B (must be ≥ 1; default: 100).
105    pub n_resamples: usize,
106    /// Selection threshold π ∈ (0.5, 1.0] (default: 0.9).
107    ///
108    /// Base-learner j is declared "stable" if its selection frequency ≥ `pi_thr`.
109    pub pi_thr: f64,
110    /// Base RNG seed; replicate `b` uses `seed.wrapping_add(b as u64)` for isolation.
111    pub seed: u64,
112}
113
114// ---------------------------------------------------------------------------
115// Result structs
116// ---------------------------------------------------------------------------
117
118/// Result of component-wise boosted function-on-scalar regression (REG-06-01).
119///
120/// Follows the `FosrResult` field convention; adds the boosting path diagnostics
121/// (`selected_learners`, `gcv_path`, `mstop`, `nu`).
122///
123/// **Divergence from FDboost:** fixed `mstop` / fixed `nu`; no CV-based early stopping.
124/// GCV path is tracked for post-hoc diagnostics only (see `gcv_path`).
125#[derive(Debug, Clone, PartialEq)]
126#[non_exhaustive]
127pub struct BoostFosrResult {
128    /// Intercept function F₀(t) = Ȳ(t) (pointwise mean of Y, length m_t).
129    pub intercept: Vec<f64>,
130    /// Accumulated coefficient functions β_j(t) per predictor (p × m_t).
131    ///
132    /// Row j holds the total contribution of base-learner j across all iterations
133    /// where it was selected, scaled by ν.
134    pub beta: FdMatrix,
135    /// Fitted functional values F̂(xᵢ, t) = Σ contributions (n × m_t).
136    pub fitted: FdMatrix,
137    /// Residual curves Y − F̂ (n × m_t).
138    pub residuals: FdMatrix,
139    /// Pointwise R²(t) at each response grid point (length m_t).
140    pub r_squared_t: Vec<f64>,
141    /// Integrated R² (scalar summary).
142    pub r_squared: f64,
143    /// Number of boosting iterations used.
144    pub mstop: usize,
145    /// Learning rate ν used.
146    pub nu: f64,
147    /// Index j* of the base-learner selected at each boosting iteration (length mstop).
148    pub selected_learners: Vec<usize>,
149    /// ‖residual‖_F² recorded after each iteration (length mstop).
150    ///
151    /// Should be non-increasing for L2 loss (use for path diagnostics / GCV).
152    pub gcv_path: Vec<f64>,
153}
154
155/// Result of component-wise boosted function-on-function regression (REG-06-02).
156///
157/// Functional predictors are compressed via FPCA score projection; the boosting
158/// core operates on the resulting scalar design matrices (bfpc variant).
159///
160/// **Divergence from FDboost:** uses FPC score compression (`fdata_to_pc_1d`) rather
161/// than FDboost's `bsignal` B-spline joint expansion. Simpler and dependency-free.
162#[derive(Debug, Clone, PartialEq)]
163#[non_exhaustive]
164pub struct BoostFofrResult {
165    /// Intercept function F₀(t) = Ȳ(t) (length m_y).
166    pub intercept: Vec<f64>,
167    /// Fitted response curves (n × m_y).
168    pub fitted: FdMatrix,
169    /// Residual curves (n × m_y).
170    pub residuals: FdMatrix,
171    /// Pointwise R²(t) at each response grid point (length m_y).
172    pub r_squared_t: Vec<f64>,
173    /// Overall R².
174    pub r_squared: f64,
175    /// FPCA result for each functional predictor (one per predictor).
176    pub fpca_x: Vec<crate::regression::FpcaResult>,
177    /// Accumulated FPC-space score coefficients per predictor (Vec[j] is K_j × m_y).
178    pub score_coefs: Vec<FdMatrix>,
179    /// Reconstructed coefficient surfaces β_j(s,t) per predictor (Vec[j] is m_x × m_y).
180    pub beta_surfaces: Vec<FdMatrix>,
181    /// Index j* of the base-learner selected at each boosting iteration (length mstop).
182    pub selected_learners: Vec<usize>,
183    /// ‖residual‖_F² per boosting iteration (length mstop).
184    pub gcv_path: Vec<f64>,
185    /// Number of boosting iterations used.
186    pub mstop: usize,
187    /// Learning rate ν used.
188    pub nu: f64,
189}
190
191/// Result of GAMLSS-style distributional functional regression (REG-06-03).
192///
193/// Models a Gaussian functional response Y(t) with location μ(t) and scale σ(t).
194/// Cyclic component-wise boosting alternates between boosting μ and log-σ.
195///
196/// **Divergence from gamboostLSS:** uses cyclic rather than noncyclic (non-cyclic
197/// per-iteration selection is superior for variable selection but more complex).
198/// Links: identity for μ, log for σ.
199#[derive(Debug, Clone, PartialEq)]
200#[non_exhaustive]
201pub struct GamlssResult {
202    /// Fitted location μ̂(t) per observation (n × m_t).
203    pub mu_fitted: FdMatrix,
204    /// Fitted scale σ̂(t) per observation (n × m_t); always positive.
205    pub sigma_fitted: FdMatrix,
206    /// Intercept for the μ model: F̂_μ,₀(t) = Ȳ(t) (length m_t).
207    pub mu_intercept: Vec<f64>,
208    /// Intercept for the log-σ model: 0 → exp(0) = 1 (length m_t).
209    pub sigma_intercept: Vec<f64>,
210    /// Accumulated μ coefficient functions (p × m_t).
211    pub mu_beta: FdMatrix,
212    /// Accumulated log-σ coefficient functions (p × m_t).
213    pub sigma_beta: FdMatrix,
214    /// Final Gaussian log-likelihood at convergence.
215    pub log_likelihood: f64,
216    /// Log-likelihood per cyclic iteration (length mstop).
217    pub ll_path: Vec<f64>,
218    /// Number of cyclic boosting iterations used.
219    pub mstop: usize,
220    /// Learning rate ν used.
221    pub nu: f64,
222}
223
224/// Result of Bayesian function-on-scalar regression via conjugate Gibbs (REG-06-04).
225///
226/// Posterior summaries are computed from thinned post-burn-in draws. Credible bands
227/// are pointwise (not simultaneous) quantiles over the retained draws.
228///
229/// **Divergence from refund:** uses FPCA score compression via `fdata_to_pc_1d`
230/// rather than spline basis priors. Pointwise credible bands only (no simultaneous bands).
231#[derive(Debug, Clone, PartialEq)]
232#[non_exhaustive]
233pub struct BayesianFosrResult {
234    /// Posterior mean coefficient functions β̄(t) (p × m_t).
235    pub beta_mean: FdMatrix,
236    /// Pointwise 2.5% credible band (p × m_t).
237    pub beta_lower: FdMatrix,
238    /// Pointwise 97.5% credible band (p × m_t).
239    pub beta_upper: FdMatrix,
240    /// Posterior-mean fitted values (n × m_t).
241    pub fitted: FdMatrix,
242    /// Posterior-mean residuals (n × m_t).
243    pub residuals: FdMatrix,
244    /// Posterior mean σ²(t) across the response grid (length m_t).
245    pub sigma2_mean: Vec<f64>,
246    /// Number of Gibbs iterations retained (after burn-in, before thinning).
247    pub n_iter: usize,
248    /// Burn-in iterations discarded.
249    pub burn_in: usize,
250    /// Thinning interval used.
251    pub thin: usize,
252    /// FPC components used for score compression.
253    pub ncomp: usize,
254}
255
256/// Result of FDboost-style stability selection (REG-06-05).
257///
258/// Aggregates base-learner selection frequencies over B subsamples of size ⌊n/2⌋.
259/// The PFER bound is informational: `E[V] ≤ q² / ((2·π_thr − 1)·p)`.
260#[derive(Debug, Clone, PartialEq)]
261#[non_exhaustive]
262pub struct StabilityResult {
263    /// Selection frequency π̂[j] ∈ [0, 1] for each base-learner j = 0..p.
264    pub selection_freq: Vec<f64>,
265    /// Indices j where π̂[j] ≥ pi_thr (the "stable set").
266    pub stable_set: Vec<usize>,
267    /// Threshold π used.
268    pub pi_thr: f64,
269    /// PFER upper bound: `q² / ((2·pi_thr − 1)·p)` where q = mean per-subsample selection count.
270    pub pfer_bound: f64,
271    /// Number of resamples B used.
272    pub n_resamples: usize,
273}
274
275// ---------------------------------------------------------------------------
276// Barrel re-exports
277// ---------------------------------------------------------------------------
278
279pub use self::bayesian::bayesian_fosr;
280pub use self::boost_fofr::boost_fofr;
281pub use self::boost_fosr::boost_fosr;
282pub use self::gamlss::gamlss_fosr;
283pub use self::stability::stability_selection;