fdars_core/elastic_regression/
mod.rs1pub mod logistic;
13pub mod pcr;
14pub mod regression;
15pub mod scalar_on_shape;
16
17#[cfg(test)]
18mod tests;
19
20pub use logistic::{
22 elastic_logistic, elastic_logistic_with_config, elastic_multinomial, predict_elastic_logistic,
23 predict_elastic_multinomial, ElasticLogisticResult, ElasticMultinomialResult,
24};
25pub use pcr::{elastic_pcr, elastic_pcr_with_config, ElasticPcrResult};
26pub use regression::{
27 elastic_regression, elastic_regression_with_config, predict_elastic_regression,
28 ElasticRegressionResult,
29};
30pub use scalar_on_shape::{predict_scalar_on_shape, scalar_on_shape, ScalarOnShapeResult};
31
32use crate::alignment::reparameterize_curve;
33use crate::matrix::FdMatrix;
34
35#[derive(Debug, Clone, PartialEq)]
39pub struct ElasticConfig {
40 pub ncomp_beta: usize,
42 pub lambda: f64,
44 pub max_iter: usize,
46 pub tol: f64,
48}
49
50impl Default for ElasticConfig {
51 fn default() -> Self {
52 Self {
53 ncomp_beta: 10,
54 lambda: 0.0,
55 max_iter: 20,
56 tol: 1e-4,
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq)]
63pub struct ElasticPcrConfig {
64 pub ncomp: usize,
66 pub pca_method: PcaMethod,
68 pub lambda: f64,
70 pub max_iter: usize,
72 pub tol: f64,
74}
75
76impl Default for ElasticPcrConfig {
77 fn default() -> Self {
78 Self {
79 ncomp: 3,
80 pca_method: PcaMethod::Vertical,
81 lambda: 0.0,
82 max_iter: 20,
83 tol: 1e-4,
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq)]
90pub struct ScalarOnShapeConfig {
91 pub nbasis: usize,
93 pub lambda: f64,
95 pub lfd_order: usize,
97 pub index_method: IndexMethod,
99 pub g_degree: usize,
101 pub max_iter_outer: usize,
103 pub max_iter_inner: usize,
105 pub tol: f64,
107 pub dp_lambda: f64,
109}
110
111impl Default for ScalarOnShapeConfig {
112 fn default() -> Self {
113 Self {
114 nbasis: 11,
115 lambda: 1e-3,
116 lfd_order: 2,
117 index_method: IndexMethod::Identity,
118 g_degree: 2,
119 max_iter_outer: 10,
120 max_iter_inner: 15,
121 tol: 1e-4,
122 dp_lambda: 0.0,
123 }
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq)]
131#[non_exhaustive]
132pub enum PcaMethod {
133 Vertical,
134 Horizontal,
135 Joint,
136}
137
138#[derive(Debug, Clone, PartialEq)]
142#[non_exhaustive]
143pub enum IndexMethod {
144 Identity,
146 Polynomial(usize),
148 NadarayaWatson(f64),
150}
151
152pub(super) fn apply_warps_to_srsfs(
156 q_all: &FdMatrix,
157 gammas: &FdMatrix,
158 argvals: &[f64],
159) -> FdMatrix {
160 let (n, m) = q_all.shape();
161 let h = (argvals[m - 1] - argvals[0]) / (m - 1) as f64;
162 let mut q_aligned = FdMatrix::zeros(n, m);
163 for i in 0..n {
164 let qi: Vec<f64> = (0..m).map(|j| q_all[(i, j)]).collect();
165 let gam: Vec<f64> = (0..m).map(|j| gammas[(i, j)]).collect();
166 let q_warped = reparameterize_curve(&qi, argvals, &gam);
167 let gam_deriv = crate::helpers::gradient_uniform(&gam, h);
168 for j in 0..m {
169 q_aligned[(i, j)] = q_warped[j] * gam_deriv[j].max(0.0).sqrt();
170 }
171 }
172 q_aligned
173}
174
175pub(super) fn init_identity_warps(n: usize, argvals: &[f64]) -> FdMatrix {
177 let m = argvals.len();
178 let mut gammas = FdMatrix::zeros(n, m);
179 for i in 0..n {
180 for j in 0..m {
181 gammas[(i, j)] = argvals[j];
182 }
183 }
184 gammas
185}
186
187pub(super) fn srsf_fitted_values(
189 q_aligned: &FdMatrix,
190 beta: &[f64],
191 weights: &[f64],
192 alpha: f64,
193) -> Vec<f64> {
194 let (n, m) = q_aligned.shape();
195 let mut fitted = vec![0.0; n];
196 for i in 0..n {
197 fitted[i] = alpha;
198 for j in 0..m {
199 fitted[i] += q_aligned[(i, j)] * beta[j] * weights[j];
200 }
201 }
202 fitted
203}
204
205pub(super) fn beta_converged(beta_new: &[f64], beta_old: &[f64], tol: f64) -> bool {
207 let diff: f64 = beta_new
208 .iter()
209 .zip(beta_old.iter())
210 .map(|(&a, &b)| (a - b).powi(2))
211 .sum::<f64>()
212 .sqrt();
213 let norm: f64 = beta_old
214 .iter()
215 .map(|&b| b * b)
216 .sum::<f64>()
217 .sqrt()
218 .max(1e-10);
219 diff / norm < tol
220}