Skip to content

Compact permuted_dense (no inverse-permutation arrays) for kron-path blocks - #108

Draft
Transurgeon wants to merge 1 commit into
mainfrom
lazy-inv-arrays
Draft

Compact permuted_dense (no inverse-permutation arrays) for kron-path blocks#108
Transurgeon wants to merge 1 commit into
mainfrom
lazy-inv-arrays

Conversation

@Transurgeon

@Transurgeon Transurgeon commented Aug 2, 2026

Copy link
Copy Markdown
Member

Every permuted_dense allocated col_inv (global n ints) and row_inv (global m ints) at construction. On the dense left_matmul kron path this metadata dominates: p independent blocks each carry a full n_vars-sized col_inv, O(p*n_vars + p^2*m0) ints per node against exact-nnz values — 5.9 GB engine peak on OptimalAdvertising where true nnz needs ~5 MB.

Change

This introduces a compact PD variant — storage proportional to the block, not the global shape — scoped to the kron-csc family:

  • new_permuted_dense_compact leaves both inverse arrays NULL; the default constructor is untouched, so every non-kron PD keeps today's layout and code paths.
  • BA_pd_csc_alloc builds its output compact; compactness propagates through copy_sparsity / transpose / index chains of compact sources.
  • Init-path membership tests (idxs_hits_set callers, index_pd_alloc) gate on NULL and fall back to binary-search scans of the sorted perms (sorted_pos / sorted_hits).
  • Eval-path consumers materialize the operand-side array at alloc time only when the product is non-empty (permuted_dense_ensure_col_inv / _row_inv), so every fill kernel is untouched — BA_pd_csc_fill_values asserts the contract (inv != NULL || C->n0 == 0); index_pd_fill_values of a compact source reads a source-row map precomputed into kernel_iwork.
  • The mutable kron scratch keeps its arrays (its kernels write col_inv).

Verification

  • All 421 tests pass (Debug, ASan+UBSan, leaks clean). New peak-memory regression test (row-sum + col-sum Jacobian at 128x128): init peak 1324 -> 292 bytes/var (21.7 MB -> 4.8 MB), asserted < 400 bytes/var.
  • Backend correctness vs CPP (get_problem_data, CLARABEL): CVaR, SDP, Huber, TvInpainting, QuantumHilbertMatrix MATCH exactly; OptimalAdvertising matches except one pre-existing NaN-constant-folding difference (identical on the baseline engine).
  • OptimalAdvertising engine-internal peak: 5901 MB -> 621 MB; the residual is the per-branch CSC cache (documented follow-up). Cold extraction: 5.12 s -> 2.33 s.
  • CVaR (8.9 GB) and SDP (941 MB) peaks are unchanged — they sit on the kron pd/spd paths (BA_pd_pd_alloc / BTA_pd_spd_alloc outputs are still full by scope). Extending compactness there is the natural follow-up.

Before merge

  • Full 25-problem timing + memory suite gate (V6 of the plan) still to run.
  • Bump the SparseDiffPy submodule after this lands.

🤖 Generated with Claude Code

…blocks

Every permuted_dense allocated col_inv (global n ints) and row_inv
(global m ints) at construction. On the dense left_matmul kron path this
metadata dominates: p independent blocks each carry a full n_vars-sized
col_inv, O(p*n_vars + p^2*m0) ints per node against exact-nnz values
(5.9 GB engine peak on the OptimalAdvertising benchmark where true nnz
needs ~5 MB).

This introduces a compact PD variant whose storage stays proportional to
the block rather than the global shape, scoped to the kron-csc family:

- new_permuted_dense_compact leaves both inverse arrays NULL; the
  default constructor is unchanged, so every other PD keeps today's
  layout and O(1) code paths.
- BA_pd_csc_alloc builds its output compact, and compactness propagates
  through copy_sparsity/transpose/index chains of compact sources.
- Init-path membership tests (idxs_hits_set callers, index_pd_alloc)
  gate on a NULL inv and fall back to binary-search scans of the
  sorted perms (sorted_pos / sorted_hits in utils).
- Eval-path consumers ensure the operand-side array at alloc time only
  when the product is non-empty (permuted_dense_ensure_col_inv /
  _row_inv), keeping every fill kernel untouched (asserted in
  BA_pd_csc_fill_values); index_pd_fill_values of a compact source
  reads a source-row map precomputed into kernel_iwork at alloc.
- The mutable kron scratch keeps its arrays (its kernels write col_inv).

New peak-memory regression test builds the row-sum + col-sum Jacobian
shape at 128x128: init peak drops from 1324 to 292 bytes/var (21.7 MB
to 4.8 MB); the test asserts < 400 bytes/var.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Transurgeon Transurgeon changed the title Lazy inverse-permutation arrays for kron-path permuted_dense blocks Compact permuted_dense (no inverse-permutation arrays) for kron-path blocks Aug 2, 2026
@Transurgeon
Transurgeon marked this pull request as draft August 3, 2026 00:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant