STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
LinearAlgebra.hpp
Go to the documentation of this file.
1#ifndef COMMS_LINEAR_ALGEBRA_HPP
2#define COMMS_LINEAR_ALGEBRA_HPP
3
4#include <algorithm>
5#include <cmath>
6#include <stdexcept>
7
8#include "Matrix.hpp"
9
10namespace comms {
11namespace math {
12
13/**
14 * @brief Linear algebra operations for sensor calibration and filtering
15 *
16 * Standalone implementation for DAQ system.
17 * Provides matrix decompositions, solving, and advanced operations needed
18 * for Bayesian calibration and filtering algorithms.
19 */
20
21// Cholesky decomposition: A = L * L^T (for positive definite matrices)
22// A: n x n (input/output), L: n x n (output, lower triangular)
23// Returns true if successful, false if matrix is not positive definite
24inline bool cholesky_decompose(double* A, size_t n, double* L) {
25 // Initialize L to zero
26 matrix_zeros(L, n, n);
27
28 for (size_t i = 0; i < n; i++) {
29 for (size_t j = 0; j <= i; j++) {
30 double sum = 0.0;
31 if (j == i) {
32 // Diagonal element
33 for (size_t k = 0; k < j; k++) {
34 sum += L[j * n + k] * L[j * n + k];
35 }
36 double diag = A[i * n + i] - sum;
37 if (diag <= 0.0) {
38 return false; // Not positive definite
39 }
40 L[i * n + i] = std::sqrt(diag);
41 } else {
42 // Off-diagonal element
43 for (size_t k = 0; k < j; k++) {
44 sum += L[i * n + k] * L[j * n + k];
45 }
46 L[i * n + j] = (A[i * n + j] - sum) / L[j * n + j];
47 }
48 }
49 }
50 return true;
51}
52
53// Solve L * x = b where L is lower triangular
54inline void solve_lower_triangular(const double* L, size_t n, const double* b, double* x) {
55 for (size_t i = 0; i < n; i++) {
56 double sum = 0.0;
57 for (size_t j = 0; j < i; j++) {
58 sum += L[i * n + j] * x[j];
59 }
60 x[i] = (b[i] - sum) / L[i * n + i];
61 }
62}
63
64// Solve L^T * x = b where L^T is upper triangular
65inline void solve_upper_triangular(const double* L, size_t n, const double* b, double* x) {
66 for (int i = static_cast<int>(n) - 1; i >= 0; i--) {
67 double sum = 0.0;
68 for (size_t j = static_cast<size_t>(i) + 1; j < n; j++) {
69 sum += L[j * n + i] * x[j]; // Note: L^T[j][i] = L[i][j]
70 }
71 x[i] = (b[i] - sum) / L[i * n + i];
72 }
73}
74
75// Solve A * x = b using Cholesky decomposition
76// A: n x n (positive definite), b: n x 1, x: n x 1 (output)
77inline bool solve_cholesky(const double* A, size_t n, const double* b, double* x) {
78 // Allocate workspace
79 double* L = new double[n * n];
80
81 // Decompose A = L * L^T
82 if (!cholesky_decompose(const_cast<double*>(A), n, L)) {
83 delete[] L;
84 return false;
85 }
86
87 // Solve L * y = b
88 double* y = new double[n];
90
91 // Solve L^T * x = y
92 solve_upper_triangular(L, n, y, x);
93
94 delete[] y;
95 delete[] L;
96 return true;
97}
98
99// Matrix inverse using Cholesky decomposition (for positive definite matrices)
100// A: n x n (input), A_inv: n x n (output)
101inline bool matrix_inverse_cholesky(const double* A, size_t n, double* A_inv) {
102 // Allocate workspace
103 double* L = new double[n * n];
104
105 // Decompose A = L * L^T
106 if (!cholesky_decompose(const_cast<double*>(A), n, L)) {
107 delete[] L;
108 return false;
109 }
110
111 // Solve L * L^T * A_inv = I column by column
112 double* b = new double[n];
113 double* x = new double[n];
114
115 for (size_t j = 0; j < n; j++) {
116 // Set b to j-th column of identity matrix
117 std::fill(b, b + n, 0.0);
118 b[j] = 1.0;
119
120 // Solve L * y = b
121 solve_lower_triangular(L, n, b, x);
122
123 // Solve L^T * col = y
124 solve_upper_triangular(L, n, x, b);
125
126 // Copy result to j-th column of A_inv
127 for (size_t i = 0; i < n; i++) {
128 A_inv[i * n + j] = b[i];
129 }
130 }
131
132 delete[] x;
133 delete[] b;
134 delete[] L;
135 return true;
136}
137
138// Weighted least squares: solve (X^T * W * X) * beta = X^T * W * y
139// X: m x n (design matrix), y: m x 1 (observations), W: m x m (diagonal weight matrix)
140// beta: n x 1 (output coefficients)
141inline bool weighted_least_squares(const double* X, size_t m, size_t n, const double* y,
142 const double* W_diag, double* beta) {
143 // Allocate workspace
144 double* XTWX = new double[n * n];
145 double* XTWy = new double[n];
146
147 // Compute X^T * W * X (where W is diagonal)
148 matrix_zeros(XTWX, n, n);
149 for (size_t i = 0; i < n; i++) {
150 for (size_t j = 0; j < n; j++) {
151 double sum = 0.0;
152 for (size_t k = 0; k < m; k++) {
153 sum += X[k * n + i] * W_diag[k] * X[k * n + j];
154 }
155 XTWX[i * n + j] = sum;
156 }
157 }
158
159 // Compute X^T * W * y
160 std::fill(XTWy, XTWy + n, 0.0);
161 for (size_t i = 0; i < n; i++) {
162 double sum = 0.0;
163 for (size_t k = 0; k < m; k++) {
164 sum += X[k * n + i] * W_diag[k] * y[k];
165 }
166 XTWy[i] = sum;
167 }
168
169 // Solve (X^T * W * X) * beta = X^T * W * y using Cholesky
170 bool success = solve_cholesky(XTWX, n, XTWy, beta);
171
172 delete[] XTWy;
173 delete[] XTWX;
174 return success;
175}
176
177// Regularized weighted least squares (for Bayesian regression)
178// Adds regularization term: (X^T * W * X + lambda * I) * beta = X^T * W * y
179inline bool regularized_weighted_least_squares(const double* X, size_t m, size_t n, const double* y,
180 const double* W_diag, double lambda, double* beta) {
181 // Allocate workspace
182 double* XTWX = new double[n * n];
183 double* XTWy = new double[n];
184
185 // Compute X^T * W * X
186 matrix_zeros(XTWX, n, n);
187 for (size_t i = 0; i < n; i++) {
188 for (size_t j = 0; j < n; j++) {
189 double sum = 0.0;
190 for (size_t k = 0; k < m; k++) {
191 sum += X[k * n + i] * W_diag[k] * X[k * n + j];
192 }
193 XTWX[i * n + j] = sum;
194 }
195 }
196
197 // Add regularization: XTWX += lambda * I
198 for (size_t i = 0; i < n; i++) {
199 XTWX[i * n + i] += lambda;
200 }
201
202 // Compute X^T * W * y
203 std::fill(XTWy, XTWy + n, 0.0);
204 for (size_t i = 0; i < n; i++) {
205 double sum = 0.0;
206 for (size_t k = 0; k < m; k++) {
207 sum += X[k * n + i] * W_diag[k] * y[k];
208 }
209 XTWy[i] = sum;
210 }
211
212 // Solve regularized system using Cholesky
213 bool success = solve_cholesky(XTWX, n, XTWy, beta);
214
215 delete[] XTWy;
216 delete[] XTWX;
217 return success;
218}
219
220} // namespace math
221} // namespace comms
222
223#endif // COMMS_LINEAR_ALGEBRA_HPP
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
Definition json.hpp:13878
bool solve_cholesky(const double *A, size_t n, const double *b, double *x)
void matrix_zeros(double *A, size_t m, size_t n)
Definition Matrix.hpp:70
bool regularized_weighted_least_squares(const double *X, size_t m, size_t n, const double *y, const double *W_diag, double lambda, double *beta)
void solve_lower_triangular(const double *L, size_t n, const double *b, double *x)
bool matrix_inverse_cholesky(const double *A, size_t n, double *A_inv)
void solve_upper_triangular(const double *L, size_t n, const double *b, double *x)
bool weighted_least_squares(const double *X, size_t m, size_t n, const double *y, const double *W_diag, double *beta)
bool cholesky_decompose(double *A, size_t n, double *L)
Linear algebra operations for sensor calibration and filtering.
Central message header for all communication messages.
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
bool success