LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
sparse_products.c
浏览该文件的文档.
1
5#include <string.h>
6#include <stdlib.h>
7
8#include "memory_bridge.h"
9#include "internal.h"
10#include "sparse_internal.h"
11#include "lmmc/sparse.h"
12
14 size_t i = 0, p = 0;
15 lmmc_status_t st = lmmc_sparse_validate(sparse);
16 if (st != LMMC_STATUS_OK || x == NULL || y == NULL || x->data == NULL || y->data == NULL) {
18 }
19 if (x->size != sparse->cols || y->size != sparse->rows) {
21 }
22
23 const lmmc_real_t* restrict vals = sparse->values;
24 const size_t* restrict col_idx = sparse->col_idx;
25 const size_t* restrict row_ptr = sparse->row_ptr;
26 const lmmc_real_t* restrict x_data = x->data;
27 lmmc_real_t* restrict y_data = y->data;
28
29 lmmc_real_t sum; LMMC_REAL_INIT(&sum);
30 lmmc_real_t tmp_mul; LMMC_REAL_INIT(&tmp_mul);
31 lmmc_real_t tmp_sum; LMMC_REAL_INIT(&tmp_sum);
32 lmmc_real_t zero; LMMC_REAL_INIT(&zero);
33 LMMC_REAL_SET_D(&zero, 0.0);
34
35 if (sparse->format == LMMC_SPARSE_CSR) {
36 for (i = 0; i < sparse->rows; ++i) {
37 LMMC_REAL_SET_D(&sum, 0.0);
38 size_t start = row_ptr[i];
39 size_t end = row_ptr[i + 1];
40
41 p = start;
42 for (; p + 3 < end; p += 4) {
43 LMMC_REAL_MUL(&tmp_mul, &vals[p], &x_data[col_idx[p]]);
44 LMMC_REAL_ADD(&tmp_sum, &sum, &tmp_mul);
45 LMMC_REAL_SET(&sum, &tmp_sum);
46
47 LMMC_REAL_MUL(&tmp_mul, &vals[p + 1], &x_data[col_idx[p + 1]]);
48 LMMC_REAL_ADD(&tmp_sum, &sum, &tmp_mul);
49 LMMC_REAL_SET(&sum, &tmp_sum);
50
51 LMMC_REAL_MUL(&tmp_mul, &vals[p + 2], &x_data[col_idx[p + 2]]);
52 LMMC_REAL_ADD(&tmp_sum, &sum, &tmp_mul);
53 LMMC_REAL_SET(&sum, &tmp_sum);
54
55 LMMC_REAL_MUL(&tmp_mul, &vals[p + 3], &x_data[col_idx[p + 3]]);
56 LMMC_REAL_ADD(&tmp_sum, &sum, &tmp_mul);
57 LMMC_REAL_SET(&sum, &tmp_sum);
58 }
59 for (; p < end; ++p) {
60 LMMC_REAL_MUL(&tmp_mul, &vals[p], &x_data[col_idx[p]]);
61 LMMC_REAL_ADD(&tmp_sum, &sum, &tmp_mul);
62 LMMC_REAL_SET(&sum, &tmp_sum);
63 }
64 LMMC_REAL_SET(&y_data[i], &sum);
65 }
66 } else {
67 st = lmmc_vec_fill(y, zero);
68 if (st != LMMC_STATUS_OK) {
69 LMMC_REAL_CLEAR(&sum);
70 LMMC_REAL_CLEAR(&tmp_mul);
71 LMMC_REAL_CLEAR(&tmp_sum);
72 LMMC_REAL_CLEAR(&zero);
73 return st;
74 }
75 for (i = 0; i < sparse->cols; ++i) {
76 size_t start = row_ptr[i];
77 size_t end = row_ptr[i + 1];
78 for (p = start; p < end; ++p) {
79 LMMC_REAL_MUL(&tmp_mul, &vals[p], &x_data[i]);
80 LMMC_REAL_ADD(&tmp_sum, &y_data[col_idx[p]], &tmp_mul);
81 LMMC_REAL_SET(&y_data[col_idx[p]], &tmp_sum);
82 }
83 }
84 }
85
86 LMMC_REAL_CLEAR(&sum);
87 LMMC_REAL_CLEAR(&tmp_mul);
88 LMMC_REAL_CLEAR(&tmp_sum);
89 LMMC_REAL_CLEAR(&zero);
90 return LMMC_STATUS_OK;
91}
92static void lmmc_sparse_dense_row_add(
93 const lmmc_real_t* value, const lmmc_real_t* b_row,
94 lmmc_real_t* c_row, size_t count,
95 lmmc_real_t* tmp_mul, lmmc_real_t* tmp_sum)
96{
97 size_t j;
98 size_t j_limit = count & ~((size_t)3);
99 for (j = 0; j < j_limit; j += 4) {
100 LMMC_REAL_MUL(tmp_mul, value, &b_row[j]);
101 LMMC_REAL_ADD(tmp_sum, &c_row[j], tmp_mul);
102 LMMC_REAL_SET(&c_row[j], tmp_sum);
103
104 LMMC_REAL_MUL(tmp_mul, value, &b_row[j + 1]);
105 LMMC_REAL_ADD(tmp_sum, &c_row[j + 1], tmp_mul);
106 LMMC_REAL_SET(&c_row[j + 1], tmp_sum);
107
108 LMMC_REAL_MUL(tmp_mul, value, &b_row[j + 2]);
109 LMMC_REAL_ADD(tmp_sum, &c_row[j + 2], tmp_mul);
110 LMMC_REAL_SET(&c_row[j + 2], tmp_sum);
111
112 LMMC_REAL_MUL(tmp_mul, value, &b_row[j + 3]);
113 LMMC_REAL_ADD(tmp_sum, &c_row[j + 3], tmp_mul);
114 LMMC_REAL_SET(&c_row[j + 3], tmp_sum);
115 }
116 for (; j < count; ++j) {
117 LMMC_REAL_MUL(tmp_mul, value, &b_row[j]);
118 LMMC_REAL_ADD(tmp_sum, &c_row[j], tmp_mul);
119 LMMC_REAL_SET(&c_row[j], tmp_sum);
120 }
121}
122
123static size_t lmmc_sparse_product_pattern(
124 const lmmc_sparse_mat_t* pa, const lmmc_sparse_mat_t* pb,
125 size_t* marker, size_t* c_row_ptr)
126{
127 size_t i, j, k, p1, p2;
128 size_t nnz_est;
129 nnz_est = 0;
130 for (i = 0; i < pa->rows; ++i) {
131 for (p1 = pa->row_ptr[i]; p1 < pa->row_ptr[i + 1]; ++p1) {
132 k = pa->col_idx[p1];
133 for (p2 = pb->row_ptr[k]; p2 < pb->row_ptr[k + 1]; ++p2) {
134 j = pb->col_idx[p2];
135 if (marker[j] != i) {
136 marker[j] = i;
137 nnz_est++;
138 }
139 }
140 }
141 c_row_ptr[i + 1] = nnz_est;
142 }
143 return nnz_est;
144}
145
146static int lmmc_sparse_index_compare(const void* lhs, const void* rhs) {
147 const size_t a = *(const size_t*)lhs;
148 const size_t b = *(const size_t*)rhs;
149 return (a > b) - (a < b);
150}
151
152static void lmmc_sparse_product_values(
153 const lmmc_sparse_mat_t* pa, const lmmc_sparse_mat_t* pb,
154 size_t* marker, lmmc_real_t* accumulator,
155 size_t* c_col_idx, lmmc_real_t* c_values)
156{
157 size_t i, j, k, p1, p2;
158 lmmc_real_t tmp_mul; LMMC_REAL_INIT(&tmp_mul);
159 lmmc_real_t tmp_sum; LMMC_REAL_INIT(&tmp_sum);
160
161 size_t current_nnz = 0;
162 for (i = 0; i < pa->rows; ++i) {
163 size_t row_start = current_nnz;
164 for (p1 = pa->row_ptr[i]; p1 < pa->row_ptr[i + 1]; ++p1) {
165 k = pa->col_idx[p1];
166 for (p2 = pb->row_ptr[k]; p2 < pb->row_ptr[k + 1]; ++p2) {
167 j = pb->col_idx[p2];
168 if (marker[j] != i) {
169 marker[j] = i;
170 c_col_idx[current_nnz++] = j;
171 }
172 LMMC_REAL_MUL(&tmp_mul, &pa->values[p1], &pb->values[p2]);
173 LMMC_REAL_ADD(&tmp_sum, &accumulator[j], &tmp_mul);
174 LMMC_REAL_SET(&accumulator[j], &tmp_sum);
175 }
176 }
177 if (current_nnz - row_start > 1) {
178 qsort(c_col_idx + row_start, current_nnz - row_start,
179 sizeof(size_t), lmmc_sparse_index_compare);
180 }
181 for (p1 = row_start; p1 < current_nnz; ++p1) {
182 j = c_col_idx[p1];
183 LMMC_REAL_SET(&c_values[p1], &accumulator[j]);
184 LMMC_REAL_SET_D(&accumulator[j], 0.0);
185 }
186 }
187
188 LMMC_REAL_CLEAR(&tmp_mul);
189 LMMC_REAL_CLEAR(&tmp_sum);
190}
191
192static void lmmc_sparse_product_workspace_clear(
193 size_t* marker, lmmc_real_t* accumulator, size_t cols,
194 size_t* c_row_ptr, size_t* c_col_idx, lmmc_real_t* c_values, size_t nnz_est)
195{
196 if (marker) {
197 lmmc_memory_free(marker);
198 }
199 if (accumulator) {
200 for (size_t act_i = 0; act_i < cols; act_i++) {
201 LMMC_REAL_CLEAR(&accumulator[act_i]);
202 }
203 lmmc_memory_free(accumulator);
204 }
205 if (c_row_ptr) {
206 lmmc_memory_free(c_row_ptr);
207 }
208 if (c_col_idx) {
209 lmmc_memory_free(c_col_idx);
210 }
211 if (c_values) {
212 for (size_t act_i = 0; act_i < nnz_est; act_i++) {
213 LMMC_REAL_CLEAR(&c_values[act_i]);
214 }
215 lmmc_memory_free(c_values);
216 }
217}
218
219
221 size_t i = 0, p = 0;
222 lmmc_status_t st = lmmc_sparse_validate(sparse);
223 if (st != LMMC_STATUS_OK || b == NULL || c == NULL || b->data == NULL || c->data == NULL) {
225 }
226 if (sparse->cols != b->rows || c->rows != sparse->rows || c->cols != b->cols) {
228 }
229
230 const lmmc_real_t* restrict vals = sparse->values;
231 const size_t* restrict col_idx = sparse->col_idx;
232 const size_t* restrict row_ptr = sparse->row_ptr;
233 const lmmc_real_t* restrict b_data = b->data;
234 lmmc_real_t* restrict c_data = c->data;
235
236 size_t b_stride = b->stride;
237 size_t c_stride = c->stride;
238 size_t b_cols = b->cols;
239
240 lmmc_real_t zero; LMMC_REAL_INIT(&zero);
241 lmmc_real_t tmp_mul; LMMC_REAL_INIT(&tmp_mul);
242 lmmc_real_t tmp_sum; LMMC_REAL_INIT(&tmp_sum);
243 LMMC_REAL_SET_D(&zero, 0.0);
244
245 st = lmmc_mat_fill(c, zero);
246 if (st != LMMC_STATUS_OK) {
247 LMMC_REAL_CLEAR(&zero);
248 LMMC_REAL_CLEAR(&tmp_mul);
249 LMMC_REAL_CLEAR(&tmp_sum);
250 return st;
251 }
252
253 if (sparse->format == LMMC_SPARSE_CSR) {
254 for (i = 0; i < sparse->rows; ++i) {
255 size_t start = row_ptr[i];
256 size_t end = row_ptr[i + 1];
257 for (p = start; p < end; ++p) {
258 size_t k = col_idx[p];
259 lmmc_real_t val_p; LMMC_REAL_INIT(&val_p);
260 LMMC_REAL_SET(&val_p, &vals[p]);
261
262 lmmc_sparse_dense_row_add(&val_p, &b_data[k * b_stride],
263 &c_data[i * c_stride], b_cols, &tmp_mul, &tmp_sum);
264 LMMC_REAL_CLEAR(&val_p);
265 }
266 }
267 } else {
268 for (i = 0; i < sparse->cols; ++i) {
269 size_t start = row_ptr[i];
270 size_t end = row_ptr[i + 1];
271 for (p = start; p < end; ++p) {
272 size_t row = col_idx[p];
273 lmmc_real_t val_p; LMMC_REAL_INIT(&val_p);
274 LMMC_REAL_SET(&val_p, &vals[p]);
275
276 lmmc_sparse_dense_row_add(&val_p, &b_data[i * b_stride],
277 &c_data[row * c_stride], b_cols, &tmp_mul, &tmp_sum);
278 LMMC_REAL_CLEAR(&val_p);
279 }
280 }
281 }
282
283 LMMC_REAL_CLEAR(&zero);
284 LMMC_REAL_CLEAR(&tmp_mul);
285 LMMC_REAL_CLEAR(&tmp_sum);
286 return LMMC_STATUS_OK;
287}
288
289static lmmc_status_t lmmc_sparse_product_numeric_workspace(
290 size_t nnz, size_t cols, size_t* marker, size_t** indices,
291 lmmc_real_t** values, lmmc_real_t** accumulator)
292{
293 *indices = (size_t*)lmmc_memory_alloc_array(nnz, sizeof(size_t));
294 *values = (lmmc_real_t*)lmmc_memory_alloc_array(nnz, sizeof(lmmc_real_t));
295 *accumulator = (lmmc_real_t*)lmmc_memory_alloc_array(cols, sizeof(lmmc_real_t));
296 if ((nnz > 0 && (*indices == NULL || *values == NULL)) || *accumulator == NULL) {
298 }
299 memset(marker, 0xFF, cols * sizeof(size_t));
300 for (size_t i = 0; i < cols; ++i) {
301 LMMC_REAL_INIT(&(*accumulator)[i]);
302 LMMC_REAL_SET_D(&(*accumulator)[i], 0.0);
303 }
304 for (size_t i = 0; i < nnz; ++i) {
305 LMMC_REAL_INIT(&(*values)[i]);
306 }
307 return LMMC_STATUS_OK;
308}
309
311 lmmc_sparse_mat_t a_csr = {0};
312 lmmc_sparse_mat_t b_csr = {0};
313 const lmmc_sparse_mat_t *pa = a;
314 const lmmc_sparse_mat_t *pb = b;
316 size_t *marker = NULL;
317 lmmc_real_t *accumulator = NULL;
318 size_t *c_row_ptr = NULL;
319 size_t *c_col_idx = NULL;
320 lmmc_real_t *c_values = NULL;
321 size_t nnz_est = 0;
322
323 if (c == NULL || lmmc_sparse_validate(a) != LMMC_STATUS_OK ||
324 lmmc_sparse_validate(b) != LMMC_STATUS_OK) {
326 }
327 if (a->cols != b->rows) {
329 }
330
331 if (a->format == LMMC_SPARSE_CSC) {
332 st = lmmc_sparse_to_csr(a, &a_csr);
333 if (st != LMMC_STATUS_OK) {
334 return st;
335 }
336 pa = &a_csr;
337 }
338 if (b->format == LMMC_SPARSE_CSC) {
339 st = lmmc_sparse_to_csr(b, &b_csr);
340 if (st != LMMC_STATUS_OK) {
341 lmmc_sparse_destroy(&a_csr);
342 return st;
343 }
344 pb = &b_csr;
345 }
346
347 marker = (size_t*)lmmc_memory_alloc_array(pb->cols, sizeof(size_t));
348 c_row_ptr = (size_t*)lmmc_memory_alloc_array_plus(pa->rows, 1, sizeof(size_t));
349 if (marker == NULL || c_row_ptr == NULL) {
351 goto cleanup;
352 }
353 memset(marker, 0xFF, pb->cols * sizeof(size_t));
354 memset(c_row_ptr, 0, (pa->rows + 1) * sizeof(size_t));
355
356 nnz_est = lmmc_sparse_product_pattern(pa, pb, marker, c_row_ptr);
357
358 st = lmmc_sparse_product_numeric_workspace(
359 nnz_est, pb->cols, marker, &c_col_idx, &c_values, &accumulator);
360 if (st != LMMC_STATUS_OK) {
361 goto cleanup;
362 }
363
364 lmmc_sparse_product_values(pa, pb, marker, accumulator, c_col_idx, c_values);
365
366 c->rows = pa->rows;
367 c->cols = pb->cols;
368 c->nnz = nnz_est;
369 c->row_ptr = c_row_ptr;
370 c->col_idx = c_col_idx;
371 c->values = c_values;
373 c->owns_data = 1;
374
375 c_row_ptr = NULL;
376 c_col_idx = NULL;
377 c_values = NULL;
378
379cleanup:
380 lmmc_sparse_product_workspace_clear(
381 marker, accumulator, pb->cols, c_row_ptr, c_col_idx, c_values, nnz_est);
382 lmmc_sparse_destroy(&a_csr);
383 lmmc_sparse_destroy(&b_csr);
384 return st;
385}
#define LMMC_REAL_CLEAR(x)
释放一个 lmmc_real_t 变量(POD 类型为空操作)。
#define LMMC_REAL_INIT(x)
初始化一个 lmmc_real_t 变量(POD 类型为空操作)。
#define LMMC_REAL_ADD(res, a, b)
计算 *res = *a + *b 。
double lmmc_real_t
Public and internal LMMC real type.
#define LMMC_REAL_SET(dst, src)
将一个 lmmc_real_t 赋值到另一个:*dst = *src 。
#define LMMC_REAL_MUL(res, a, b)
计算 *res = *a * *b 。
#define LMMC_REAL_SET_D(dst, dbl)
将一个 double 字面量赋值到 lmmc_real_t 。
lmmc_status_t lmmc_vec_fill(lmmc_vec_t *vec, lmmc_real_t value)
就地将向量所有元素设为 value。
lmmc_status_t lmmc_mat_fill(lmmc_mat_t *mat, lmmc_real_t value)
就地将矩阵所有元素设为 value。
lmmc_real_t * data
lmmc_real_t * data
行优先存储的稠密矩阵。
一维稠密向量。
LMMC 内部使用的公共工具(仅源文件可见).
void * lmmc_memory_alloc_array(size_t count, size_t element_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_mat_mat_mul_sparse(const lmmc_sparse_mat_t *a, const lmmc_sparse_mat_t *b, lmmc_sparse_mat_t *c)
稀疏矩阵-稀疏矩阵乘法(SpGEMM):C = A * B。
lmmc_status_t lmmc_sparse_mat_vec_mul(const lmmc_sparse_mat_t *sparse, const lmmc_vec_t *x, lmmc_vec_t *y)
稀疏矩阵-稠密向量乘法:y = A * x。
lmmc_status_t lmmc_sparse_mat_mat_mul_dense(const lmmc_sparse_mat_t *sparse, const lmmc_mat_t *b, lmmc_mat_t *c)
稀疏矩阵 x 稠密矩阵:C = A * B(结果为稠密矩阵)。
稀疏矩阵模块内部共享辅助函数(仅源文件可见)。
lmmc_status_t lmmc_sparse_to_csr(const lmmc_sparse_mat_t *src, lmmc_sparse_mat_t *dst)
转换为 CSR,src 已为 CSR 时执行深拷贝。
void lmmc_sparse_destroy(lmmc_sparse_mat_t *sparse)
销毁稀疏矩阵,释放其拥有的缓冲区并清零全部字段。
@ LMMC_SPARSE_CSR
@ LMMC_SPARSE_CSC
lmmc_sparse_format_t format
通用稀疏矩阵(CSR / CSC 共用同一结构)。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_DIMENSION_MISMATCH
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_INVALID_ARGUMENT