LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
sparse_lu.c
浏览该文件的文档.
1#include <string.h>
2#include <math.h>
3#include "memory_bridge.h"
4#include "internal.h"
5#include "lmmc/sparse.h"
7
8static lmmc_status_t lu_allocate_factors(lmmc_sparse_lu_t* lu, size_t nnz)
9{
10 size_t n = lu->n;
11 size_t initial_capacity = (nnz > 64) ? nnz : 64;
13 if (initial_capacity < n) {
14 initial_capacity = n;
15 }
16
17 lu->L_row_idx = (size_t*)lmmc_memory_alloc_array(initial_capacity, sizeof(size_t));
18 lu->L_values = (lmmc_real_t*)lmmc_memory_alloc_array(initial_capacity, sizeof(lmmc_real_t));
19 lu->U_row_idx = (size_t*)lmmc_memory_alloc_array(initial_capacity, sizeof(size_t));
20 lu->U_values = (lmmc_real_t*)lmmc_memory_alloc_array(initial_capacity, sizeof(lmmc_real_t));
21
22if (!lu->L_row_idx || !lu->L_values || !lu->U_row_idx || !lu->U_values) {
24 }
25 lu->L_capacity = initial_capacity;
26 lu->U_capacity = initial_capacity;
27 return LMMC_STATUS_OK;
28}
29
30static lmmc_status_t lu_allocate_indices(lmmc_sparse_lu_t* lu)
31{
32 size_t n = lu->n;
33 lu->col_perm = (size_t*)lmmc_memory_alloc_array(n, sizeof(size_t));
34 lu->row_perm = (size_t*)lmmc_memory_alloc_array(n, sizeof(size_t));
35 lu->L_col_ptr = (size_t*)lmmc_memory_alloc_array_plus(n, 1, sizeof(size_t));
36 lu->U_col_ptr = (size_t*)lmmc_memory_alloc_array_plus(n, 1, sizeof(size_t));
37
38if (!lu->col_perm || !lu->row_perm || !lu->L_col_ptr || !lu->U_col_ptr) {
40}
41 memset(lu->L_col_ptr, 0, (n + 1) * sizeof(size_t));
42 memset(lu->U_col_ptr, 0, (n + 1) * sizeof(size_t));
43
45 for (size_t i = 0; i < n; i++) lu->row_perm[i] = i;
46 return LMMC_STATUS_OK;
47}
48
50 const lmmc_sparse_mat_t* a,
51 lmmc_sparse_lu_t** out_lu)
52{
53 lmmc_sparse_lu_t* lu = NULL;
55 int csc_needs_free = 0;
56 size_t n;
57 lmmc_status_t status;
58
59 if (a == NULL || out_lu == NULL) {
61 }
62 if (a->rows == 0 || a->cols == 0) {
64 }
65 if (a->rows != a->cols) {
67 }
68
69 n = a->rows;
70
71 status = lmmc_sparse_direct_csc(a, &csc, &csc_needs_free);
72 if (status != LMMC_STATUS_OK) {
73 return status;
74 }
75
77 if (lu == NULL) {
78 if (csc_needs_free) {
80 }
82 }
83 memset(lu, 0, sizeof(lmmc_sparse_lu_t));
84 lu->n = n;
85status = lu_allocate_indices(lu);
86if (status == LMMC_STATUS_OK) {
87 status = lmmc_sparse_residual_reorder(&csc, lu->col_perm);
88}
89if (status == LMMC_STATUS_OK) {
90 status = lu_allocate_factors(lu, a->nnz);
91}
92if (status != LMMC_STATUS_OK) {
94 if (csc_needs_free) {
96 }
97 return status;
98}
99 lu->L_nnz = 0;
100 lu->U_nnz = 0;
101
102 if (csc_needs_free) {
104 }
105 *out_lu = lu;
106 return LMMC_STATUS_OK;
107}
108
109static void lu_scatter_column(const lmmc_sparse_mat_t* csc, size_t orig_col,
110 const size_t* piv_inv, lmmc_real_t* col_dense)
111{
112 memset(col_dense, 0, csc->rows * sizeof(lmmc_real_t));
113 size_t col_start = csc->row_ptr[orig_col];
114 size_t col_end = csc->row_ptr[orig_col + 1];
115 for (size_t p = col_start; p < col_end; p++) {
116 size_t row = csc->col_idx[p];
117 size_t prow = piv_inv[row];
118 col_dense[prow] += csc->values[p];
119 }
120}
121
122static void lu_update_column(const lmmc_sparse_lu_t* lu, size_t j,
123 lmmc_real_t* col_dense)
124{
126 for (size_t k = 0; k < j; k++) {
127 if (col_dense[k] == 0.0) {
128 continue;
129 }
130
131 lmmc_real_t ukj = col_dense[k];
132
134 size_t L_start = lu->L_col_ptr[k];
135 size_t L_end = lu->L_col_ptr[k + 1];
136 for (size_t p = L_start; p < L_end; p++) {
137 size_t row = lu->L_row_idx[p];
138 lmmc_real_t old_val = col_dense[row];
139 col_dense[row] = old_val - lu->L_values[p] * ukj;
140 }
141 }
142}
143
144static void lu_swap_previous_rows(lmmc_sparse_lu_t* lu, size_t j,
145 size_t pivot_row)
146{
148 for (size_t k = 0; k < j; k++) {
149 size_t L_start = lu->L_col_ptr[k];
150 size_t L_end = lu->L_col_ptr[k + 1];
151 size_t idx_j = (size_t)-1;
152 size_t idx_p = (size_t)-1;
153 for (size_t p = L_start; p < L_end; p++) {
154 if (lu->L_row_idx[p] == j) {
155 idx_j = p;
156 }
157 if (lu->L_row_idx[p] == pivot_row) {
158 idx_p = p;
159 }
160 }
161 if (idx_j != (size_t)-1 && idx_p != (size_t)-1) {
162 lmmc_real_t tmp_v = lu->L_values[idx_j];
163 lu->L_values[idx_j] = lu->L_values[idx_p];
164 lu->L_values[idx_p] = tmp_v;
165 } else if (idx_j != (size_t)-1) {
166 lu->L_row_idx[idx_j] = pivot_row;
167 } else if (idx_p != (size_t)-1) {
168 lu->L_row_idx[idx_p] = j;
169 }
170 }
171}
172
173static lmmc_status_t lu_pivot_column(lmmc_sparse_lu_t* lu, size_t j,
174 size_t* piv_inv, lmmc_real_t* col_dense)
175{
176 size_t n = lu->n;
177 size_t pivot_row = j;
178 lmmc_real_t max_val = lmmc_abs(col_dense[j]);
179
180 for (size_t i = j + 1; i < n; i++) {
181 lmmc_real_t av = lmmc_abs(col_dense[i]);
182 if (av > max_val) {
183 max_val = av;
184 pivot_row = i;
185 }
186 }
187
188 if (max_val == 0.0) {
190 }
191
192 if (pivot_row != j) {
193 lmmc_real_t tmp = col_dense[j];
194 col_dense[j] = col_dense[pivot_row];
195 col_dense[pivot_row] = tmp;
196
197 size_t orig_j = lu->row_perm[j];
198 size_t orig_p = lu->row_perm[pivot_row];
199 lu->row_perm[j] = orig_p;
200 lu->row_perm[pivot_row] = orig_j;
201 piv_inv[orig_p] = j;
202 piv_inv[orig_j] = pivot_row;
203
204lu_swap_previous_rows(lu, j, pivot_row);
205 }
206 return LMMC_STATUS_OK;
207}
208static lmmc_status_t lu_store_column(lmmc_sparse_lu_t* lu, size_t j,
209 const lmmc_real_t* col_dense)
210{
211 size_t n = lu->n;
212 lmmc_status_t status;
214 lu->U_col_ptr[j] = lu->U_nnz;
215 for (size_t i = 0; i <= j; i++) {
216 if (col_dense[i] != 0.0) {
218 &lu->U_capacity, lu->U_nnz);
219 if (status != LMMC_STATUS_OK) {
220 return status;
221 }
222 lu->U_row_idx[lu->U_nnz] = i;
223 lu->U_values[lu->U_nnz] = col_dense[i];
224 lu->U_nnz++;
225 }
226 }
227
229 {
230 lmmc_real_t diag = col_dense[j];
231 for (size_t i = j + 1; i < n; i++) {
232 if (col_dense[i] != 0.0) {
234 &lu->L_capacity, lu->L_nnz);
235 if (status != LMMC_STATUS_OK) {
236 return status;
237 }
238 lu->L_row_idx[lu->L_nnz] = i;
239 lu->L_values[lu->L_nnz] = col_dense[i] / diag;
240 lu->L_nnz++;
241 }
242 }
243 }
244 return LMMC_STATUS_OK;
245}
246
253static lmmc_status_t lu_factor_columns(const lmmc_sparse_mat_t* csc,
254 lmmc_sparse_lu_t* lu, lmmc_real_t* col_dense, size_t* piv_inv)
255{
256 size_t n = lu->n;
257 for (size_t i = 0; i < n; i++) {
258 piv_inv[i] = i;
259 }
260 lu->L_nnz = 0;
261 lu->U_nnz = 0;
262 for (size_t i = 0; i < n; i++) {
263 lu->row_perm[i] = i;
264 }
265 for (size_t j = 0; j < n; j++) {
267 lu->L_col_ptr[j] = lu->L_nnz;
268 lu_scatter_column(csc, lu->col_perm[j], piv_inv, col_dense);
269 lu_update_column(lu, j, col_dense);
270 lmmc_status_t status = lu_pivot_column(lu, j, piv_inv, col_dense);
271 if (status != LMMC_STATUS_OK) {
272 return status;
273 }
274 status = lu_store_column(lu, j, col_dense);
275 if (status != LMMC_STATUS_OK) {
276 return status;
277 }
278 }
279 lu->L_col_ptr[n] = lu->L_nnz;
280 lu->U_col_ptr[n] = lu->U_nnz;
281 return LMMC_STATUS_OK;
282}
283
285 const lmmc_sparse_mat_t* a,
287{
289 int csc_needs_free = 0;
291 size_t n;
292 lmmc_real_t* col_dense = NULL;
293 size_t* piv_inv = NULL;
294
295 if (a == NULL || lu == NULL) {
297 }
298 if (a->rows != a->cols) {
300 }
301 if (a->rows != lu->n) {
303 }
304
305 n = lu->n;
306
307 status = lmmc_sparse_direct_csc(a, &csc, &csc_needs_free);
308 if (status != LMMC_STATUS_OK) {
309 return status;
310 }
311
313 col_dense = (lmmc_real_t*)lmmc_memory_alloc_array(n, sizeof(lmmc_real_t));
314 piv_inv = (size_t*)lmmc_memory_alloc_array(n, sizeof(size_t));
315
316 if (!col_dense || !piv_inv) {
318 goto cleanup;
319 }
320
321 status = lu_factor_columns(&csc, lu, col_dense, piv_inv);
322
323cleanup:
324 if (col_dense) {
325 lmmc_memory_free(col_dense);
326 }
327 if (piv_inv) {
328 lmmc_memory_free(piv_inv);
329 }
330 if (csc_needs_free) {
332 }
333 return status;
334}
335
336static lmmc_status_t lu_triangular_solve(const lmmc_sparse_lu_t* lu,
337 lmmc_real_t* work)
338{
339 size_t n = lu->n;
341 for (size_t j = 0; j < n; j++) {
342 size_t L_start = lu->L_col_ptr[j];
343 size_t L_end = lu->L_col_ptr[j + 1];
344 for (size_t p = L_start; p < L_end; p++) {
345 size_t i = lu->L_row_idx[p];
346 work[i] -= lu->L_values[p] * work[j];
347 }
348 }
350 for (size_t jj = 0; jj < n; jj++) {
351 size_t j = n - 1 - jj;
352 size_t U_start = lu->U_col_ptr[j];
353 size_t U_end = lu->U_col_ptr[j + 1];
354 lmmc_real_t diag = 0.0;
355 for (size_t p = U_start; p < U_end; p++) {
356 if (lu->U_row_idx[p] == j) {
357 diag = lu->U_values[p];
358 break;
359 }
360 }
361 if (diag == 0.0) {
363 }
364 work[j] = work[j] / diag;
365 for (size_t p = U_start; p < U_end; p++) {
366 size_t i = lu->U_row_idx[p];
367 if (i != j) {
368 work[i] -= lu->U_values[p] * work[j];
369 }
370 }
371 }
372 return LMMC_STATUS_OK;
373}
374
381 const lmmc_sparse_lu_t* lu,
382 const lmmc_vec_t* b,
383 lmmc_vec_t* x)
384{
385 size_t n;
386 lmmc_real_t* work = NULL;
387
388 if (lu == NULL || b == NULL || x == NULL) {
390 }
391 if (b->data == NULL || x->data == NULL) {
393 }
394
395 n = lu->n;
396 if (b->size != n || x->size != n) {
398 }
399
401 if (work == NULL) {
403 }
404
406 for (size_t i = 0; i < n; i++) {
407 work[i] = b->data[lu->row_perm[i]];
408 }
409
410 lmmc_status_t status = lu_triangular_solve(lu, work);
411 if (status != LMMC_STATUS_OK) {
412 lmmc_memory_free(work);
413 return status;
414 }
415
418 for (size_t k = 0; k < n; k++) {
419 x->data[lu->col_perm[k]] = work[k];
420 }
421
422 lmmc_memory_free(work);
423 return LMMC_STATUS_OK;
424}
425
427{
428 if (lu == NULL) {
429 return;
430 }
431 if (lu->col_perm) {
433 }
434 if (lu->row_perm) {
436 }
437 if (lu->L_col_ptr) {
439 }
440 if (lu->U_col_ptr) {
442 }
443 if (lu->L_row_idx) {
445 }
446 if (lu->L_values) {
448 }
449 if (lu->U_row_idx) {
451 }
452 if (lu->U_values) {
454 }
456}
double lmmc_real_t
Public and internal LMMC real type.
lmmc_real_t * data
一维稠密向量。
LMMC 内部使用的公共工具(仅源文件可见).
void * lmmc_memory_alloc_array(size_t count, size_t element_size)
void * lmmc_memory_alloc(size_t size)
void * lmmc_memory_alloc_array_plus(size_t count, size_t extra, size_t element_size)
void lmmc_memory_free(void *pointer)
Private recoverable allocator for persistent LMMC ownership.
lmmc_status_t lmmc_sparse_factor_capacity(size_t **idx, lmmc_real_t **vals, size_t *capacity, size_t needed)
lmmc_status_t lmmc_sparse_direct_csc(const lmmc_sparse_mat_t *a, lmmc_sparse_mat_t *csc_out, int *needs_free)
lmmc_status_t lmmc_sparse_residual_reorder(const lmmc_sparse_mat_t *csc, size_t *perm)
稀疏 LU 分解上下文(不透明类型)。
lmmc_status_t lmmc_sparse_lu_solve(const lmmc_sparse_lu_t *lu, const lmmc_vec_t *b, lmmc_vec_t *x)
使用已分解的稀疏 LU 因子求解 A*x = b。
void lmmc_sparse_lu_destroy(lmmc_sparse_lu_t *lu)
销毁稀疏 LU 上下文。
lmmc_status_t lmmc_sparse_lu_numeric(const lmmc_sparse_mat_t *a, lmmc_sparse_lu_t *lu)
稀疏 LU 数值分解:稠密列工作区左看算法与部分主元法。
lmmc_status_t lmmc_sparse_lu_symbolic(const lmmc_sparse_mat_t *a, lmmc_sparse_lu_t **out_lu)
稀疏 LU 符号分析:去重简单图与贪心残余度重排序。
void lmmc_sparse_destroy(lmmc_sparse_mat_t *sparse)
销毁稀疏矩阵,释放其拥有的缓冲区并清零全部字段。
通用稀疏矩阵(CSR / CSC 共用同一结构)。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_DIMENSION_MISMATCH
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_SINGULAR_MATRIX
@ LMMC_STATUS_INVALID_ARGUMENT