LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
eigen_svd.c
浏览该文件的文档.
1
5#include <math.h>
6#include <string.h>
8#include "internal.h"
9#include "eigen_internal.h"
10#include "lmmc/linear_algebra.h"
11
12static lmmc_status_t restore_singular_value_scale(lmmc_vec_t *sigma,
13 lmmc_real_t scale)
14{
15 for (size_t i = 0; i < sigma->size; ++i) {
16 sigma->data[i] *= scale;
17 if (!isfinite(sigma->data[i])) {
19 }
20 }
21 return LMMC_STATUS_OK;
22}
23
24static lmmc_status_t transpose_factors(const lmmc_svd_result_t *source,
25 lmmc_svd_result_t *result)
26{
27 lmmc_status_t status = lmmc_mat_create(source->Vt.rows, source->Vt.cols,
28 &result->U);
29 if (status != LMMC_STATUS_OK) {
30 return status;
31 }
32 status = lmmc_vec_create(source->sigma.size, &result->sigma);
33 if (status != LMMC_STATUS_OK) {
34 return status;
35 }
36 status = lmmc_mat_create(source->U.rows, source->U.cols, &result->Vt);
37 if (status != LMMC_STATUS_OK) {
38 return status;
39 }
40 status = lmmc_mat_transpose_to(&source->Vt, &result->U);
41 if (status != LMMC_STATUS_OK) {
42 return status;
43 }
44 for (size_t i = 0; i < source->sigma.size; ++i) {
45 result->sigma.data[i] = source->sigma.data[i];
46 }
47 return lmmc_mat_transpose_to(&source->U, &result->Vt);
48}
49
50static lmmc_status_t svd_wide(const lmmc_mat_t *a, lmmc_real_t scale,
51 lmmc_svd_result_t *result)
52{
53 lmmc_mat_t transpose;
54 lmmc_svd_result_t temporary = {0};
55 lmmc_status_t status = lmmc_mat_create(a->cols, a->rows, &transpose);
56 if (status != LMMC_STATUS_OK) {
57 return status;
58 }
59 status = lmmc_mat_transpose_to(a, &transpose);
60 if (status == LMMC_STATUS_OK) {
61 status = lmmc_svd_tall(&transpose, scale, &temporary);
62 }
63 lmmc_mat_destroy(&transpose);
64 if (status != LMMC_STATUS_OK) {
65 return status;
66 }
67 status = restore_singular_value_scale(&temporary.sigma, scale);
68 if (status == LMMC_STATUS_OK) {
69 status = transpose_factors(&temporary, result);
70 }
71 lmmc_svd_result_destroy(&temporary);
72 return status;
73}
74
76{
77 lmmc_real_t scale;
78 if (!lmmc_mat_descriptor_is_valid(a) || !out_result) {
80 }
81 lmmc_status_t status = lmmc_eigen_matrix_scale(a, &scale);
82 if (status != LMMC_STATUS_OK) {
83 return status;
84 }
85 lmmc_svd_result_t result = {0};
86 if (a->rows >= a->cols) {
87 status = lmmc_svd_tall(a, scale, &result);
88 if (status == LMMC_STATUS_OK) {
89 status = restore_singular_value_scale(&result.sigma, scale);
90 }
91 } else {
92 status = svd_wide(a, scale, &result);
93 }
94 if (status == LMMC_STATUS_OK) {
95 *out_result = result;
96 } else {
98 }
99 return status;
100}
101
102static int matrix_is_finite(const lmmc_mat_t *matrix)
103{
104 for (size_t i = 0; i < matrix->rows; ++i) {
105 for (size_t j = 0; j < matrix->cols; ++j) {
106 if (!isfinite(MAT_ELEM(matrix, i, j))) {
107 return 0;
108 }
109 }
110 }
111 return 1;
112}
113
115static lmmc_status_t prepare_reciprocals(lmmc_svd_result_t *svd,
116 lmmc_real_t tol)
117{
118 if (!matrix_is_finite(&svd->U) || !matrix_is_finite(&svd->Vt)) {
120 }
121 const size_t m = svd->U.rows;
122 const size_t n = svd->Vt.rows;
123 if (tol <= 0.0) {
124 lmmc_real_t dimension = (lmmc_real_t)((m > n) ? m : n);
125 tol = LMMC_REAL_EPSILON * dimension * svd->sigma.data[0];
126 }
127 if (!isfinite(tol)) {
129 }
130 for (size_t k = 0; k < svd->sigma.size; ++k) {
131 lmmc_real_t singular = svd->sigma.data[k];
132 if (!isfinite(singular) || singular < 0.0) {
134 }
135 lmmc_real_t reciprocal = (singular > tol) ? 1.0 / singular : 0.0;
136 if (!isfinite(reciprocal)) {
138 }
139 svd->sigma.data[k] = reciprocal;
140 }
141 return LMMC_STATUS_OK;
142}
143
144static lmmc_status_t reconstruct_entry(const lmmc_svd_result_t *svd,
145 size_t i, size_t j, lmmc_real_t *value)
146{
147 lmmc_real_t sum = 0.0;
148 for (size_t k = 0; k < svd->sigma.size; ++k) {
149 const lmmc_real_t reciprocal = svd->sigma.data[k];
150 if (reciprocal == 0.0) {
151 continue;
152 }
153 lmmc_real_t product = MAT_ELEM(&svd->Vt, k, i) * MAT_ELEM(&svd->U, j, k);
154 if (!isfinite(product)) {
156 }
157 product *= reciprocal;
158 if (!isfinite(product)) {
160 }
161 sum += product;
162 if (!isfinite(sum)) {
164 }
165 }
166 *value = sum;
167 return LMMC_STATUS_OK;
168}
169
170static lmmc_status_t reconstruct_pinv(const lmmc_svd_result_t *svd,
171 lmmc_mat_t *candidate)
172{
173 for (size_t i = 0; i < candidate->rows; ++i) {
174 for (size_t j = 0; j < candidate->cols; ++j) {
175 lmmc_status_t status = reconstruct_entry(svd, i, j,
176 &MAT_ELEM(candidate, i, j));
177 if (status != LMMC_STATUS_OK) {
178 return status;
179 }
180 }
181 }
182 return LMMC_STATUS_OK;
183}
184
185static lmmc_status_t validate_pinv(const lmmc_mat_t *a, lmmc_real_t tol,
186 const lmmc_mat_t *out_pinv)
187{
188 if (!a || !out_pinv || !a->data || !out_pinv->data) {
190 }
191 if (a->rows == 0 || a->cols == 0) {
193 }
194 if (out_pinv->rows != a->cols || out_pinv->cols != a->rows) {
196 }
197 if (!lmmc_mat_descriptor_is_valid(a) || !lmmc_mat_descriptor_is_valid(out_pinv)) {
199 }
200 return isfinite(tol) ? LMMC_STATUS_OK : LMMC_STATUS_NUMERICAL_FAILURE;
201}
202
204{
205 lmmc_status_t status = validate_pinv(a, tol, out_pinv);
206 if (status != LMMC_STATUS_OK) {
207 return status;
208 }
210 status = lmmc_svd(a, &svd);
211 if (status != LMMC_STATUS_OK) {
212 return status;
213 }
214 lmmc_mat_t candidate = {0};
215 status = prepare_reciprocals(&svd, tol);
216 if (status != LMMC_STATUS_OK) {
217 goto cleanup;
218 }
219 status = lmmc_mat_create(a->cols, a->rows, &candidate);
220 if (status != LMMC_STATUS_OK) {
221 goto cleanup;
222 }
223 status = reconstruct_pinv(&svd, &candidate);
224 if (status == LMMC_STATUS_OK) {
225 for (size_t i = 0; i < candidate.rows; ++i) {
226 memcpy(out_pinv->data + i * out_pinv->stride,
227 candidate.data + i * candidate.stride,
228 candidate.cols * sizeof(*candidate.data));
229 }
230 }
231cleanup:
232 lmmc_mat_destroy(&candidate);
234 return status;
235}
236
238{
239 if (!a || !out_cond || !a->data) {
241 }
242 if (a->rows == 0 || a->cols == 0) {
244 }
246 lmmc_status_t status = lmmc_svd(a, &svd);
247 if (status != LMMC_STATUS_OK) {
248 return status;
249 }
250 size_t p = svd.sigma.size;
251 if (p == 0) {
254 }
255 lmmc_real_t s_max = svd.sigma.data[0];
256 lmmc_real_t s_min = svd.sigma.data[p - 1];
257 *out_cond = (s_min <= 0.0) ? INFINITY : s_max / s_min;
259 return LMMC_STATUS_OK;
260}
261
263{
264 if (!result) {
265 return;
266 }
267 lmmc_mat_destroy(&result->U);
268 lmmc_vec_destroy(&result->sigma);
269 lmmc_mat_destroy(&result->Vt);
270}
double lmmc_real_t
Public and internal LMMC real type.
#define LMMC_REAL_EPSILON
既有算法的经验近似阈值,非机器 epsilon。
lmmc_status_t lmmc_mat_create(size_t rows, size_t cols, lmmc_mat_t *out_mat)
创建 rows x cols 的零矩阵。
lmmc_status_t lmmc_mat_transpose_to(const lmmc_mat_t *src, lmmc_mat_t *dst)
计算矩阵转置:dst = src^T。
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
lmmc_real_t * data
行优先存储的稠密矩阵。
一维稠密向量。
lmmc_status_t lmmc_svd(const lmmc_mat_t *a, lmmc_svd_result_t *out_result)
计算奇异值分解:A = U * Σ * V^T.
lmmc_vec_t sigma
定义 eigen.h:54
lmmc_status_t lmmc_pinv(const lmmc_mat_t *a, lmmc_real_t tol, lmmc_mat_t *out_pinv)
计算 Moore-Penrose 伪逆 A^+。
lmmc_status_t lmmc_cond(const lmmc_mat_t *a, lmmc_real_t *out_cond)
计算矩阵的 2-范数条件数:κ_2(A) = sigma_max / sigma_min.
void lmmc_svd_result_destroy(lmmc_svd_result_t *result)
释放 lmmc_svd 输出结果.
奇异值分解结果: .
定义 eigen.h:52
lmmc_status_t lmmc_eigen_matrix_scale(const lmmc_mat_t *a, lmmc_real_t *scale)
特征值 / SVD 模块内部共享的缩放、Householder 与 Givens 辅助接口(仅源文件可见)。
#define MAT_ELEM(mat, i, j)
lmmc_status_t lmmc_svd_tall(const lmmc_mat_t *a, lmmc_real_t input_scale, lmmc_svd_result_t *result)
LMMC 内部使用的公共工具(仅源文件可见).
稠密矩阵直接分解接口(LU、Cholesky、QR)。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_DIMENSION_MISMATCH
@ LMMC_STATUS_INVALID_ARGUMENT