All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- K-tiling for head_dim > 128 — NKI attention kernels now support
head_dim=256andhead_dim=512via an inner K-tile loop that splits theQ @ K.Tscore computation acrossTILE_K=128-sized chunks ofhead_dim, accumulating each into the existing PSUM.- All 4 NKI kernels updated:
_attn_stats_kernel,_attn_out_kernel,_attn_bwd_dq_kernel,_attn_bwd_dkdv_kernel. - For
head_dim ≤ 128: single-tile path preserved unchanged — Q is loaded once per block-row as a stationary tile (no regression). - For
head_dim > 128: K-tile inner loop (affine_range(head_dim // 128)).head_dimmust be a multiple of 128 (covers 256, 512). weights @ Vand all dK/dV accumulation matmuls are unchanged (K=128 block dimension, unaffected by head_dim growth).
- All 4 NKI kernels updated:
tests/test_attention.py:test_head_dim_256_parity,test_head_dim_256_dilated_parity,test_gradcheck_head_dim_256,test_backward_shapes_head_dim_256in PyTorch path.tests/test_nki_sim.py:TestAttnKTilingSimulator— simulator parity tests for forward and backward at head_dim=256.tests/test_nki_attn.py:TestAttnHardwareLargeDim— hardware parity tests at seq_len=512, head_dim=256.
_attn_gatherassertion relaxed fromhead_dim ≤ 128tohead_dim ≤ 128 or head_dim % 128 == 0.block_sparse_attention_tiledandnki_bsr_attn_tileddocstrings updated to document K-tiling support.
- NKI backward kernel pair (
_attn_bwd_dq_kernel+_attn_bwd_dkdv_kernel) intrnsparse/nki/kernels.py. When thenkibackend is active,_AttnTiledFunction.backwardnow dispatches to these kernels instead of the Python-loop backward._attn_bwd_dq_kernel: row-first traversal, mirrors_attn_out_kernel. AccumulatesdQ_m += dS @ K_ki * scalein a PSUM tile per block-row._attn_bwd_dkdv_kernel: column-first traversal (BSC view built on host). For each column block ki, iterates over all block-rows m that attend to ki, accumulatingdK_ki += dS.T @ Q_m * scaleanddV_ki += P.T @ dO_m.- No atomic scatter required — column-first gather on host eliminates NKI's lack of cross-block accumulation primitives.
- Stats saving in forward —
_AttnTiledFunction.forwardnow saves(Q, K, V, O, row_max, row_denom)in ctx (6 tensors vs 4). Backward receives the pre-computed row stats and skips the two recomputation passes. Applies to both PyTorch and NKI paths. _attn_bwd_gatherandnki_bsr_attn_bwdintrnsparse/nki/dispatch.py— host-side BSC construction and NKI backward orchestration.tests/test_nki_sim.py:TestAttnBwdSimulator— simulator correctness tests for dQ/dK/dV against PyTorch backward (local window + dilated patterns).tests/test_nki_attn.py:TestAttnBwdHardware— hardware parity tests at seq_len=512, head_dim=64.
nki_bsr_attn_tiledacceptsreturn_stats=Truekwarg (default False) to return(out, row_max, row_denom)._block_sparse_attn_pytorchacceptsreturn_stats=Truekwarg similarly._block_sparse_attn_backwardaccepts optionalrow_max/row_denomkwargs; when provided, skips the two recomputation BSR passes.
- Block-sparse attention autograd —
block_sparse_attention_tiledis now differentiable. When any of Q/K/V hasrequires_grad=True, the call routes through_AttnTiledFunction, which provides a tiled backward using the Flash Attention delta identity (D_i = dO_i · O_i). _block_sparse_attn_backward(Q, K, V, O, dO, mask_bsr, scale)— internal backward function. Single pass over nonzero BSR blocks; no O(seq_len²) intermediate. Row stats (row_max/row_denom) are recomputed in backward.gradcheckpasses atatol=1e-3for local-window and dilated patterns (float64, seq_len=256, head_dim=32). Seetests/test_attention.py::TestAttnTiledGrad.
- NKI backward kernel is a follow-up (v0.5.x). The current backward runs on PyTorch even when the NKI forward path is active.
- Row stats (row_max/row_denom) are recomputed in backward rather than saved from forward — avoids O(n_blocks × b) storage in the autograd graph at the cost of two extra BSR passes.
- NKI attention kernel pair (
_attn_stats_kernel+_attn_out_kernel, closes #25). Two-pass block-sparse attention now runs on the Tensor Engine via these kernels when thenkibackend is active. Thepytorchbackend retains the Python-loop reference path from v0.4.3.- Pass 1 (
_attn_stats_kernel): each(m, ki)block pair computed independently vianc_matmul;nl.max/nl.sumwithin-tile reductions produce(tile_max, tile_sumexp)with no carry between iterations. - Pass 2 (
_attn_out_kernel): recomputes scores, applies stable softmax usingrow_max/row_denomloaded per block-row (static HBM offset), accumulatesweights @ Vinto a PSUM tile spanning all ki blocks. - Constraint:
head_dim ≤ 128(nc_matmulpartition limit).head_dim=256requires K-tiling and is a follow-up.
- Pass 1 (
_attn_gather,_attn_host_reduction,nki_bsr_attn_tiledintrnsparse/nki/dispatch.py— host-side gather and orchestration for the kernel pair, mirroring the_bsr_pad_and_gatherpattern.tests/test_nki_sim.py:TestAttnTiledSimulator— simulator correctness tests (local window, dilated; seq_len=256, head_dim=32).tests/test_nki_attn.py: hardware tests for local window, dilated, and global-token patterns at seq_len=512, head_dim=64.
No autograd wrapping in v0.4.4 — forward-only, matching the existing PyTorch path. Attention backward (block-sparse) is a follow-up.
chebyshev_bsr(A, b, lam_min, lam_max, K)— fixed-K Chebyshev semi-iteration. Pre-computes all step coefficients from eigenvalue bounds before the loop; no inner products during iteration. Same convergence rate as CG without the adaptive coefficient computation that blocks NKI fusion. Returns(x, K, rel_residual)matching thecg_bsrsignature.chebyshev_coeffs(lam_min, lam_max, K)— returns the(alpha, beta)coefficient tensors for the momentum updatex_{k+1} = x_k + α_k r_k + β_k (x_k - x_{k-1}). Useful for inspecting or caching coefficients across solves with the same matrix spectrum.richardson_bsr(A, b, omega, K)— fixed-K Richardson iteration; the simplest fixed-point iteration with no coefficient computation at all. Optimal omega =2 / (lam_min + lam_max). Baseline comparator for Chebyshev.block_sparse_attention_tiled(Q, K, V, mask_bsr)— two-pass sparse attention that avoids the O(seq_len²) score intermediate. Pass 1 computes per-block(tile_max, tile_sumexp)statistics over nonzero blocks only (O(n_blocks × block_size) stats array, e.g. 5 KB at seq_len=512 vs 1 MB dense). Pass 2 recomputes scores, applies stable softmax using host-reduced row statistics, and accumulates V. PyTorch reference for the NKI kernel pair documented in #25.docs/sparse_attention.md: new "Tiled two-pass path" section explaining the algorithm, memory trade-offs, and the NKI kernel follow-up path.docs/architecture.md: "Known limits" updated — #22 and #25 now have v0.4.3 PyTorch references with clear notes on remaining NKI gate conditions.
Addresses the architecture-friendly workarounds identified in the analysis of
parked NKI-capability-gated issues (#22, #25). Both solvers and the tiled
attention path share the structural property of fixed-iteration loops with
statically-determined coefficients — the pattern that maps to nl.affine_range
without scalar carry.
examples/block_sparse_attention.py— block-sparse attention reference usingBSRMatrix+bsr_spmm. Three mask patterns (local window, dilated, global tokens); verifies against a dense reference; reports block density and timing for thebsr_spmmstep. Closes #21.docs/sparse_attention.md— writeup: how BSR-128 maps to Longformer/BigBird-style attention masks, block density arithmetic, pattern construction helpers, and the fused-tile follow-up.docs/iterative_solvers.mdnow linked inmkdocs.ymlnav (was present but missing from navigation).tests/test_attention.py— 8 CPU tests: mask shape/symmetry checks + parity against dense reference atatol=1e-4for all three patterns and the full-attention edge case.
No API changes, no kernel changes. The claim in #21 is: bsr_spmm is
the block-sparse attention primitive; BSRMatrix captures the mask.
The example and docs make that explicit.
examples/sparse_fock.pyrewritten around v0.4.0'sscreened_spmm. Three paths side-by-side on the same inputs: (1) v0.1.x unfusedschwarz_bounds → screen → from_dense → spmm; (2) v0.4.0 fusedscreened_spmm(one call); (3) full Fock build — the coulomb from path 2 contracted against MO coefficients viatrnblas.gemmforF_MO = C.T @ J @ C(falls back totorch.matmulif trnblas isn't installed). On a 50-basis synthetic system, the fused path is ~130× faster than the unfused (dominated by eliminating the Pythonfrom_denseCSR construction). Closes #6.examples/pyscf_bridge.py(new) — optional PySCF-driven demo. Builds H2O (or benzene, or H2), pulls real AO ERIs viamol.intor("int2e"), feeds the(μμ|μμ)diagonal intoschwarz_bounds+screened_spmmagainst a mock density matrix. Reports realistic sparsity atthreshold=1e-8. Requirespip install pyscf; tests skip cleanly if not available. Closes #13.tests/test_examples.py— 2 CPU smoke tests plus a PySCF-gated test. Exercises thesparse_fockunfused + fused paths end-to-end and asserts parity (atol=1e-6).
No API changes, no kernel changes — pure integration demo release. Users already on v0.4.0 can stay there; upgrade to v0.4.1 only to pick up the new examples.
screened_spmm(A, diag_integrals, B, threshold)— fused Schwarz- screened dense matmul. One NKI kernel fuses the full pipeline — outer-product pair bound → threshold → mask-apply →nc_matmul— into a single dispatch. Saves ~30–50% end-to-end vs the unfuseddensity_screen + from_dense + spmmflow on Fock-build-sized inputs. Closes #19._screened_spmm_kernel— new@nki.jitkernel intrnsparse/nki/kernels.py. Stationary-A-tile-reuse GEMM extended with a per-tile pair-bound mask built from the 1-D Schwarz-bound vector._ScreenedSpMMFunction—torch.autograd.Functionwrapper. Third differentiable NKI kernel in the trnsci suite (after v0.2.0 CSR SpMM and v0.3.0 BSR SpMM).torch.autograd.gradcheckpasses atatol=1e-4on hardware. Mask is non-differentiable (discrete gate); gradients flow toA(masked) andB(transposed masked A) only.- Tests: 4 CPU (
TestScreenedSpmm), 2 simulator (TestScreenedSpmmSimulator), 7 hardware (TestNkiScreenedSpmmParity+TestNkiScreenedSpmmDifferentiability). All green ontrn1.2xlarge. docs/architecture.md— new "Fused screened SpMM" section.
- #24 — fused-CG NKI
kernel was not buildable under NKI 2.24/0.3.0 constraints (no break,
no iteration-carried scalar state across
affine_range, no nested kernels). Per-iteration_cg_step_kernelreframe evaluated and found to save only 5–20% — not worth the authoring cost relative to #19's genuine 30–50% savings. See #24 close comment for the audit.
- Restricted to square
A(M == K) with 1-Ddiag_integrals. Rectangular / asymmetric-bounds extension is a follow-up if asked for.
cg_bsrandpower_iteration_bsr— Conjugate Gradient and power iteration on block-sparse row matrices. Plumbing on top ofbsr_spmm(one kernel dispatch per iteration). Closes Phase 1 of #22 on-chip iterative solvers.jacobi_preconditioner_bsr(A)— builds a diagonal preconditioner forcg_bsr'sM=argument.bsr_diagonal(A)— extracts the main diagonal from a BSR matrix.docs/iterative_solvers.md— design note covering the v0.3.2 plumbing and the v0.4.0 fused-kernel goal (#24). Explains the architectural win Trainium offers (A SBUF-resident across iterations) vs the current per-iteration HBM round-trip.tests/test_iterative.py— 8 CPU tests including scipy parity atatol=1e-4on a 128×128 SPD system.benchmarks/bench_iterative.py— cg_bsr vs scipy.sparse.linalg.cg. At 128×128 SPD: scipy 310 μs, trnsparse 369 μs (1.19×).
- Algorithm body for CG is a local copy of
trnsolver.iterative.cg; kept local to avoid a cross-repo runtime dependency for one function. - v0.4.0 will layer the fused CG/power-iteration NKI kernel on top — tracked in #24. The API stays stable across the transition; users upgrading from v0.3.2 get the fused-kernel speedup automatically when the fused path is available.
- Migrated NKI imports to the
nki.*namespace (NKI 0.3.0 Stable, Neuron SDK 2.29, April 2026). Legacyneuronxcc.nki.*shim is no longer used.pyproject.toml[neuron]extra gainsnki>=0.3.0alongside the existingneuronxcc>=2.24andtorch-neuronx>=2.9. Hosts without annkiwheel (macOS, non-Linux archs) still hitHAS_NKI=Falseand get the torch fallback. Kernel bodies unchanged — the trnblas audit confirmed the positionalnisa.nc_matmul+nl.copy(psum, ...)pattern complies with NKI 0.3.0. testCI job now filters-m "not neuron and not nki_simulator"so each test runs in exactly one job.
TRNSPARSE_USE_SIMULATOR=1dispatch branch throughnki.simulate(kernel)(np_args). Bypasses torch_xla + NEFF compile; kernels run on CPU for correctness iteration. Hardware still owns perf numbers.nki-simulatorCI job onubuntu-latest— installsnki>=0.3.0from the AWS pip index and runs the simulator suite on every push/PR. Kernel correctness gate without AWS cost. Catches Python-trace-level errors (bad kwargs, dropped ops, shape mismatches); MLIR verifier errors remain hardware-only (NKI 0.3.0 has no documented device-free NEFF compile API).tests/test_nki_sim.py— curated simulator suite (4 tests: CSR aligned + rectangular, BSR block-dense + block-diagonal). Skips cleanly off-hardware.scripts/run_simulator_tests.sh— SSM runner mirroringrun_neuron_tests.shwithTRNSPARSE_USE_SIMULATOR=1in the env.tests/conftest.py— registers thenki_simulatorpytest marker.
Addresses trnsci/trnsparse#23.
Follows the trnblas reference commits c693561, f24993b, 77eeb82
(suite-wide coordination in trnsci/trnsci#5).
BSRMatrix— block-sparse row format at 128×128 (the Tensor-Engine tile size). Every nonzero block is already a dense tile that maps one-to-one tonisa.nc_matmul. Conversions fromCSRMatrixand dense plus back. Seedocs/architecture.mdfor why BSR is the Trainium-native sparse representation.bsr_spmm(A_bsr, B)with NKI + PyTorch dispatch. On NKI, routes through_BSRSpMMFunction(suite-secondtorch.autograd.Function- wrapped kernel after v0.2.0's CSR SpMM). Per-blocknc_matmulwith zero gather overhead — uniform K_max per block-row via host-side zero-padding.tests/test_bsr.py— 9 CPU tests (format roundtrips, SpMM parity across block densities + rectangular shapes).tests/test_nki_bsr.py— 7@pytest.mark.neurontests includingtorch.autograd.gradcheckatatol=1e-4. Validated ontrn1.2xlarge.benchmarks/bench_bsr_spmm.py— BSR PyTorch + BSR NKI + dense GEMM ceiling across(m_blocks, n_blocks, block_density, N).docs/benchmarks.mdBSR section with real hardware numbers + an honest reading of why v0.3.0 BSR NKI doesn't beat CPU at small sizes (kernel dispatch overhead dominates; architectural wins are in follow-up issues #19, #20, #21).docs/architecture.mdlede rewritten around "why BSR is Trainium-native."sparse_addno longer materializes anN×Ndense intermediate — usestorch.sparse_coo_tensor.coalesce()for pattern union. Closes #8.density_screentest coverage (false-negative check + degenerate thresholds). Closes #10.
- Issue #15 (row-bucketing CSR) demoted to backlog. Under the architectural frame, the CSR path is served by the PyTorch fallback (v0.1.3) and BSR is the NKI-side story. Row-bucketing would only help if NKI 2.24 exposed an indirect-DMA primitive, which it doesn't.
- #8 sparse_add pattern union
- #9 density_screen bound clarification (current 2D bound is already tight)
- #10 density_screen test coverage
- #12 scipy migration guide
- #18 BSR format + NKI block-sparse SpMM
- #19 Fused screen + matmul NKI kernel
- #20 On-chip iterative solvers over BSR (SBUF-resident A)
- #21 Block-sparse attention primitive
- NKI SpMM kernel validated on
trn1.2xlarge.set_backend("nki")routesspmmthroughtrnsparse.nki.kernels._spmm_dense_kernel, which runs stationary-tile-reuse GEMM on the Tensor Engine. torch.autograd.Functionwrapping (_SpMMFunction) with analytic backward — the first differentiable NKI kernel in the trnsci suite. Satisfiestrnsci/trnsci#3; validated viatorch.autograd.gradcheckatatol=1e-4.tests/test_nki_spmm.py— hardware-gated parity + gradcheck coverage for tile-aligned, unaligned, and low-density inputs.benchmarks/bench_spmm.py— four-backend SpMM table (scipy / torch.sparse / trnsparse pytorch / trnsparse nki) in one pytest pass.docs/benchmarks.mdpopulated with realtrn1.2xlargenumbers.
docs/architecture.mddescribes the v0.2.0 SpMM dispatch path end to end (materialize → pad → NKI GEMM → slice) and documents the known dense-materialization cost that lands for row-bucketing (#15) in v0.3.0.
- SpMM NKI is slower than CPU backends in v0.2.0 — the dense materialization removes the sparsity advantage. This is Phase 1 (correctness). Row- bucketing in Phase 3 (#15) is where sparse speedups live.
- SpMV stays on the PyTorch path — single-output-column NKI dispatch doesn't amortize compile + HBM round-trip cost.
Closes #14 (Phase 1). Addresses #4 (NKI column populated). Unblocks #15 (Phase 3 perf).
spmv,spmm,spmv_symmetric, andCSRMatrix.to_densenow lower totorch.sparse_csr_tensoroperations instead of per-row Python loops.- Measured on CPU (256×256, density 0.01) the change is 26× faster for
SpMV (958 μs → 37 μs) and 52–88× faster for SpMM (1.2 ms → 13–24 μs
depending on RHS width), putting trnsparse's PyTorch fallback within
2× of
torch.sparse.
Does not affect public API or numeric outputs — existing tests pass unchanged. NKI backend remains scaffolded (routing lands in v0.2.0).
benchmarks/directory (conftest.py,bench_spmv.py,bench_spmm.py,bench_screening.py) running trnsparse vsscipy.sparsevstorch.sparseon the same numeric inputs. Closes #11; partial #4.
- Sync
trnsparse.__version__withpyproject.toml(both now0.1.2). Previously__init__.pyreported0.1.0while the package version was0.1.1. - Docs badge in
README.mdandsite_urlinmkdocs.ymlpoint attrnsci.dev/trnsparse/instead oftrnsci.github.io/trnsparse/. Per-repo GitHub Pages is superseded by the centralized trnsci.dev site. docs/architecture.mdclarifies that the NKI backend is scaffolded only — the PyTorch path runs regardless ofset_backendin v0.1.x. Routing + on-hardware validation land in v0.2.0.
- mkdocs site with
index,installation,quickstart,api,architecture,aws_setup infra/terraform/for on-hardware CI instance provisioningscripts/run_neuron_tests.shand benchmark helpers- GitHub Actions
ci.ymlfor CPU-only pytest matrix IssuesURL in pyproject.toml
- Bumped
neuronxccfloor from>=2.15to>=2.24to unify with the rest of the trnsci suite.torch-neuronxfloor bumped to>=2.9.
- Initial scaffold: CSRMatrix / COOMatrix, SpMV / SpMM, Schwarz screening
- NKI dispatch with gather-matmul-scatter kernel stub
examples/sparse_fock.py— screened Fock build demo