2222#include "matrix.h"
2323#include "permuted_dense.h"
2424
25+ /* Shape metadata for one would-be source block of a coalesce. Used by
26+ spd_blockwise_alloc_coalesce to drive symbolic coalesce without
27+ allocating per-block Gram PDs, and by spd_blockwise_fill_coalesce to
28+ re-aim a shared scratch PD at each input block's metadata.
29+ - (m0, n0): block shape.
30+ - row_perm, col_perm: aliased pointers into operand arrays; not owned.
31+ - scratch_*_needed: sizes the matching fill op will read from its
32+ destination PD's kernel_dwork / kernel_iwork. Coalesce ignores these;
33+ spd_blockwise uses them to size the shared scratch PD once at the
34+ max-across-blocks. */
35+ typedef struct
36+ {
37+ int m0 ;
38+ int n0 ;
39+ /* Both arrays are SP_MALLOC'd by the shape callback and owned by the
40+ per_block_shapes array (freed by stacked_pd_free). */
41+ int * row_perm ;
42+ int * col_perm ;
43+ /* Sizes the matching fill op will read from its destination PD's
44+ kernel_dwork / kernel_iwork. */
45+ size_t scratch_dwork_needed ;
46+ size_t scratch_iwork_needed ;
47+ /* Optional metadata to seed scratch->kernel_iwork[0..init_len) with
48+ before invoking the per-block fill op. Used by BTA_pd_spd's fill
49+ which reads s_max / max_n0_A from iwork[0..1]. */
50+ int iwork_init [2 ];
51+ int iwork_init_len ;
52+ } spd_per_block_shape ;
53+
2554/* stacked_pd represents a matrix that is the (vertical) union of 'n_blocks'
2655 permuted_dense blocks. Two different blocks have disjoint row permutations,
2756 but the column permutations may overlap across blocks.
@@ -56,6 +85,44 @@ typedef struct stacked_pd
5685 the kron-expanded transpose-of-A buffer across fills. NULL when
5786 not used. */
5887 permuted_dense * kernel_pd_scratch ;
88+
89+ /* Streaming-fill state for stacked_pds produced by
90+ spd_blockwise_alloc_coalesce. NULL when not used. All buffers are
91+ sized at alloc time so the matching fill function does zero
92+ SP_MALLOC.
93+ - scratch_X: per-iteration buffer holding one source block's Gram.
94+ Sized at max over input blocks of (m0_partial * n0_partial).
95+ - scratch_dwork / scratch_iwork: workspaces the per-block fill op
96+ reads from its (scratch) destination PD's kernel_dwork /
97+ kernel_iwork. Sized at max-across-blocks.
98+ - per_block_shapes: shape (and scratch sizes) for each input block's
99+ partial. Indexed [0, n_input_blocks). The scratch PD's metadata
100+ (m0, n0, row_perm, col_perm) is re-aimed from this array each
101+ iteration.
102+ - n_input_blocks: number of input blocks (length of per_block_shapes
103+ and of src_to_outs_p[]-1). Distinct from n_blocks (which counts
104+ OUTPUT blocks after coalesce).
105+ - src_to_outs_p, src_to_outs_data: CSR-style inverse of
106+ src_block_idx_p/_data. src_to_outs_data[src_to_outs_p[k] ..
107+ src_to_outs_p[k+1]) lists the output block indices that block k
108+ contributes to. Drives the per-input-block scatter loop in fill. */
109+ double * scratch_X ;
110+ size_t scratch_X_capacity ;
111+ double * scratch_dwork ;
112+ size_t scratch_dwork_capacity ;
113+ int * scratch_iwork ;
114+ size_t scratch_iwork_capacity ;
115+ /* Re-initialized per iteration from per_block_shapes[k].row_perm /
116+ col_perm. Sized at (m, n) — the global dimensions. Some fill ops
117+ (e.g. BTA_pd_spd_fill_values at stacked_pd_linalg.c:477) read
118+ C->col_inv to look up scatter positions; the scratch must have
119+ it populated. */
120+ int * scratch_row_inv ;
121+ int * scratch_col_inv ;
122+ spd_per_block_shape * per_block_shapes ;
123+ int n_input_blocks ;
124+ int * src_to_outs_p ;
125+ int * src_to_outs_data ;
59126} stacked_pd ;
60127
61128/* Constructor for stacked_pd. Takes ownership of every block in 'blocks'. The
@@ -75,6 +142,26 @@ matrix *new_stacked_pd_unchecked(int m, int n, int n_blocks, permuted_dense **bl
75142 const int * src_block_idx_p ,
76143 const int * src_block_idx );
77144
145+ /* Like new_stacked_pd_unchecked, but the blocks' X pointers already point
146+ into the caller-supplied `shared_x` buffer at the correct offsets
147+ (typically constructed via new_permuted_dense_view). No absorb / memcpy
148+ is performed. The stacked_pd takes ownership of `shared_x` and frees it
149+ on destruction; each block must have owns_X == false. */
150+ matrix * new_stacked_pd_borrowed_x (int m , int n , int n_blocks ,
151+ permuted_dense * * blocks ,
152+ const int * src_block_idx_p ,
153+ const int * src_block_idx , double * shared_x );
154+
155+ /* Build a stacked_pd from precomputed per-block shapes. Sums total_nnz,
156+ SP_MALLOCs one shared X buffer, constructs n_blocks view PDs pointing
157+ into that buffer at sequential offsets, and wraps via
158+ new_stacked_pd_borrowed_x. No absorb / double-count. Identity
159+ src_block_idx (one source per output block). Reads only the shape's
160+ m0 / n0 / row_perm / col_perm fields; per-block kernel_iwork /
161+ kernel_dwork are the caller's responsibility. */
162+ matrix * new_stacked_pd_from_shapes_unchecked (int m , int n , int n_blocks ,
163+ const spd_per_block_shape * shapes );
164+
78165/* Filter-map over a stacked_pd's blocks. For each block of B, calls op(Bk,
79166 ctx). Drop blocks whose result has nnz == 0 and assembles the survivors into
80167 a new stacked_pd (dimensions Cm x Cn) with one source per output block. */
@@ -93,13 +180,31 @@ matrix *coalesce_spd_alloc(const stacked_pd *A);
93180 overlapping row permutations. */
94181matrix * coalesce_spd_alloc_unchecked (const stacked_pd * A );
95182
183+ /* Symbolic coalesce driven by a shapes array (no per-block X / PDs needed).
184+ Returns an output stacked_pd whose blocks are views into one shared X
185+ buffer that the output owns. The output's src_block_idx_p / src_block_idx
186+ hold the forward map; callers that need the inverse read those fields
187+ directly. Equivalent to coalesce_spd_alloc_unchecked but works from shape
188+ metadata only — used by spd_blockwise_alloc_coalesce to skip the
189+ per-block Gram materialization. */
190+ matrix * coalesce_spd_alloc_from_shapes_unchecked (const spd_per_block_shape * shapes ,
191+ int n_input_blocks , int m , int n );
192+
96193/* Fill values of C = coalesce(A). */
97194void coalesce_spd_fill_values (const stacked_pd * A , stacked_pd * C );
98195
99196/* Same scatter as coalesce_spd_fill_values but with += instead of =. Caller is
100197 responsible for zeroing C->base.x first. */
101198void coalesce_spd_fill_values_accumulate (const stacked_pd * A , stacked_pd * C );
102199
200+ /* Scatter the cells of one source PD into one output PD (using out_k's
201+ row_inv / col_inv to map global row / col indices to local positions),
202+ accumulating with += (caller must zero out_k->X before the first call
203+ in a scatter cycle). Used by the per-input-block streaming fill in
204+ spd_blockwise_fill_coalesce_accumulate. */
205+ void scatter_one_source_into_one_output_accumulate (const permuted_dense * src_k ,
206+ permuted_dense * out_k );
207+
103208/* Re-index `idx_map` in place from CSR ordering to spd-native ordering
104209 (block-major). Useful for atoms that build idx_maps from a `to_csr`
105210 view at init time and want to read spd values directly at eval time,
0 commit comments