Skip to content

Commit 299a080

Browse files
committed
split up stacked pd into two files
1 parent ddca2c2 commit 299a080

7 files changed

Lines changed: 856 additions & 695 deletions

File tree

‎include/utils/stacked_pd.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ matrix *new_stacked_pd_unchecked(int m, int n, int n_blocks, permuted_dense **bl
8585
and the BA_spd_*_alloc kernels in stacked_pd_linalg.c. */
8686
typedef matrix *(*spd_block_op)(permuted_dense *Bk, const void *ctx);
8787

88-
matrix *spd_map_filter_blocks(const stacked_pd *B, int Cm, int Cn,
89-
spd_block_op op, const void *ctx);
88+
matrix *spd_map_filter_blocks(const stacked_pd *B, int Cm, int Cn, spd_block_op op,
89+
const void *ctx);
9090

9191
#endif /* STACKED_PD_H */

‎include/utils/stacked_pd_linalg.h‎

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2026 Daniel Cederberg and William Zhang
3+
*
4+
* This file is part of the SparseDiffEngine project.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
#ifndef STACKED_PD_LINALG_H
19+
#define STACKED_PD_LINALG_H
20+
21+
#include "CSC_matrix.h"
22+
#include "permuted_dense.h"
23+
#include "stacked_pd.h"
24+
25+
/* Allocate C = B @ A where B is spd, A is CSC. Result blocks are produced
26+
per source block via BA_pd_csc_alloc; blocks whose result has n0 == 0
27+
(no column of A intersects that block's col_perm) are dropped.
28+
C->n_blocks may be less than B->n_blocks; the src_block_idx_* map
29+
records the surviving source indices (exactly one source per output
30+
block). */
31+
matrix *BA_spd_csc_alloc(const stacked_pd *B, const CSC_matrix *A);
32+
33+
/* Fill values of C = B @ A. The caller must have produced C via
34+
BA_spd_csc_alloc and must keep A's CSC structure (sparsity) identical
35+
between alloc and this call. */
36+
void BA_spd_csc_fill_values(const stacked_pd *B, const CSC_matrix *A, stacked_pd *C);
37+
38+
/* Coalesce: take an spd whose blocks may have overlapping row perms but
39+
pairwise-disjoint cells, and produce an equivalent spd that satisfies
40+
the disjoint-row invariant. The output is the tightest dense
41+
representation — no structural zeros inside any output block —
42+
built by grouping rows by signature (the set of source blocks
43+
containing each row). Each unique signature becomes one output block
44+
whose row_perm is its rows and whose col_perm is the sorted union of
45+
the source col_perms in the signature. Output blocks are ordered by
46+
their minimum row index.
47+
48+
Precondition (debug-asserted): for any two source blocks sharing a
49+
row, their col_perms must be disjoint. The transpose use case
50+
(step 3) satisfies this automatically. */
51+
matrix *coalesce_spd_alloc(const stacked_pd *src);
52+
53+
/* Fill values of out = coalesce(src). The caller must have produced
54+
`out` via coalesce_spd_alloc on a `src` with the same structure
55+
(block count, row_perms, col_perms) as the one passed now. */
56+
void coalesce_spd_fill_values(const stacked_pd *src, stacked_pd *out);
57+
58+
/* Allocate a new spd C with the same sparsity as A.
59+
TODO: should this be publically available? */
60+
matrix *copy_sparsity_spd_alloc(const stacked_pd *A);
61+
62+
/* Fill values of C = diag(d) @ A, where 'd' is 'global' with length A->m. */
63+
void DA_spd_fill_values(const double *d, const stacked_pd *A, stacked_pd *C);
64+
65+
/* Allocate sparsity for C = B @ A where B is a stacked_pd and A is a
66+
permuted_dense. Per source block, computes B_k @ A via BA_pd_pd_alloc;
67+
B-blocks whose contribution is structurally empty (n0 == 0) are
68+
dropped; src_block_idx_* records the surviving source indices (one
69+
source per output block). Output is a stacked_pd: row_perms inherited
70+
from B (pairwise disjoint by the spd invariant). */
71+
matrix *BA_spd_pd_alloc(const stacked_pd *B, const permuted_dense *A);
72+
73+
/* Fill values of C = B @ A. Caller must have produced C via
74+
BA_spd_pd_alloc with the same structural inputs. Per surviving
75+
output block, delegates to BA_pd_pd_fill_values. */
76+
void BA_spd_pd_fill_values(const stacked_pd *B, const permuted_dense *A,
77+
stacked_pd *C);
78+
79+
/* Allocate sparsity for C = B @ A where B is a permuted_dense and A is
80+
a stacked_pd. Output is a single permuted_dense: row_perm =
81+
B->row_perm, col_perm = sorted union of A's block col_perms over
82+
those A-blocks whose row_perm intersects B->col_perm. If no A-block
83+
contributes, the output has n0 = 0. */
84+
matrix *BA_pd_spd_alloc(const permuted_dense *B, const stacked_pd *A);
85+
86+
/* Fill values of C = B @ A. Caller must have produced C via
87+
BA_pd_spd_alloc on the same B, A structure. Per A-block, this does a
88+
small gather + dgemm and scatters the result into C with += semantics
89+
on overlapping output cols. */
90+
void BA_pd_spd_fill_values(const permuted_dense *B, const stacked_pd *A,
91+
permuted_dense *C);
92+
93+
/* Allocate sparsity for C = B @ A where both B and A are stacked_pds.
94+
For each B-block, computes its contribution as a single PD via
95+
BA_pd_spd_alloc. B-blocks whose contribution is empty (n0 == 0) are
96+
dropped; src_block_idx_* records the surviving source indices (one
97+
source per output block). Output row_perms inherit from B and are
98+
pairwise disjoint by the spd invariant on B. */
99+
matrix *BA_spd_spd_alloc(const stacked_pd *B, const stacked_pd *A);
100+
101+
/* Fill values of C = B @ A. */
102+
void BA_spd_spd_fill_values(const stacked_pd *B, const stacked_pd *A, stacked_pd *C);
103+
104+
/* Allocate sparsity for C = A^T A. A^T A decomposes as Σ_k B_k^T B_k,
105+
where summands with overlapping col_perms (C_k) share cells. The
106+
output groups cols of A by signature sig_C(c) = {k : c ∈ C_k}; each
107+
unique signature becomes one output PD with row_perm = group cols
108+
and col_perm = ⋃ C_k for k in the signature. No structural zeros.
109+
The output's `work` slot holds per-source scratch PDs (one symmetric
110+
PD per source block, row_perm = col_perm = C_k) that
111+
ATDA_spd_fill_values writes into. */
112+
matrix *ATA_spd_alloc(const stacked_pd *A);
113+
114+
/* Fill values of C = A^T diag(d) A. */
115+
void ATDA_spd_fill_values(const stacked_pd *A, const double *d, stacked_pd *C);
116+
117+
/* Allocate out = transpose(src). Implementation: transpose each source
118+
PD block individually (yielding a raw spd that may have overlapping
119+
row perms but pairwise-disjoint cells, by the spd invariant), then
120+
coalesce. The raw spd is stored on `out->work` for reuse by
121+
`transpose_spd_fill_values`. */
122+
matrix *transpose_spd_alloc(const stacked_pd *src);
123+
124+
/* Fill values of out = transpose(src). The caller must have produced
125+
`out` via transpose_spd_alloc on a `src` with the same structure
126+
(block count, row_perms, col_perms) as the one passed now. */
127+
void transpose_spd_fill_values(const stacked_pd *src, stacked_pd *out);
128+
129+
#endif /* STACKED_PD_LINALG_H */

‎src/utils/matrix_BTA.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "utils/permuted_dense.h"
1717
#include "utils/sparse_matrix.h"
1818
#include "utils/stacked_pd.h"
19+
#include "utils/stacked_pd_linalg.h"
1920
#include "utils/tracked_alloc.h"
2021

2122
/* Effective-type view of a BTA/BTDA dispatcher operand. Exactly one of

0 commit comments

Comments
 (0)