LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
eigen_symmetric.c
浏览该文件的文档.
1
5
6#include <math.h>
7#include <string.h>
8#include "memory_bridge.h"
9#include "internal.h"
10#include "eigen_internal.h"
11#include "lmmc/config.h"
12#include "lmmc/dense.h"
13#include "lmmc/eigen.h"
14#include "lmmc/linear_algebra.h"
16
17static void tridiag_rank_update(lmmc_mat_t* a, size_t k, lmmc_real_t tau,
18 lmmc_real_t* w)
19{
20 size_t n = a->rows;
21 size_t i, j;
22 for (i = 0; i < n - k - 1; i++) {
23 lmmc_real_t sum = 0.0;
24 for (j = 0; j < n - k - 1; j++) {
25 lmmc_real_t vj = (j == 0) ? 1.0 : MAT_ELEM(a, k + 1 + j, k);
26 sum += MAT_ELEM(a, k + 1 + i, k + 1 + j) * vj;
27 }
28 w[i] = tau * sum;
29 }
30 lmmc_real_t gamma = 0.0;
31 for (i = 0; i < n - k - 1; i++) {
32 lmmc_real_t vi = (i == 0) ? 1.0 : MAT_ELEM(a, k + 1 + i, k);
33 gamma += w[i] * vi;
34 }
35 gamma *= 0.5 * tau;
36 for (i = 0; i < n - k - 1; i++) {
37 lmmc_real_t vi = (i == 0) ? 1.0 : MAT_ELEM(a, k + 1 + i, k);
38 w[i] -= gamma * vi;
39 }
40 for (i = 0; i < n - k - 1; i++) {
41 lmmc_real_t vi = (i == 0) ? 1.0 : MAT_ELEM(a, k + 1 + i, k);
42 for (j = i; j < n - k - 1; j++) {
43 lmmc_real_t vj = (j == 0) ? 1.0 : MAT_ELEM(a, k + 1 + j, k);
44 lmmc_real_t update = vi * w[j] + w[i] * vj;
45 MAT_ELEM(a, k + 1 + i, k + 1 + j) -= update;
46 if (i != j) {
47 MAT_ELEM(a, k + 1 + j, k + 1 + i) -= update;
48 }
49 }
50 }
51}
52
53static lmmc_status_t tridiag_column(lmmc_mat_t* a, size_t k, lmmc_real_t* diag,
54 lmmc_real_t* offdiag, lmmc_real_t* taus, lmmc_real_t** workspace)
55{
56 size_t n = a->rows;
57 size_t i;
59 lmmc_scaled_sumsq_init(&acc);
60 for (i = k + 2; i < n; i++) {
61 lmmc_scaled_sumsq_add(&acc, MAT_ELEM(a, i, k));
62 }
63 lmmc_real_t tail_norm = lmmc_scaled_sumsq_norm(&acc);
64 lmmc_real_t alpha_val = MAT_ELEM(a, k + 1, k);
65 if (tail_norm == 0.0) {
66 offdiag[k] = alpha_val; diag[k] = MAT_ELEM(a, k, k);
67 if (taus) {
68 taus[k] = 0.0;
69 }
70
71 for (i = k + 2; i < n; i++) {
72 MAT_ELEM(a, i, k) = 0.0;
73 }
74 return LMMC_STATUS_OK;
75 }
76 lmmc_real_t norm_x = hypot(alpha_val, tail_norm);
77 lmmc_real_t beta = -copysign(norm_x, alpha_val);
78 lmmc_real_t tau = 1.0 - alpha_val / beta;
79 lmmc_real_t denominator_scaled =
80 alpha_val / norm_x - beta / norm_x;
81 for (i = k + 2; i < n; i++) {
82 MAT_ELEM(a, i, k) =
83 (MAT_ELEM(a, i, k) / norm_x) / denominator_scaled;
84 }
85 offdiag[k] = beta;
86 if (taus) {
87 taus[k] = tau;
88 }
89 if (!*workspace) {
90 *workspace = (lmmc_real_t*)lmmc_memory_alloc_array(n - k - 1, sizeof(lmmc_real_t));
91 if (!*workspace) {
93 }
94 }
95 tridiag_rank_update(a, k, tau, *workspace);
96 return LMMC_STATUS_OK;
97}
98
99static lmmc_status_t householder_tridiag(lmmc_mat_t* a, lmmc_real_t* diag,
100 lmmc_real_t* offdiag, lmmc_real_t* taus)
101{
102 size_t n = a->rows;
103 if (taus) {
104 for (size_t i = 0; i + 1 < n; i++) taus[i] = 0.0;
105 }
106 if (n == 1) {
107 diag[0] = MAT_ELEM(a, 0, 0);
108 return LMMC_STATUS_OK;
109 }
111 lmmc_real_t* w = NULL;
113 for (size_t k = 0; k < n - 1; k++) {
114 status = tridiag_column(a, k, diag, offdiag, taus, &w);
115 if (status != LMMC_STATUS_OK) {
116 break;
117 }
118 }
119 if (w) {
121 }
122 if (status != LMMC_STATUS_OK) {
123 return status;
124 }
125 for (size_t i = 0; i < n; i++) diag[i] = MAT_ELEM(a, i, i);
126 return LMMC_STATUS_OK;
127}
128
129static void accumulate_reflector(const lmmc_mat_t* a, size_t k,
130 lmmc_real_t tau, lmmc_mat_t* Q)
131{
132 size_t n = a->rows;
133 size_t i, j;
134 for (j = 0; j < n; j++) {
135 lmmc_real_t s = MAT_ELEM(Q, k + 1, j);
136 for (i = k + 2; i < n; i++) {
137 s += MAT_ELEM(a, i, k) * MAT_ELEM(Q, i, j);
138 }
139 s *= tau;
140 MAT_ELEM(Q, k + 1, j) -= s;
141 for (i = k + 2; i < n; i++) {
142 MAT_ELEM(Q, i, j) -= s * MAT_ELEM(a, i, k);
143 }
144 }
145}
146
147static lmmc_status_t accumulate_q(const lmmc_mat_t *a, const lmmc_real_t *taus, lmmc_mat_t *Q) {
148 size_t n = a->rows; size_t i, j, k;
149 for (i = 0; i < n; i++) {
150 for (j = 0; j < n; j++) {
151 MAT_ELEM(Q, i, j) = (i == j) ? 1.0 : 0.0;
152 }
153 }
154 if (n <= 2) {
155 return LMMC_STATUS_OK;
156 }
157
158 for (k = n - 3; ; k--) {
159 lmmc_real_t tau = taus ? taus[k] : 0.0;
160 if (tau != 0.0) {
161
162 accumulate_reflector(a, k, tau, Q);
163 }
164 if (k == 0) {
165 break;
166 }
167 }
168 return LMMC_STATUS_OK;
169}
170
171static void ql_rotate_vectors(lmmc_mat_t* Q, size_t i, lmmc_real_t s, lmmc_real_t c)
172{
173 if (!Q) {
174 return;
175 }
176 for (size_t k = 0; k < Q->rows; k++) {
177 lmmc_real_t fv = MAT_ELEM(Q, k, i);
178 MAT_ELEM(Q, k, i) = s * MAT_ELEM(Q, k, i - 1) + c * fv;
179 MAT_ELEM(Q, k, i - 1) = c * MAT_ELEM(Q, k, i - 1) - s * fv;
180 }
181}
182
183static void tridiag_ql_step(lmmc_real_t* diag, lmmc_real_t* offdiag,
184 size_t l, size_t m, lmmc_mat_t* Q)
185{
186 size_t i;
187 lmmc_real_t c, g, p, r, s, f, b;
188 g = (diag[l + 1] - diag[l]) / (2.0 * offdiag[l]);
189 r = hypot(g, 1.0);
190 if (g >= 0.0) {
191 g = diag[m] - diag[l] + offdiag[l] / (g + r);
192 } else {
193 g = diag[m] - diag[l] + offdiag[l] / (g - r);
194 }
195 s = 1.0; c = 1.0; p = 0.0;
196 for (i = m; i > l; i--) {
197 f = s * offdiag[i - 1]; b = c * offdiag[i - 1];
198 if (lmmc_abs(f) >= lmmc_abs(g)) {
199 c = g / f; r = sqrt(c * c + 1.0); offdiag[i] = f * r; s = 1.0 / r; c *= s;
200 } else {
201 s = f / g; r = sqrt(s * s + 1.0); offdiag[i] = g * r; c = 1.0 / r; s *= c;
202 }
203 g = diag[i] - p; r = (diag[i - 1] - g) * s + 2.0 * c * b;
204 p = s * r; diag[i] = g + p; g = c * r - b;
205 ql_rotate_vectors(Q, i, s, c);
206 }
207 diag[l] -= p; offdiag[l] = g; offdiag[m] = 0.0;
208}
209
210static lmmc_status_t tridiag_ql(lmmc_real_t *diag, lmmc_real_t *offdiag, size_t n, lmmc_mat_t *Q) {
211 size_t l, m, iter;
212 lmmc_real_t tst1;
213 lmmc_real_t eps = 2.2204460492503131e-16;
214 if (n <= 1) {
215 return LMMC_STATUS_OK;
216 }
217 for (l = 0; l < n; l++) {
218 iter = 0;
219 while (1) {
220 for (m = l; m < n - 1; m++) {
221 tst1 = lmmc_abs(diag[m]) + lmmc_abs(diag[m + 1]);
222 if (tst1 == 0.0) {
223 tst1 = 1.0;
224 }
225 if (lmmc_abs(offdiag[m]) <= eps * tst1) {
226 break;
227 }
228 }
229 if (m == l) {
230 break;
231 }
232 if (iter >= (size_t)EIGEN_MAX_ITER) {
234 }
235 iter++;
236 tridiag_ql_step(diag, offdiag, l, m, Q);
237 }
238 }
239 return LMMC_STATUS_OK;
240}
241
242static void sort_eigen_ascending(lmmc_real_t *eigenvalues, lmmc_mat_t *Q, size_t n) {
243 size_t i, j, min_idx;
244 for (i = 0; i < n - 1; i++) {
245 min_idx = i;
246 for (j = i + 1; j < n; j++) {
247 if (eigenvalues[j] < eigenvalues[min_idx]) {
248 min_idx = j;
249 }
250 }
251 if (min_idx != i) {
252 lmmc_swap(&eigenvalues[i], &eigenvalues[min_idx]);
253 if (Q) {
254 for (j = 0; j < n; j++) {
255 lmmc_swap(&MAT_ELEM(Q, j, i), &MAT_ELEM(Q, j, min_idx));
256 }
257 }
258 }
259 }
260}
261
262static void tridiag_positive_offdiagonal(lmmc_real_t* offdiag, lmmc_mat_t* vectors)
263{
264 size_t n = vectors->rows;
265 size_t ii, jj;
266 lmmc_real_t s = 1.0;
267 for (ii = 0; ii < n - 1; ii++) {
268 lmmc_real_t off = offdiag[ii];
269 lmmc_real_t sign_next = (off >= 0.0) ? s : -s;
270 offdiag[ii] = (off >= 0.0) ? off : -off;
271 s = sign_next;
272 if (sign_next < 0.0) {
273
274 for (jj = 0; jj < n; jj++) {
275 MAT_ELEM(vectors, jj, ii + 1) =
276 -MAT_ELEM(vectors, jj, ii + 1);
277 }
278 }
279 }
280}
281
282static lmmc_status_t symmetric_reduce(lmmc_mat_t* work, lmmc_real_t* offdiag,
284{
285 size_t n = work->rows;
286 lmmc_real_t* taus = (lmmc_real_t*)lmmc_memory_alloc_array(n > 1 ? n - 1 : 1, sizeof(lmmc_real_t));
287 if (!taus) {
289 }
290 lmmc_status_t status = householder_tridiag(
291 work, result->eigenvalues.data, offdiag, taus);
292 if (status == LMMC_STATUS_OK) {
293 status = accumulate_q(work, taus, &result->eigenvectors);
294 }
295 lmmc_memory_free(taus);
296 return status;
297}
298
299static lmmc_status_t symmetric_large(const lmmc_mat_t* a, lmmc_real_t matrix_scale,
301{
302 size_t n = a->rows;
303 lmmc_mat_t work;
304 lmmc_status_t status = lmmc_mat_create(n, n, &work);
305 if (status != LMMC_STATUS_OK) {
306 return status;
307 }
308 status = lmmc_mat_copy(a, &work);
309 if (status != LMMC_STATUS_OK) {
310 lmmc_mat_destroy(&work);
311 return status;
312 }
313 for (size_t i = 0; i < n; ++i) {
314 for (size_t j = 0; j < n; ++j) {
315 MAT_ELEM(&work, i, j) /= matrix_scale;
316 }
317 }
319 if (!offdiag) {
320 lmmc_mat_destroy(&work);
322 }
323 memset(offdiag, 0, n * sizeof(lmmc_real_t));
324 status = symmetric_reduce(&work, offdiag, result);
325 lmmc_mat_destroy(&work);
326 if (status == LMMC_STATUS_OK) {
327 tridiag_positive_offdiagonal(offdiag, &result->eigenvectors);
328 status = tridiag_ql(result->eigenvalues.data, offdiag, n, &result->eigenvectors);
329 }
330 lmmc_memory_free(offdiag);
331 return status;
332}
333
335 lmmc_status_t status;
336 lmmc_real_t matrix_scale = 0.0;
337 size_t n, i;
338 if (!lmmc_mat_descriptor_is_valid(a) || !out_result) {
340 }
341 if (a->rows != a->cols) {
343 }
344 n = a->rows;
345 status = lmmc_eigen_matrix_scale(a, &matrix_scale);
346 if (status != LMMC_STATUS_OK) {
347 return status;
348 }
349 status = lmmc_vec_create(n, &out_result->eigenvalues);
350 if (status != LMMC_STATUS_OK) {
351 return status;
352 }
353 status = lmmc_mat_create(n, n, &out_result->eigenvectors);
354 if (status != LMMC_STATUS_OK) { lmmc_vec_destroy(&out_result->eigenvalues); return status; }
355 if (n == 1) {
356 out_result->eigenvalues.data[0] = MAT_ELEM(a, 0, 0);
357 MAT_ELEM(&out_result->eigenvectors, 0, 0) = 1.0;
358 return LMMC_STATUS_OK;
359 }
360 status = symmetric_large(a, matrix_scale, out_result);
361 if (status != LMMC_STATUS_OK) {
363 return status;
364 }
365 sort_eigen_ascending(out_result->eigenvalues.data, &out_result->eigenvectors, n);
366 for (i = 0; i < n; ++i) {
367 out_result->eigenvalues.data[i] *= matrix_scale;
368 if (!isfinite(out_result->eigenvalues.data[i])) {
369 lmmc_vec_destroy(&out_result->eigenvalues);
370 lmmc_mat_destroy(&out_result->eigenvectors);
372 }
373 }
374 return LMMC_STATUS_OK;
375}
376
378 if (!result) {
379 return;
380 }
383}
LMMC binary real type and arithmetic spelling.
double lmmc_real_t
Public and internal LMMC real type.
lmmc_status_t lmmc_mat_copy(const lmmc_mat_t *src, lmmc_mat_t *dst)
逐元素复制矩阵内容:dst = src。
lmmc_status_t lmmc_mat_create(size_t rows, size_t cols, lmmc_mat_t *out_mat)
创建 rows x cols 的零矩阵。
lmmc_status_t lmmc_vec_create(size_t size, lmmc_vec_t *out_vec)
创建长度为 size 的零向量。
void lmmc_mat_destroy(lmmc_mat_t *mat)
销毁矩阵,释放其拥有的缓冲区并清零结构体字段。
void lmmc_vec_destroy(lmmc_vec_t *vec)
销毁向量,释放其拥有的缓冲区并清零字段。
lmmc_real_t * data
行优先存储的稠密矩阵。
特征值 / 奇异值分解,伪逆与条件数接口.
void lmmc_eigen_sym_result_destroy(lmmc_eigen_sym_result_t *result)
释放 lmmc_eigen_symmetric 输出结果.
lmmc_status_t lmmc_eigen_symmetric(const lmmc_mat_t *a, lmmc_eigen_sym_result_t *out_result)
计算实对称矩阵的全部特征值与特征向量.
对称矩阵特征分解结果.
定义 eigen.h:20
lmmc_status_t lmmc_eigen_matrix_scale(const lmmc_mat_t *a, lmmc_real_t *scale)
特征值 / SVD 模块内部共享的缩放、Householder 与 Givens 辅助接口(仅源文件可见)。
#define EIGEN_MAX_ITER
#define MAT_ELEM(mat, i, j)
LMMC 内部使用的公共工具(仅源文件可见).
稠密矩阵直接分解接口(LU、Cholesky、QR)。
void * lmmc_memory_alloc_array(size_t count, size_t element_size)
void lmmc_memory_free(void *pointer)
Private recoverable allocator for persistent LMMC ownership.
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_CONVERGENCE_FAILED
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_INVALID_ARGUMENT