solver/linalg/
regression.rs1use super::gaussian::solve_linear_system;
2use faer::prelude::*;
3
4pub trait RegressionSolver: Sized {
10 fn create(n_basis: usize) -> Self;
12
13 fn add_observation(&mut self, features: &[f64], target: f64);
17
18 fn solve(self, lambda: f64) -> Vec<f64>;
21
22 fn merge(&mut self, other: Self);
24}
25
26#[derive(Clone, Debug)]
32pub struct NormalEquationSolver {
33 n_basis: usize,
34 ata: Vec<f64>,
36 atb: Vec<f64>,
38}
39
40impl NormalEquationSolver {
41 pub fn new(n_basis: usize) -> Self {
42 Self::create(n_basis)
43 }
44}
45
46impl RegressionSolver for NormalEquationSolver {
47 fn create(n_basis: usize) -> Self {
48 Self {
49 n_basis,
50 ata: vec![0.0; n_basis * n_basis],
51 atb: vec![0.0; n_basis],
52 }
53 }
54
55 fn add_observation(&mut self, features: &[f64], target: f64) {
56 for i in 0..self.n_basis {
58 let feat_i = features[i];
61 self.atb[i] += feat_i * target;
62
63 let row_offset = i * self.n_basis;
67
68 self.ata[row_offset + i] += feat_i * feat_i;
70
71 for (j, &feat_j) in features.iter().enumerate().skip(i + 1) {
73 let val = feat_i * feat_j;
74 self.ata[row_offset + j] += val;
75 self.ata[j * self.n_basis + i] += val; }
77 }
78 }
79
80 fn solve(mut self, lambda: f64) -> Vec<f64> {
81 let n = self.n_basis;
82 let mut d = vec![1.0; n];
83 for (i, d_i) in d.iter_mut().enumerate() {
84 let diag = self.ata[i * n + i];
85 if diag > 1e-12 {
86 *d_i = 1.0 / diag.sqrt();
87 } else {
88 *d_i = 1.0; }
90 }
91
92 for i in 0..n {
94 for j in 0..n {
95 self.ata[i * n + j] *= d[i] * d[j];
96 }
97 self.atb[i] *= d[i];
98 }
99
100 if lambda > 0.0 {
102 for i in 0..n {
103 self.ata[i * n + i] += lambda;
104 }
105 }
106
107 let x_tilde = solve_linear_system(&self.ata, &self.atb, n);
108
109 let mut x = vec![0.0; n];
110 for i in 0..n {
111 x[i] = x_tilde[i] * d[i];
112 }
113 x
114 }
115
116 fn merge(&mut self, other: Self) {
117 for i in 0..self.ata.len() {
118 self.ata[i] += other.ata[i];
119 }
120 for i in 0..self.atb.len() {
121 self.atb[i] += other.atb[i];
122 }
123 }
124}
125
126#[derive(Clone, Debug)]
139pub struct FullMatrixSolver {
140 n_basis: usize,
141 a: Vec<f64>,
143 b: Vec<f64>,
145}
146
147impl FullMatrixSolver {
148 pub fn new(n_basis: usize) -> Self {
149 Self::create(n_basis)
150 }
151}
152
153impl RegressionSolver for FullMatrixSolver {
154 fn create(n_basis: usize) -> Self {
155 Self {
156 n_basis,
157 a: Vec::new(),
158 b: Vec::new(),
159 }
160 }
161
162 fn add_observation(&mut self, features: &[f64], target: f64) {
163 self.a.extend_from_slice(features);
164 self.b.push(target);
165 }
166
167 fn solve(self, _lambda: f64) -> Vec<f64> {
168 let n_samples = self.b.len();
169 let n_features = self.n_basis;
170
171 if n_samples < n_features {
172 return vec![0.0; n_features];
173 }
174
175 let mut v_col = vec![0.0; n_samples * n_features];
181 for i in 0..n_samples {
184 for j in 0..n_features {
185 v_col[j * n_samples + i] = self.a[i * n_features + j];
186 }
187 }
188
189 let mut q_col = vec![0.0; n_samples * n_features];
191 let mut r = vec![0.0; n_features * n_features]; for i in 0..n_features {
194 let v_i_start = i * n_samples;
195 let v_i_end = v_i_start + n_samples;
196 let v_i = &v_col[v_i_start..v_i_end];
197
198 let mut norm_sq = 0.0;
201 for &val in v_i.iter() {
202 norm_sq += val * val;
203 }
204 let norm = norm_sq.sqrt();
205 r[i * n_features + i] = norm;
206
207 let q_i_start = i * n_samples;
209 if norm > 1e-12 {
212 let inv_norm = 1.0 / norm;
213 for k in 0..n_samples {
214 q_col[q_i_start + k] = v_col[v_i_start + k] * inv_norm;
215 }
216 }
217
218 for j in (i + 1)..n_features {
220 let v_j_start = j * n_samples;
221
222 let mut projection = 0.0;
224 let v_j = &v_col[v_j_start..(v_j_start + n_samples)];
225 let q_i = &q_col[q_i_start..(q_i_start + n_samples)];
226
227 for k in 0..n_samples {
229 projection += q_i[k] * v_j[k];
230 }
231 r[i * n_features + j] = projection;
232
233 let v_j_mut = &mut v_col[v_j_start..(v_j_start + n_samples)];
236 for k in 0..n_samples {
237 v_j_mut[k] -= projection * q_i[k];
238 }
239 }
240 }
241
242 let mut y_rhs = vec![0.0; n_features];
248 for (i, y_rhs_i) in y_rhs.iter_mut().enumerate() {
249 let q_i_start = i * n_samples;
250 let mut sum = 0.0;
251 for (&a, &b) in q_col[q_i_start..].iter().zip(self.b.iter()) {
252 sum += a * b;
253 }
254 *y_rhs_i = sum;
255 }
256
257 let mut x = vec![0.0; n_features];
259 for i in (0..n_features).rev() {
260 let mut sum = y_rhs[i];
261 for j in (i + 1)..n_features {
262 sum -= r[i * n_features + j] * x[j];
263 }
264 if r[i * n_features + i].abs() > 1e-12 {
265 x[i] = sum / r[i * n_features + i];
266 } else {
267 x[i] = 0.0;
268 }
269 }
270
271 x
272 }
273
274 fn merge(&mut self, mut other: Self) {
275 self.a.append(&mut other.a);
276 self.b.append(&mut other.b);
277 }
278}
279
280#[derive(Clone, Debug)]
284pub struct FaerSolver {
285 n_basis: usize,
286 a: Vec<f64>,
287 b: Vec<f64>,
288}
289
290impl RegressionSolver for FaerSolver {
291 fn create(n_basis: usize) -> Self {
292 Self {
293 n_basis,
294 a: Vec::new(),
295 b: Vec::new(),
296 }
297 }
298
299 fn add_observation(&mut self, features: &[f64], target: f64) {
300 self.a.extend_from_slice(features);
301 self.b.push(target);
302 }
303
304 fn solve(self, lambda: f64) -> Vec<f64> {
305 let n_samples = self.b.len();
306 let n_features = self.n_basis;
307 if n_samples < n_features {
308 return vec![0.0; n_features];
309 }
310
311 let mut a_col = vec![0.0; n_samples * n_features];
314 for i in 0..n_samples {
315 for j in 0..n_features {
316 a_col[j * n_samples + i] = self.a[i * n_features + j];
317 }
318 }
319
320 if lambda > 0.0 {
323 let sqrt_lam = lambda.sqrt();
324 let total_rows = n_samples + n_features;
325 let mut a_aug = vec![0.0; total_rows * n_features];
326 for j in 0..n_features {
328 for i in 0..n_samples {
329 a_aug[j * total_rows + i] = a_col[j * n_samples + i];
330 }
331 a_aug[j * total_rows + n_samples + j] = sqrt_lam;
333 }
334 let mut b_aug = Vec::with_capacity(total_rows);
335 b_aug.extend_from_slice(&self.b);
336 b_aug.resize(total_rows, 0.0);
337
338 let a_mat = faer::mat::from_column_major_slice::<f64>(&a_aug, total_rows, n_features);
339 let b_mat = faer::mat::from_column_major_slice::<f64>(&b_aug, total_rows, 1);
340 let qr = a_mat.col_piv_qr();
341 let x = qr.solve_lstsq(b_mat);
342 return (0..n_features).map(|i| x.read(i, 0)).collect();
343 }
344
345 let a_mat = faer::mat::from_column_major_slice::<f64>(&a_col, n_samples, n_features);
347 let b_mat = faer::mat::from_column_major_slice::<f64>(&self.b, n_samples, 1);
348 let qr = a_mat.col_piv_qr();
349 let x = qr.solve_lstsq(b_mat);
350 (0..n_features).map(|i| x.read(i, 0)).collect()
351 }
352
353 fn merge(&mut self, mut other: Self) {
354 self.a.append(&mut other.a);
355 self.b.append(&mut other.b);
356 }
357}