LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
sparse_reordering.c
浏览该文件的文档.
1#include <string.h>
2#include <math.h>
3#include "memory_bridge.h"
4#include "internal.h"
5#include "lmmc/sparse.h"
7
8
9static void degree_bucket_remove(size_t node, size_t degree,
10 size_t* heads, size_t* next, size_t* prev)
11{
12 if (prev[node] != SIZE_MAX) {
13 next[prev[node]] = next[node];
14 } else {
15 heads[degree] = next[node];
16 }
17 if (next[node] != SIZE_MAX) {
18 prev[next[node]] = prev[node];
19 }
20 next[node] = SIZE_MAX;
21 prev[node] = SIZE_MAX;
22}
23
24static void degree_bucket_insert(size_t node, size_t degree,
25 size_t* heads, size_t* next, size_t* prev)
26{
27 next[node] = heads[degree];
28 prev[node] = SIZE_MAX;
29 if (heads[degree] != SIZE_MAX) {
30 prev[heads[degree]] = node;
31 }
32 heads[degree] = node;
33}
34
40typedef struct {
41 size_t* degree;
42 size_t* heads;
43 size_t* next;
44 size_t* prev;
45 unsigned char* eliminated;
47
48static void degree_workspace_destroy(lmmc_sparse_degree_workspace_t* work)
49{
50 if (work->degree) {
52 }
53 if (work->heads) {
55 }
56 if (work->next) {
58 }
59 if (work->prev) {
61 }
62 if (work->eliminated) {
64 }
65}
66
67static lmmc_status_t degree_workspace_create(size_t n, lmmc_sparse_degree_workspace_t* work)
68{
69 size_t bytes;
70 if (!lmmc_safe_mul_size(n, sizeof(size_t), &bytes)) {
72 }
73 work->degree = (size_t*)lmmc_memory_alloc(bytes);
74 work->heads = (size_t*)lmmc_memory_alloc(bytes);
75 work->next = (size_t*)lmmc_memory_alloc(bytes);
76 work->prev = (size_t*)lmmc_memory_alloc(bytes);
77 work->eliminated = (unsigned char*)lmmc_memory_alloc(n);
78 if (!work->degree || !work->heads || !work->next ||
79 !work->prev || !work->eliminated) {
80 degree_workspace_destroy(work);
82 }
83 return LMMC_STATUS_OK;
84}
85static size_t eliminate_neighbors(size_t node, const size_t* col_ptr,
86 const size_t* row_idx, lmmc_sparse_degree_workspace_t* work, size_t current_min)
87{
88 for (size_t p = col_ptr[node]; p < col_ptr[node + 1]; ++p) {
89 const size_t neighbor = row_idx[p];
90 if (work->eliminated[neighbor]) {
91 continue;
92 }
93 size_t old_degree = work->degree[neighbor];
94 degree_bucket_remove(neighbor, old_degree, work->heads, work->next, work->prev);
95 work->degree[neighbor] = old_degree - 1;
96 degree_bucket_insert(neighbor, work->degree[neighbor],
97 work->heads, work->next, work->prev);
98 if (work->degree[neighbor] < current_min) {
99 current_min = work->degree[neighbor];
100 }
101 }
102 return current_min;
103}
104
105static lmmc_status_t reorder_buckets(size_t n, const size_t* col_ptr,
106 const size_t* row_idx, size_t* perm, lmmc_sparse_degree_workspace_t* work)
107{
108 size_t current_min = SIZE_MAX;
109 memset(work->eliminated, 0, n);
110 for (size_t i = 0; i < n; ++i) {
111 work->heads[i] = SIZE_MAX;
112 work->next[i] = SIZE_MAX;
113 work->prev[i] = SIZE_MAX;
114 }
115 for (size_t i = 0; i < n; ++i) {
116 work->degree[i] = col_ptr[i + 1] - col_ptr[i];
117 degree_bucket_insert(i, work->degree[i], work->heads, work->next, work->prev);
118 if (current_min == SIZE_MAX || work->degree[i] < current_min) {
119 current_min = work->degree[i];
120 }
121 }
122
123 for (size_t step = 0; step < n; ++step) {
124 size_t node;
125 size_t candidate;
126 while (current_min < n && work->heads[current_min] == SIZE_MAX) {
127 ++current_min;
128 }
129 if (current_min == n) {
131 }
132
133 node = work->heads[current_min];
134 for (candidate = work->next[node]; candidate != SIZE_MAX; candidate = work->next[candidate]) {
135 if (candidate < node) {
136 node = candidate;
137 }
138 }
139 degree_bucket_remove(node, work->degree[node], work->heads, work->next, work->prev);
140 work->eliminated[node] = 1;
141 perm[step] = node;
142
143 current_min = eliminate_neighbors(node, col_ptr, row_idx, work, current_min);
144 }
145 return LMMC_STATUS_OK;
146}
147
148static lmmc_status_t bucketed_greedy_residual_degree_reorder(
149 size_t n,
150 const size_t* col_ptr,
151 const size_t* row_idx,
152 size_t* perm)
153{
154 if (n == 0) {
155 return LMMC_STATUS_OK;
156 }
157 if (!col_ptr || !perm) {
159 }
160 if (col_ptr[n] == 0) {
161 for (size_t i = 0; i < n; ++i) perm[i] = i;
162 return LMMC_STATUS_OK;
163 }
164 if (!row_idx) {
166 }
168 lmmc_status_t status = degree_workspace_create(n, &work);
169 if (status != LMMC_STATUS_OK) {
170 return status;
171 }
172 status = reorder_buckets(n, col_ptr, row_idx, perm, &work);
173 degree_workspace_destroy(&work);
174 return status;
175}
176static lmmc_status_t count_graph_edges(size_t n, const size_t* col_ptr,
177 const size_t* row_idx, size_t* graph_col_ptr)
178{
179 for (size_t column = 0; column < n; ++column) {
180 for (size_t p = col_ptr[column]; p < col_ptr[column + 1]; ++p) {
181 const size_t row = row_idx[p];
182 if (row >= n) {
184 }
185 if (row == column) {
186 continue;
187 }
188 if (graph_col_ptr[column + 1] == SIZE_MAX ||
189 graph_col_ptr[row + 1] == SIZE_MAX) {
191 }
192 ++graph_col_ptr[column + 1];
193 ++graph_col_ptr[row + 1];
194 }
195 }
196 for (size_t column = 0; column < n; ++column) {
197 if (!lmmc_safe_add_size(graph_col_ptr[column],
198 graph_col_ptr[column + 1],
199 &graph_col_ptr[column + 1])) {
201 }
202 }
203 return LMMC_STATUS_OK;
204}
205
206static void fill_graph_edges(size_t n, const size_t* col_ptr,
207 const size_t* row_idx, const size_t* graph_col_ptr,
208 size_t* graph_row_idx, size_t* cursor)
209{
210 memcpy(cursor, graph_col_ptr, n * sizeof(size_t));
211 for (size_t column = 0; column < n; ++column) {
212 for (size_t p = col_ptr[column]; p < col_ptr[column + 1]; ++p) {
213 const size_t row = row_idx[p];
214 if (row == column) {
215 continue;
216 }
217 graph_row_idx[cursor[column]++] = row;
218 graph_row_idx[cursor[row]++] = column;
219 }
220 }
221}
222
223static void compact_graph_edges(size_t n, size_t* graph_col_ptr,
224 size_t* graph_row_idx, size_t* stamp)
225{
226 size_t read_begin = 0;
227 size_t write = 0;
228 for (size_t i = 0; i < n; ++i) stamp[i] = SIZE_MAX;
229 for (size_t column = 0; column < n; ++column) {
230 const size_t read_end = graph_col_ptr[column + 1];
231 graph_col_ptr[column] = write;
232 for (size_t p = read_begin; p < read_end; ++p) {
233 const size_t row = graph_row_idx[p];
234 if (stamp[row] != column) {
235 stamp[row] = column;
236 graph_row_idx[write++] = row;
237 }
238 }
239 read_begin = read_end;
240 }
241 graph_col_ptr[n] = write;
242}
243
244static lmmc_status_t allocate_graph_indices(size_t n, size_t** graph_col_ptr,
245 size_t** cursor, size_t** stamp)
246{
247 size_t pointer_count, pointer_bytes, index_bytes;
248 if (!lmmc_safe_add_size(n, 1, &pointer_count) ||
249 !lmmc_safe_mul_size(pointer_count, sizeof(size_t), &pointer_bytes)) {
251 }
252 if (!lmmc_safe_mul_size(n, sizeof(size_t), &index_bytes)) {
254 }
255 *graph_col_ptr = (size_t*)lmmc_memory_alloc(pointer_bytes);
256 *cursor = (size_t*)lmmc_memory_alloc(index_bytes);
257 *stamp = (size_t*)lmmc_memory_alloc(index_bytes);
258 if (!*graph_col_ptr || (n != 0 && (!*cursor || !*stamp))) {
259 lmmc_memory_free(*graph_col_ptr);
260 lmmc_memory_free(*cursor);
261 lmmc_memory_free(*stamp);
263 }
264 memset(*graph_col_ptr, 0, pointer_bytes);
265 return LMMC_STATUS_OK;
266}
267
272static lmmc_status_t build_symmetric_simple_graph(
273 size_t n,
274 const size_t* col_ptr,
275 const size_t* row_idx,
276 size_t** out_graph_col_ptr,
277 size_t** out_graph_row_idx)
278{
279 lmmc_status_t status;
280 size_t index_bytes;
281 size_t* graph_col_ptr = NULL;
282 size_t* graph_row_idx = NULL;
283 size_t* cursor = NULL;
284 size_t* stamp = NULL;
285 size_t raw_nnz;
286 status = allocate_graph_indices(n, &graph_col_ptr, &cursor, &stamp);
287 if (status != LMMC_STATUS_OK) {
288 return status;
289 }
290status = count_graph_edges(n, col_ptr, row_idx, graph_col_ptr);
291 if (status != LMMC_STATUS_OK) {
292 goto cleanup;
293 }
294 raw_nnz = graph_col_ptr[n];
295 if (raw_nnz != 0) {
297 if (!lmmc_safe_mul_size(raw_nnz, sizeof(size_t), &index_bytes)) {
298 goto cleanup;
299 }
300 graph_row_idx = (size_t*)lmmc_memory_alloc(index_bytes);
301 if (!graph_row_idx) {
302 goto cleanup;
303 }
304 fill_graph_edges(n, col_ptr, row_idx, graph_col_ptr, graph_row_idx, cursor);
305 compact_graph_edges(n, graph_col_ptr, graph_row_idx, stamp);
306 }
307 *out_graph_col_ptr = graph_col_ptr;
308 *out_graph_row_idx = graph_row_idx;
309 status = LMMC_STATUS_OK;
310cleanup:
311 lmmc_memory_free(cursor);
312 lmmc_memory_free(stamp);
313 if (status != LMMC_STATUS_OK) {
314 lmmc_memory_free(graph_col_ptr);
315 if (graph_row_idx) {
316 lmmc_memory_free(graph_row_idx);
317 }
318 }
319 return status;
320}
321
323 size_t* perm)
324{
325 size_t* sym_col_ptr = NULL;
326 size_t* sym_row_idx = NULL;
327 lmmc_status_t status = build_symmetric_simple_graph(
328 csc->rows, csc->row_ptr, csc->col_idx, &sym_col_ptr, &sym_row_idx);
329 if (status != LMMC_STATUS_OK) {
330 return status;
331 }
332 status = bucketed_greedy_residual_degree_reorder(
333 csc->rows, sym_col_ptr, sym_row_idx, perm);
334 lmmc_memory_free(sym_col_ptr);
335 if (sym_row_idx) {
336 lmmc_memory_free(sym_row_idx);
337 }
338 return status;
339}
LMMC 内部使用的公共工具(仅源文件可见).
void * lmmc_memory_alloc(size_t size)
void lmmc_memory_free(void *pointer)
Private recoverable allocator for persistent LMMC ownership.
lmmc_status_t lmmc_sparse_residual_reorder(const lmmc_sparse_mat_t *csc, size_t *perm)
按当前残余度贪心选择顶点,度数桶支持常数时间更新。 同度数时选择原始索引最小的顶点,保证结果确定。消去时仅删除残余关联边, 不模拟填充边,区别于近似最小度算法。
通用稀疏矩阵(CSR / CSC 共用同一结构)。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_INVALID_ARGUMENT