Skip to content

Commit cdfecf9

Browse files
committed
unify code in stacked_pd
1 parent 7706df1 commit cdfecf9

6 files changed

Lines changed: 104 additions & 81 deletions

File tree

‎include/utils/permuted_dense_linalg.h‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,14 @@ matrix *BTA_csc_pd_alloc(const CSC_matrix *B, const permuted_dense *A);
8181
void BTDA_csc_pd_fill_values(const CSC_matrix *B, const double *d,
8282
const permuted_dense *A, permuted_dense *C);
8383

84+
/* Allocate a new PD C with the same sparsity (m, n, m0, n0, row_perm,
85+
col_perm) as A; values uninitialized. */
86+
matrix *copy_sparsity_pd_alloc(const permuted_dense *A);
87+
88+
/* Allocate C = A^T (transpose). */
89+
matrix *transpose_pd_alloc(const permuted_dense *A);
90+
91+
/* Fill values of C = A^T. */
92+
void transpose_pd_fill_values(const permuted_dense *A, permuted_dense *C);
93+
8494
#endif /* PERMUTED_DENSE_LINALG_H */

‎include/utils/stacked_pd.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,10 @@ matrix *coalesce_spd_alloc_unchecked(const stacked_pd *src);
114114
(block count, row_perms, col_perms) as the one passed now. */
115115
void coalesce_spd_fill_values(const stacked_pd *src, stacked_pd *out);
116116

117+
/* Same scatter as coalesce_spd_fill_values but with += instead of =.
118+
Use when src blocks share cells targeting the same out block (e.g.
119+
the B_k^T B_k summands aggregated by ATDA_spd_fill_values). Caller is
120+
responsible for zeroing out->base.x first. */
121+
void coalesce_spd_fill_values_accumulate(const stacked_pd *src, stacked_pd *out);
122+
117123
#endif /* STACKED_PD_H */

‎src/utils/permuted_dense.c‎

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ static void permuted_dense_refresh_csc_values(matrix *self)
5959
/* Vtable adapters — each delegates to the existing permuted_dense_* kernel. */
6060
static matrix *permuted_dense_vtable_copy_sparsity(const matrix *self)
6161
{
62-
const permuted_dense *pd = (const permuted_dense *) self;
63-
return new_permuted_dense(pd->base.m, pd->base.n, pd->m0, pd->n0, pd->row_perm,
64-
pd->col_perm, NULL);
62+
return copy_sparsity_pd_alloc((const permuted_dense *) self);
6563
}
6664

6765
static void permuted_dense_vtable_DA_fill_values(const double *d, const matrix *self,
@@ -99,28 +97,13 @@ static CSR_matrix *permuted_dense_to_csr(matrix *self)
9997

10098
static matrix *permuted_dense_vtable_transpose_alloc(const matrix *self)
10199
{
102-
const permuted_dense *pd = (const permuted_dense *) self;
103-
/* Swap (m, n), (m0, n0), and (row_perm, col_perm). The constructor
104-
asserts strict increase of both perms, which holds by construction. */
105-
return new_permuted_dense(pd->base.n, pd->base.m, pd->n0, pd->m0, pd->col_perm,
106-
pd->row_perm, NULL);
100+
return transpose_pd_alloc((const permuted_dense *) self);
107101
}
108102

109103
static void permuted_dense_vtable_transpose_fill_values(const matrix *self,
110104
matrix *out)
111105
{
112-
const permuted_dense *pd_in = (const permuted_dense *) self;
113-
permuted_dense *pd_out = (permuted_dense *) out;
114-
int m0 = pd_in->m0;
115-
int n0 = pd_in->n0;
116-
/* pd_out has shape (n0, m0); transpose pd_in->X into pd_out->X. */
117-
for (int ii = 0; ii < m0; ii++)
118-
{
119-
for (int jj = 0; jj < n0; jj++)
120-
{
121-
pd_out->X[jj * m0 + ii] = pd_in->X[ii * n0 + jj];
122-
}
123-
}
106+
transpose_pd_fill_values((const permuted_dense *) self, (permuted_dense *) out);
124107
}
125108

126109
static matrix *permuted_dense_vtable_index_alloc(matrix *self, const int *indices,

‎src/utils/permuted_dense_linalg.c‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,34 @@
2626
#include <stdlib.h>
2727
#include <string.h>
2828

29+
matrix *copy_sparsity_pd_alloc(const permuted_dense *A)
30+
{
31+
return new_permuted_dense(A->base.m, A->base.n, A->m0, A->n0, A->row_perm,
32+
A->col_perm, NULL);
33+
}
34+
35+
matrix *transpose_pd_alloc(const permuted_dense *A)
36+
{
37+
/* Swap (m, n), (m0, n0), and (row_perm, col_perm). The constructor
38+
asserts strict increase of both perms, which holds by construction. */
39+
return new_permuted_dense(A->base.n, A->base.m, A->n0, A->m0, A->col_perm,
40+
A->row_perm, NULL);
41+
}
42+
43+
void transpose_pd_fill_values(const permuted_dense *A, permuted_dense *out)
44+
{
45+
int m0 = A->m0;
46+
int n0 = A->n0;
47+
/* out has shape (n0, m0); transpose A->X into out->X. */
48+
for (int ii = 0; ii < m0; ii++)
49+
{
50+
for (int jj = 0; jj < n0; jj++)
51+
{
52+
out->X[jj * m0 + ii] = A->X[ii * n0 + jj];
53+
}
54+
}
55+
}
56+
2957
void DA_pd_fill_values(const double *d, const permuted_dense *A, permuted_dense *C)
3058
{
3159
int m0 = A->m0;

‎src/utils/stacked_pd.c‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,34 +600,69 @@ matrix *coalesce_spd_alloc_unchecked(const stacked_pd *src)
600600
return coalesce_spd_alloc_impl(src, false);
601601
}
602602

603-
void coalesce_spd_fill_values(const stacked_pd *src, stacked_pd *out)
603+
/* Scatter src's blocks into out's blocks, following out's src_block_idx_*
604+
map. If accumulate is true, += into out (caller must zero out->base.x
605+
first); otherwise = (precondition: source cells are pairwise disjoint
606+
across blocks targeting the same out block). `accumulate` is a
607+
compile-time constant at every call site, so the inner-loop branch is
608+
folded away when this is inlined into the public wrappers. */
609+
static inline void coalesce_spd_scatter(const stacked_pd *src, stacked_pd *out,
610+
bool accumulate)
604611
{
612+
/* for each block out_k of out */
605613
for (int k = 0; k < out->n_blocks; k++)
606614
{
607615
permuted_dense *out_k = out->blocks[k];
608616
int s_lo = out->src_block_idx_p[k];
609617
int s_hi = out->src_block_idx_p[k + 1];
618+
619+
/* for each block src_k in src contributing to out_k */
610620
for (int t = s_lo; t < s_hi; t++)
611621
{
612622
const permuted_dense *src_k = src->blocks[out->src_block_idx[t]];
623+
624+
/* for each row in src_k */
613625
for (int i = 0; i < src_k->m0; i++)
614626
{
615627
int out_i = out_k->row_inv[src_k->row_perm[i]];
616628
if (out_i < 0)
617629
{
618-
continue; /* row not in this output PD */
630+
/* this row in src_k does not contribute to out_k */
631+
continue;
619632
}
633+
634+
double *out_row = out_k->X + out_i * out_k->n0;
635+
const double *src_row = src_k->X + i * src_k->n0;
636+
637+
/* place each col in src_row at its correct position in out_row */
620638
for (int j = 0; j < src_k->n0; j++)
621639
{
622640
int out_j = out_k->col_inv[src_k->col_perm[j]];
623-
out_k->X[out_i * out_k->n0 + out_j] =
624-
src_k->X[i * src_k->n0 + j];
641+
assert(out_j >= 0);
642+
if (accumulate)
643+
{
644+
out_row[out_j] += src_row[j];
645+
}
646+
else
647+
{
648+
out_row[out_j] = src_row[j];
649+
}
625650
}
626651
}
627652
}
628653
}
629654
}
630655

656+
void coalesce_spd_fill_values(const stacked_pd *src, stacked_pd *out)
657+
{
658+
coalesce_spd_scatter(src, out, false);
659+
}
660+
661+
void coalesce_spd_fill_values_accumulate(const stacked_pd *src, stacked_pd *out)
662+
{
663+
coalesce_spd_scatter(src, out, true);
664+
}
665+
631666
#ifndef NDEBUG
632667
/* Pairwise verify that block row permutations are disjoint via a
633668
two-pointer merge over the sorted arrays. */

‎src/utils/stacked_pd_linalg.c‎

Lines changed: 18 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ matrix *copy_sparsity_spd_alloc(const stacked_pd *A)
120120
(permuted_dense **) SP_MALLOC(n_blocks * sizeof(permuted_dense *));
121121
for (int k = 0; k < n_blocks; k++)
122122
{
123-
const matrix *Ak = (matrix *) A->blocks[k];
124-
C_blocks[k] = (permuted_dense *) Ak->copy_sparsity(Ak);
123+
C_blocks[k] = (permuted_dense *) copy_sparsity_pd_alloc(A->blocks[k]);
125124
}
126125

127126
matrix *C = new_stacked_pd(A->base.m, A->base.n, n_blocks, C_blocks, NULL, NULL);
@@ -137,9 +136,14 @@ void DA_spd_fill_values(const double *d, const stacked_pd *A, stacked_pd *C)
137136
}
138137
}
139138

140-
/* ------------------------------------------------------------------ */
141-
/* Transpose */
142-
/* ------------------------------------------------------------------ */
139+
// ----------------------------------------------------------------------------------
140+
// Transpose C = A^T where A is stacked_pd. Blockwise logic followed by coalescing:
141+
// we first transpose each block independently, yielding a raw spd with the same
142+
// block count and swapped row/col perms as the final output but potentially
143+
// overlapping row perms. We then coalesce that raw spd to get the final output with
144+
// disjoint row perms. We keep the raw spd around on `out->work` for reuse by
145+
// `transpose_spd_fill_values` since its per-block transposes are also needed there.
146+
// ----------------------------------------------------------------------------------
143147

144148
matrix *transpose_spd_alloc(const stacked_pd *A)
145149
{
@@ -148,8 +152,7 @@ matrix *transpose_spd_alloc(const stacked_pd *A)
148152
(permuted_dense **) SP_MALLOC(n_blocks * sizeof(permuted_dense *));
149153
for (int k = 0; k < n_blocks; k++)
150154
{
151-
matrix *blk = (matrix *) A->blocks[k];
152-
AT_blocks[k] = (permuted_dense *) blk->transpose_alloc(blk);
155+
AT_blocks[k] = (permuted_dense *) transpose_pd_alloc(A->blocks[k]);
153156
}
154157

155158
/* Raw spd: dimensions swapped, identity src_block_idx_*; rows may
@@ -168,16 +171,17 @@ void transpose_spd_fill_values(const stacked_pd *A, stacked_pd *C)
168171
stacked_pd *raw = C->work;
169172
for (int k = 0; k < A->n_blocks; k++)
170173
{
171-
matrix *A_blk = (matrix *) A->blocks[k];
172-
matrix *raw_blk = (matrix *) raw->blocks[k];
173-
A_blk->transpose_fill_values(A_blk, raw_blk);
174+
transpose_pd_fill_values(A->blocks[k], raw->blocks[k]);
174175
}
175176
coalesce_spd_fill_values(raw, C);
176177
}
177178

178-
/* ------------------------------------------------------------------ */
179-
/* ATA / ATDA */
180-
/* ------------------------------------------------------------------ */
179+
// ------------------------------------------------------------------------------------
180+
// ATDA for stacked_pd A. Let A = [A1; A2; A3] where Ai has n columns (the same
181+
// number as A). Then ATDA = A1^T D1 A1 + A2^T D2 A2 + A3^T D3 A3. Term i and j
182+
// overlap if Ai and Aj have overlapping column entries. We therefore first compute
183+
// Ai^T Di Ai and then take care of the accumulation.
184+
// ------------------------------------------------------------------------------------
181185

182186
matrix *ATA_spd_alloc(const stacked_pd *A)
183187
{
@@ -207,49 +211,6 @@ matrix *ATA_spd_alloc(const stacked_pd *A)
207211
return out;
208212
}
209213

210-
/* Scatter + add each scratch PD into its destination block in C. Multiple scratch
211-
PDs may target the same Ck (their cells overlap by design — that's why we
212-
accumulate). Caller is responsible for zeroing C->base.x first. */
213-
static inline void accumulate_scratch_blocks(const stacked_pd *scratch,
214-
stacked_pd *C)
215-
{
216-
/* for each block Ck of C */
217-
for (int k = 0; k < C->n_blocks; k++)
218-
{
219-
permuted_dense *Ck = C->blocks[k];
220-
int s_lo = C->src_block_idx_p[k];
221-
int s_hi = C->src_block_idx_p[k + 1];
222-
223-
/* for each block Eq = Ai^T D Ai in scratch contributing to block Ck in C */
224-
for (int qq = s_lo; qq < s_hi; qq++)
225-
{
226-
const permuted_dense *Eq = scratch->blocks[C->src_block_idx[qq]];
227-
228-
/* for each row in Eq */
229-
for (int i = 0; i < Eq->m0; i++)
230-
{
231-
int Ck_row_idx = Ck->row_inv[Eq->row_perm[i]];
232-
if (Ck_row_idx < 0)
233-
{
234-
/* this row in Eq does not contribute to Ck */
235-
continue;
236-
}
237-
238-
double *Ck_row = Ck->X + Ck_row_idx * Ck->n0;
239-
double *Eq_row = Eq->X + i * Eq->n0;
240-
241-
/* place each col in Eq_row at its correct position in Ck_row */
242-
for (int j = 0; j < Eq->n0; j++)
243-
{
244-
int out_j = Ck->col_inv[Eq->col_perm[j]];
245-
assert(out_j >= 0);
246-
Ck_row[out_j] += Eq_row[j];
247-
}
248-
}
249-
}
250-
}
251-
}
252-
253214
void ATDA_spd_fill_values(const stacked_pd *A, const double *d, stacked_pd *C)
254215
{
255216
/* Let A = [A1; A2; A3] where Ai has n columns (the same number as A). Then
@@ -270,7 +231,7 @@ void ATDA_spd_fill_values(const stacked_pd *A, const double *d, stacked_pd *C)
270231
memset(C->base.x, 0, C->base.nnz * sizeof(double));
271232

272233
/* scatter + add each Ai^T Di Ai into its destination C block. */
273-
accumulate_scratch_blocks(scratch, C);
234+
coalesce_spd_fill_values_accumulate(scratch, C);
274235
}
275236

276237
/* ------------------------------------------------------------------ */

0 commit comments

Comments
 (0)