trnsparse 0.4.1__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. {trnsparse-0.4.1 → trnsparse-0.6.0}/.github/workflows/ci.yml +3 -0
  2. {trnsparse-0.4.1 → trnsparse-0.6.0}/.pre-commit-config.yaml +1 -1
  3. {trnsparse-0.4.1 → trnsparse-0.6.0}/CHANGELOG.md +177 -0
  4. {trnsparse-0.4.1 → trnsparse-0.6.0}/CLAUDE.md +23 -22
  5. {trnsparse-0.4.1/trnsparse.egg-info → trnsparse-0.6.0}/PKG-INFO +35 -8
  6. trnsparse-0.6.0/README.md +100 -0
  7. trnsparse-0.6.0/docs/architecture.md +143 -0
  8. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/benchmarks.md +22 -15
  9. trnsparse-0.6.0/docs/sparse_attention.md +229 -0
  10. trnsparse-0.6.0/examples/block_sparse_attention.py +309 -0
  11. {trnsparse-0.4.1 → trnsparse-0.6.0}/mkdocs.yml +2 -0
  12. {trnsparse-0.4.1 → trnsparse-0.6.0}/pyproject.toml +1 -1
  13. trnsparse-0.6.0/tests/test_attention.py +327 -0
  14. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_iterative.py +91 -0
  15. trnsparse-0.6.0/tests/test_nki_attn.py +201 -0
  16. trnsparse-0.6.0/tests/test_nki_sim.py +432 -0
  17. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse/__init__.py +15 -2
  18. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse/iterative.py +150 -0
  19. trnsparse-0.6.0/trnsparse/nki/dispatch.py +929 -0
  20. trnsparse-0.6.0/trnsparse/nki/kernels.py +573 -0
  21. trnsparse-0.6.0/trnsparse/ops.py +625 -0
  22. {trnsparse-0.4.1 → trnsparse-0.6.0/trnsparse.egg-info}/PKG-INFO +35 -8
  23. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse.egg-info/SOURCES.txt +4 -0
  24. trnsparse-0.4.1/README.md +0 -73
  25. trnsparse-0.4.1/docs/architecture.md +0 -109
  26. trnsparse-0.4.1/tests/test_nki_sim.py +0 -153
  27. trnsparse-0.4.1/trnsparse/nki/dispatch.py +0 -487
  28. trnsparse-0.4.1/trnsparse/nki/kernels.py +0 -192
  29. trnsparse-0.4.1/trnsparse/ops.py +0 -277
  30. {trnsparse-0.4.1 → trnsparse-0.6.0}/.github/workflows/notify-umbrella.yml +0 -0
  31. {trnsparse-0.4.1 → trnsparse-0.6.0}/.github/workflows/publish.yml +0 -0
  32. {trnsparse-0.4.1 → trnsparse-0.6.0}/.gitignore +0 -0
  33. {trnsparse-0.4.1 → trnsparse-0.6.0}/CODE_OF_CONDUCT.md +0 -0
  34. {trnsparse-0.4.1 → trnsparse-0.6.0}/CONTRIBUTING.md +0 -0
  35. {trnsparse-0.4.1 → trnsparse-0.6.0}/LICENSE +0 -0
  36. {trnsparse-0.4.1 → trnsparse-0.6.0}/benchmarks/bench_bsr_spmm.py +0 -0
  37. {trnsparse-0.4.1 → trnsparse-0.6.0}/benchmarks/bench_iterative.py +0 -0
  38. {trnsparse-0.4.1 → trnsparse-0.6.0}/benchmarks/bench_screening.py +0 -0
  39. {trnsparse-0.4.1 → trnsparse-0.6.0}/benchmarks/bench_spmm.py +0 -0
  40. {trnsparse-0.4.1 → trnsparse-0.6.0}/benchmarks/bench_spmv.py +0 -0
  41. {trnsparse-0.4.1 → trnsparse-0.6.0}/benchmarks/conftest.py +0 -0
  42. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/api.md +0 -0
  43. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/aws_setup.md +0 -0
  44. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/index.md +0 -0
  45. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/installation.md +0 -0
  46. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/iterative_solvers.md +0 -0
  47. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/migration_scipy.md +0 -0
  48. {trnsparse-0.4.1 → trnsparse-0.6.0}/docs/quickstart.md +0 -0
  49. {trnsparse-0.4.1 → trnsparse-0.6.0}/examples/sparse_fock.py +0 -0
  50. {trnsparse-0.4.1 → trnsparse-0.6.0}/infra/terraform/.terraform.lock.hcl +0 -0
  51. {trnsparse-0.4.1 → trnsparse-0.6.0}/infra/terraform/README.md +0 -0
  52. {trnsparse-0.4.1 → trnsparse-0.6.0}/infra/terraform/main.tf +0 -0
  53. {trnsparse-0.4.1 → trnsparse-0.6.0}/scripts/bench_to_md.py +0 -0
  54. {trnsparse-0.4.1 → trnsparse-0.6.0}/scripts/run_benchmarks.sh +0 -0
  55. {trnsparse-0.4.1 → trnsparse-0.6.0}/scripts/run_neuron_tests.sh +0 -0
  56. {trnsparse-0.4.1 → trnsparse-0.6.0}/scripts/run_simulator_tests.sh +0 -0
  57. {trnsparse-0.4.1 → trnsparse-0.6.0}/setup.cfg +0 -0
  58. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/conftest.py +0 -0
  59. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_bsr.py +0 -0
  60. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_examples.py +0 -0
  61. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_formats.py +0 -0
  62. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_nki_bsr.py +0 -0
  63. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_nki_screened_spmm.py +0 -0
  64. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_nki_spmm.py +0 -0
  65. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_ops.py +0 -0
  66. {trnsparse-0.4.1 → trnsparse-0.6.0}/tests/test_screening.py +0 -0
  67. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse/formats.py +0 -0
  68. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse/nki/__init__.py +0 -0
  69. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse/screening.py +0 -0
  70. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse.egg-info/dependency_links.txt +0 -0
  71. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse.egg-info/requires.txt +0 -0
  72. {trnsparse-0.4.1 → trnsparse-0.6.0}/trnsparse.egg-info/top_level.txt +0 -0
@@ -41,6 +41,8 @@ jobs:
41
41
  # Catches Python-trace-level errors (bad kwargs, dropped ops, shape
42
42
  # mismatches) pre-merge without AWS round-trips. MLIR verifier
43
43
  # errors remain hardware-only (simulator explicitly skips compile).
44
+ # TRNSPARSE_REQUIRE_NKI=1 disables the PyTorch fallback so kernel
45
+ # failures propagate — the simulator tests actually validate NKI kernels.
44
46
  runs-on: ubuntu-latest
45
47
  steps:
46
48
  - uses: actions/checkout@v6
@@ -55,4 +57,5 @@ jobs:
55
57
  - name: Run simulator-backed kernel tests
56
58
  env:
57
59
  TRNSPARSE_USE_SIMULATOR: "1"
60
+ TRNSPARSE_REQUIRE_NKI: "1"
58
61
  run: pytest tests/ -v -m nki_simulator --tb=short
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/astral-sh/ruff-pre-commit
3
- rev: v0.8.6
3
+ rev: v0.15.11
4
4
  hooks:
5
5
  - id: ruff
6
6
  args: [--fix]
@@ -5,6 +5,183 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] — 2026-04-17
9
+
10
+ ### Added
11
+
12
+ - **K-tiling for head_dim > 128** — NKI attention kernels now support
13
+ `head_dim=256` and `head_dim=512` via an inner K-tile loop that splits
14
+ the `Q @ K.T` score computation across `TILE_K=128`-sized chunks of
15
+ `head_dim`, accumulating each into the existing PSUM.
16
+ - All 4 NKI kernels updated: `_attn_stats_kernel`, `_attn_out_kernel`,
17
+ `_attn_bwd_dq_kernel`, `_attn_bwd_dkdv_kernel`.
18
+ - For `head_dim ≤ 128`: single-tile path preserved unchanged — Q is
19
+ loaded once per block-row as a stationary tile (no regression).
20
+ - For `head_dim > 128`: K-tile inner loop (`affine_range(head_dim //
21
+ 128)`). `head_dim` must be a multiple of 128 (covers 256, 512).
22
+ - `weights @ V` and all dK/dV accumulation matmuls are unchanged (K=128
23
+ block dimension, unaffected by head_dim growth).
24
+ - `tests/test_attention.py`: `test_head_dim_256_parity`,
25
+ `test_head_dim_256_dilated_parity`, `test_gradcheck_head_dim_256`,
26
+ `test_backward_shapes_head_dim_256` in PyTorch path.
27
+ - `tests/test_nki_sim.py`: `TestAttnKTilingSimulator` — simulator parity
28
+ tests for forward and backward at head_dim=256.
29
+ - `tests/test_nki_attn.py`: `TestAttnHardwareLargeDim` — hardware parity
30
+ tests at seq_len=512, head_dim=256.
31
+
32
+ ### Changed
33
+
34
+ - `_attn_gather` assertion relaxed from `head_dim ≤ 128` to `head_dim ≤ 128
35
+ or head_dim % 128 == 0`.
36
+ - `block_sparse_attention_tiled` and `nki_bsr_attn_tiled` docstrings updated
37
+ to document K-tiling support.
38
+
39
+ ## [0.5.1] — 2026-04-16
40
+
41
+ ### Added
42
+
43
+ - **NKI backward kernel pair** (`_attn_bwd_dq_kernel` + `_attn_bwd_dkdv_kernel`)
44
+ in `trnsparse/nki/kernels.py`. When the `nki` backend is active,
45
+ `_AttnTiledFunction.backward` now dispatches to these kernels instead of
46
+ the Python-loop backward.
47
+ - `_attn_bwd_dq_kernel`: row-first traversal, mirrors `_attn_out_kernel`.
48
+ Accumulates `dQ_m += dS @ K_ki * scale` in a PSUM tile per block-row.
49
+ - `_attn_bwd_dkdv_kernel`: column-first traversal (BSC view built on host).
50
+ For each column block ki, iterates over all block-rows m that attend to ki,
51
+ accumulating `dK_ki += dS.T @ Q_m * scale` and `dV_ki += P.T @ dO_m`.
52
+ - No atomic scatter required — column-first gather on host eliminates NKI's
53
+ lack of cross-block accumulation primitives.
54
+ - **Stats saving in forward** — `_AttnTiledFunction.forward` now saves
55
+ `(Q, K, V, O, row_max, row_denom)` in ctx (6 tensors vs 4). Backward
56
+ receives the pre-computed row stats and skips the two recomputation passes.
57
+ Applies to both PyTorch and NKI paths.
58
+ - `_attn_bwd_gather` and `nki_bsr_attn_bwd` in `trnsparse/nki/dispatch.py`
59
+ — host-side BSC construction and NKI backward orchestration.
60
+ - `tests/test_nki_sim.py`: `TestAttnBwdSimulator` — simulator correctness tests
61
+ for dQ/dK/dV against PyTorch backward (local window + dilated patterns).
62
+ - `tests/test_nki_attn.py`: `TestAttnBwdHardware` — hardware parity tests
63
+ at seq_len=512, head_dim=64.
64
+
65
+ ### Changed
66
+
67
+ - `nki_bsr_attn_tiled` accepts `return_stats=True` kwarg (default False) to
68
+ return `(out, row_max, row_denom)`.
69
+ - `_block_sparse_attn_pytorch` accepts `return_stats=True` kwarg similarly.
70
+ - `_block_sparse_attn_backward` accepts optional `row_max` / `row_denom`
71
+ kwargs; when provided, skips the two recomputation BSR passes.
72
+
73
+ ## [0.5.0] — 2026-04-16
74
+
75
+ ### Added
76
+
77
+ - **Block-sparse attention autograd** — `block_sparse_attention_tiled` is now
78
+ differentiable. When any of Q/K/V has `requires_grad=True`, the call routes
79
+ through `_AttnTiledFunction`, which provides a tiled backward using the Flash
80
+ Attention delta identity (`D_i = dO_i · O_i`).
81
+ - **`_block_sparse_attn_backward(Q, K, V, O, dO, mask_bsr, scale)`** — internal
82
+ backward function. Single pass over nonzero BSR blocks; no O(seq_len²)
83
+ intermediate. Row stats (row_max/row_denom) are recomputed in backward.
84
+ - `gradcheck` passes at `atol=1e-3` for local-window and dilated patterns
85
+ (float64, seq_len=256, head_dim=32). See `tests/test_attention.py::TestAttnTiledGrad`.
86
+
87
+ ### Notes
88
+
89
+ - NKI backward kernel is a follow-up (v0.5.x). The current backward runs
90
+ on PyTorch even when the NKI forward path is active.
91
+ - Row stats (row_max/row_denom) are recomputed in backward rather than saved
92
+ from forward — avoids O(n_blocks × b) storage in the autograd graph at the
93
+ cost of two extra BSR passes.
94
+
95
+ ## [0.4.4] — 2026-04-16
96
+
97
+ ### Added
98
+
99
+ - **NKI attention kernel pair** (`_attn_stats_kernel` + `_attn_out_kernel`,
100
+ closes [#25](https://github.com/trnsci/trnsparse/issues/25)). Two-pass
101
+ block-sparse attention now runs on the Tensor Engine via these kernels when
102
+ the `nki` backend is active. The `pytorch` backend retains the Python-loop
103
+ reference path from v0.4.3.
104
+ - Pass 1 (`_attn_stats_kernel`): each `(m, ki)` block pair computed
105
+ independently via `nc_matmul`; `nl.max` / `nl.sum` within-tile reductions
106
+ produce `(tile_max, tile_sumexp)` with no carry between iterations.
107
+ - Pass 2 (`_attn_out_kernel`): recomputes scores, applies stable softmax
108
+ using `row_max` / `row_denom` loaded per block-row (static HBM offset),
109
+ accumulates `weights @ V` into a PSUM tile spanning all ki blocks.
110
+ - **Constraint:** `head_dim ≤ 128` (`nc_matmul` partition limit). `head_dim=256`
111
+ requires K-tiling and is a follow-up.
112
+ - `_attn_gather`, `_attn_host_reduction`, `nki_bsr_attn_tiled` in
113
+ `trnsparse/nki/dispatch.py` — host-side gather and orchestration for the kernel
114
+ pair, mirroring the `_bsr_pad_and_gather` pattern.
115
+ - `tests/test_nki_sim.py`: `TestAttnTiledSimulator` — simulator correctness
116
+ tests (local window, dilated; seq_len=256, head_dim=32).
117
+ - `tests/test_nki_attn.py`: hardware tests for local window, dilated, and
118
+ global-token patterns at seq_len=512, head_dim=64.
119
+
120
+ ### Notes
121
+
122
+ No autograd wrapping in v0.4.4 — forward-only, matching the existing PyTorch
123
+ path. Attention backward (block-sparse) is a follow-up.
124
+
125
+ ## [0.4.3] — 2026-04-16
126
+
127
+ ### Added
128
+
129
+ - **`chebyshev_bsr(A, b, lam_min, lam_max, K)`** — fixed-K Chebyshev
130
+ semi-iteration. Pre-computes all step coefficients from eigenvalue bounds
131
+ before the loop; no inner products during iteration. Same convergence rate
132
+ as CG without the adaptive coefficient computation that blocks NKI fusion.
133
+ Returns `(x, K, rel_residual)` matching the `cg_bsr` signature.
134
+ - **`chebyshev_coeffs(lam_min, lam_max, K)`** — returns the `(alpha, beta)`
135
+ coefficient tensors for the momentum update `x_{k+1} = x_k + α_k r_k +
136
+ β_k (x_k - x_{k-1})`. Useful for inspecting or caching coefficients across
137
+ solves with the same matrix spectrum.
138
+ - **`richardson_bsr(A, b, omega, K)`** — fixed-K Richardson iteration; the
139
+ simplest fixed-point iteration with no coefficient computation at all.
140
+ Optimal omega = `2 / (lam_min + lam_max)`. Baseline comparator for Chebyshev.
141
+ - **`block_sparse_attention_tiled(Q, K, V, mask_bsr)`** — two-pass sparse
142
+ attention that avoids the O(seq_len²) score intermediate. Pass 1 computes
143
+ per-block `(tile_max, tile_sumexp)` statistics over nonzero blocks only
144
+ (O(n_blocks × block_size) stats array, e.g. 5 KB at seq_len=512 vs 1 MB
145
+ dense). Pass 2 recomputes scores, applies stable softmax using host-reduced
146
+ row statistics, and accumulates V. PyTorch reference for the NKI kernel pair
147
+ documented in [#25](https://github.com/trnsci/trnsparse/issues/25).
148
+ - **`docs/sparse_attention.md`**: new "Tiled two-pass path" section explaining
149
+ the algorithm, memory trade-offs, and the NKI kernel follow-up path.
150
+ - **`docs/architecture.md`**: "Known limits" updated — #22 and #25 now have
151
+ v0.4.3 PyTorch references with clear notes on remaining NKI gate conditions.
152
+
153
+ ### Notes
154
+
155
+ Addresses the architecture-friendly workarounds identified in the analysis of
156
+ parked NKI-capability-gated issues (#22, #25). Both solvers and the tiled
157
+ attention path share the structural property of fixed-iteration loops with
158
+ statically-determined coefficients — the pattern that maps to `nl.affine_range`
159
+ without scalar carry.
160
+
161
+ ## [0.4.2] — 2026-04-15
162
+
163
+ ### Added
164
+
165
+ - **`examples/block_sparse_attention.py`** — block-sparse attention
166
+ reference using `BSRMatrix` + `bsr_spmm`. Three mask patterns
167
+ (local window, dilated, global tokens); verifies against a dense
168
+ reference; reports block density and timing for the `bsr_spmm` step.
169
+ Closes [#21](https://github.com/trnsci/trnsparse/issues/21).
170
+ - **`docs/sparse_attention.md`** — writeup: how BSR-128 maps to
171
+ Longformer/BigBird-style attention masks, block density arithmetic,
172
+ pattern construction helpers, and the fused-tile follow-up.
173
+ - **`docs/iterative_solvers.md`** now linked in `mkdocs.yml` nav
174
+ (was present but missing from navigation).
175
+ - **`tests/test_attention.py`** — 8 CPU tests: mask shape/symmetry
176
+ checks + parity against dense reference at `atol=1e-4` for all three
177
+ patterns and the full-attention edge case.
178
+
179
+ ### Notes
180
+
181
+ No API changes, no kernel changes. The claim in #21 is: `bsr_spmm` is
182
+ the block-sparse attention primitive; `BSRMatrix` captures the mask.
183
+ The example and docs make that explicit.
184
+
8
185
  ## [0.4.1] — 2026-04-14
9
186
 
10
187
  ### Added
@@ -19,38 +19,39 @@ both memory and compute. trnsparse makes the sparsity explicit.
19
19
  trnsparse/
20
20
  ├── trnsparse/
21
21
  │ ├── __init__.py
22
- │ ├── formats.py # CSRMatrix, COOMatrix, conversions, from_dense
23
- │ ├── ops.py # spmv, spmm, spmv_symmetric, add, scale, transpose
22
+ │ ├── formats.py # CSRMatrix, COOMatrix, BSRMatrix, from_dense
23
+ │ ├── ops.py # spmv, spmm, bsr_spmm, screened_spmm, add, scale, transpose
24
24
  │ ├── screening.py # schwarz_bounds, screen_quartets, density_screen
25
+ │ ├── iterative.py # cg_bsr, power_iteration_bsr, jacobi_preconditioner_bsr
25
26
  │ └── nki/
26
27
  │ ├── __init__.py
27
- │ └── dispatch.py # Gather-matmul-scatter pattern for SpMM
28
+ │ ├── kernels.py # _bsr_spmm_kernel, _screened_spmm_kernel
29
+ │ └── dispatch.py # torch.autograd.Function wrappers + NKI/PyTorch routing
28
30
  ├── tests/
29
- │ ├── test_formats.py # CSR/COO construction, roundtrips
30
- │ ├── test_ops.py # SpMV, SpMM vs dense reference
31
- │ └── test_screening.py
32
31
  ├── examples/
33
- │ └── sparse_fock.py # Screened Fock build demo
32
+ │ ├── sparse_fock.py # Schwarz-screened Fock build (3 paths + trnblas)
33
+ │ ├── pyscf_bridge.py # Real AO integrals via PySCF (optional dep)
34
+ │ └── block_sparse_attention.py # Block-sparse attention via bsr_spmm
34
35
  ```
35
36
 
36
- ## NKI SpMM Strategy
37
+ ## NKI compute strategy (v0.4.x current)
37
38
 
38
- **v0.2.0 (current):** SpMM materializes the CSR into a dense `(M, K)` tile
39
- and runs stationary-tile-reuse GEMM on the Tensor Engine. Validated on
40
- trn1; forward + `torch.autograd.Function` backward both pass parity +
41
- `gradcheck` at `atol=1e-4`. The dense materialization means no sparsity
42
- advantage yet — NKI is slower than the PyTorch fallback at small sparse
43
- sizes (see `docs/benchmarks.md`).
39
+ **Formats:** CSR/COO are interop and PyTorch-fallback formats.
40
+ BSR at `block_size=128` is the NKI compute format — every nonzero block
41
+ is already a 128×128 Tensor Engine tile, zero gather overhead.
44
42
 
45
- **v0.3.0 (planned, #15):** gather-matmul-scatter with row-bucketing.
46
- For each bucket of rows with similar nnz:
47
- 1. **DMA engine**: gather non-zero column indices into dense SBUF tiles.
48
- 2. **Tensor Engine**: matmul the tile against B columns.
49
- 3. **DMA engine**: scatter results back to output rows.
43
+ **Dispatch hierarchy:**
44
+ 1. `spmm(csr, B)` — PyTorch `torch.sparse_csr_tensor` fallback (within
45
+ 2× of scipy; NKI overhead not worth it for CSR at current sizes).
46
+ 2. `bsr_spmm(bsr, B)` — NKI kernel `_bsr_spmm_kernel`; one `nc_matmul`
47
+ per nonzero block. `torch.autograd.Function`-wrapped; gradcheck passes.
48
+ 3. `screened_spmm(A, diag, B, threshold)` — fused Schwarz + mask + matmul
49
+ in one NKI dispatch; avoids 4 host passes + CSR build + separate SpMM.
50
+ 4. `block_sparse_attention(Q, K, V, mask_bsr)` — `bsr_spmm` applied to
51
+ the attention weight matrix; no new kernel, mask is a BSRMatrix.
50
52
 
51
- Same pattern used in sparse attention. Uniform nnz maps cleanly to fixed-
52
- size tiles; variable nnz is handled by bucketing rows by nnz quantile so
53
- each bucket pads only within itself.
53
+ **Open gaps (NKI capability-gated):** row-bucketing CSR (#15), sharded BSR
54
+ across NeuronCores (#16), fused tile-level attention scores (#25).
54
55
 
55
56
  ## Dependencies
56
57
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trnsparse
3
- Version: 0.4.1
3
+ Version: 0.6.0
4
4
  Summary: Sparse matrix operations for AWS Trainium via NKI
5
5
  Author-email: Scott Friedman <scttfrdmn@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -47,12 +47,17 @@ CSR/COO formats, SpMV, SpMM, and integral screening for sparse scientific comput
47
47
 
48
48
  trnsparse follows the [trnsci 5-phase roadmap](https://trnsci.dev/roadmap/). Active work is tracked in phase-labeled GitHub issues:
49
49
 
50
- - **[Phase 1 — correctness](https://github.com/trnsci/trnsparse/issues/14)** ✅ v0.2.0: NKI SpMM validated on trn1 via densify-then-GEMM; first `torch.autograd.Function`-wrapped NKI kernel in the suite (see [`trnsci/trnsci#3`](https://github.com/trnsci/trnsci/issues/3)). Benchmarks in [`docs/benchmarks.md`](https://trnsci.dev/trnsparse/benchmarks/).
51
- - **[Phase 3 — perf](https://github.com/trnsci/trnsparse/issues/15)**: nnz-bucketing SpMM, streaming large-sparse, NEFF cache reuse.
52
- - **[Phase 4 — multi-chip](https://github.com/trnsci/trnsparse/issues/16)**: sharded sparse matrices across chips.
50
+ - **[Phase 1 — correctness](https://github.com/trnsci/trnsparse/issues/14)** ✅ v0.2.0: NKI SpMM validated on trn1 via densify-then-GEMM (see [`trnsci/trnsci#3`](https://github.com/trnsci/trnsci/issues/3)).
51
+ - **v0.3.0** ✅ [`BSRMatrix`](https://trnsci.dev/trnsparse/architecture/) — Trainium-native 128×128 block-sparse format; `bsr_spmm` NKI kernel. CSR becomes interop; BSR is the compute path.
52
+ - **v0.3.2** ✅ `cg_bsr`, `power_iteration_bsr` — iterative solvers over BSR (Python loop; fused kernel gated on NKI capability).
53
+ - **v0.4.0** ✅ `screened_spmm` — fused Schwarz-screened SpMM in one NKI dispatch.
54
+ - **v0.4.2** ✅ Block-sparse attention — `BSRMatrix` + `bsr_spmm` as the primitive; `examples/block_sparse_attention.py` + [`docs/sparse_attention.md`](https://trnsci.dev/trnsparse/sparse_attention/).
55
+ - **v0.4.3** ✅ Architecture-friendly alternatives: `chebyshev_bsr` / `richardson_bsr` (fixed-K solvers, no inner products); `block_sparse_attention_tiled` (two-pass, no O(seq²) intermediate).
56
+ - **[Phase 3 — perf](https://github.com/trnsci/trnsparse/issues/15)**: nnz-bucketing, NKI attention kernel pair — parked on NKI indirect DMA gather / hardware validation.
57
+ - **[Phase 4 — multi-chip](https://github.com/trnsci/trnsparse/issues/16)**: sharded BSR across NeuronCores.
53
58
  - **[Phase 5 — generation](https://github.com/trnsci/trnsparse/issues/17)**: trn2 DMA bandwidth exploitation.
54
59
 
55
- _(No Phase 2 for trnsparse — the precision story is inherited from trnblas.)_
60
+ _(No Phase 2 for trnsparse — precision inherited from trnblas.)_
56
61
 
57
62
  Suite-wide tracker: [trnsci/trnsci#1](https://github.com/trnsci/trnsci/issues/1).
58
63
 
@@ -89,12 +94,21 @@ stats = trnsparse.sparsity_stats(Q)
89
94
 
90
95
  | Operation | Description |
91
96
  |-----------|-------------|
92
- | `spmv` | Sparse × dense vector |
93
- | `spmm` | Sparse × dense matrix |
97
+ | `spmv` | Sparse × dense vector (CSR) |
98
+ | `spmm` | Sparse × dense matrix (CSR, PyTorch fallback) |
99
+ | `bsr_spmm` | Block-sparse × dense (BSR-native, Tensor Engine) |
100
+ | `screened_spmm` | Fused Schwarz-screened matmul (one NKI dispatch) |
94
101
  | `spmv_symmetric` | Symmetric SpMV (half storage) |
95
102
  | `sparse_add` | C = αA + βB |
96
103
  | `sparse_scale` | B = αA |
97
- | `sparse_transpose` | A^T |
104
+ | `sparse_transpose` | Aᵀ |
105
+ | `cg_bsr` | Conjugate Gradient on BSR matrix |
106
+ | `chebyshev_bsr` | Fixed-K Chebyshev semi-iteration (no inner products) |
107
+ | `richardson_bsr` | Fixed-K Richardson iteration |
108
+ | `power_iteration_bsr` | Dominant eigenpair via power iteration |
109
+ | `jacobi_preconditioner_bsr` | Diagonal preconditioner for `cg_bsr` |
110
+ | `bsr_diagonal` | Extract main diagonal from BSR matrix |
111
+ | `block_sparse_attention_tiled` | Two-pass sparse attention, no O(seq²) intermediate |
98
112
  | `schwarz_bounds` | Schwarz screening bounds |
99
113
  | `screen_quartets` | Shell quartet significance mask |
100
114
  | `density_screen` | Density-weighted screening |
@@ -102,3 +116,16 @@ stats = trnsparse.sparsity_stats(Q)
102
116
  ## License
103
117
 
104
118
  Apache 2.0 — Copyright 2026 Scott Friedman
119
+
120
+
121
+ ## Disclaimer
122
+
123
+ trnsci is an **independent open-source project**. It is not sponsored by, endorsed by, or affiliated with Amazon.com, Inc., Amazon Web Services, Inc., or Annapurna Labs Ltd.
124
+
125
+ "AWS", "Amazon", "Trainium", "Inferentia", "NeuronCore", "Neuron SDK", and related identifiers are trademarks of their respective owners and are used here solely for descriptive and interoperability purposes. Use does not imply endorsement, partnership, or any other relationship.
126
+
127
+ All work, opinions, analyses, benchmark results, architectural commentary, and editorial judgments in this repository and on [trnsci.dev](https://trnsci.dev) are those of the project's contributors. They do not represent the views, positions, or commitments of Amazon, AWS, or Annapurna Labs.
128
+
129
+ Feedback directed at the Neuron SDK or Trainium hardware is good-faith ecosystem commentary from independent users. It is not privileged information, is not pre-reviewed by AWS, and should not be read as authoritative about product roadmap, behavior, or quality.
130
+
131
+ For official AWS guidance, see [aws-neuron documentation](https://awsdocs-neuron.readthedocs-hosted.com/) and the [AWS Trainium product page](https://aws.amazon.com/ai/machine-learning/trainium/).
@@ -0,0 +1,100 @@
1
+ # trnsparse
2
+
3
+ [![CI](https://github.com/trnsci/trnsparse/actions/workflows/ci.yml/badge.svg)](https://github.com/trnsci/trnsparse/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/trnsci/trnsparse/graph/badge.svg)](https://codecov.io/gh/trnsci/trnsparse)
5
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
6
+ [![PyPI](https://img.shields.io/pypi/v/trnsparse)](https://pypi.org/project/trnsparse/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/trnsparse)](https://pypi.org/project/trnsparse/)
8
+ [![License](https://img.shields.io/github/license/trnsci/trnsparse)](LICENSE)
9
+ [![Docs](https://img.shields.io/badge/docs-trnsci.dev-blue)](https://trnsci.dev/trnsparse/)
10
+
11
+ Sparse matrix operations for AWS Trainium via NKI.
12
+
13
+ CSR/COO formats, SpMV, SpMM, and integral screening for sparse scientific computing on Trainium. Part of the trnsci scientific computing suite ([github.com/trnsci](https://github.com/trnsci)).
14
+
15
+ ## Current phase
16
+
17
+ trnsparse follows the [trnsci 5-phase roadmap](https://trnsci.dev/roadmap/). Active work is tracked in phase-labeled GitHub issues:
18
+
19
+ - **[Phase 1 — correctness](https://github.com/trnsci/trnsparse/issues/14)** ✅ v0.2.0: NKI SpMM validated on trn1 via densify-then-GEMM (see [`trnsci/trnsci#3`](https://github.com/trnsci/trnsci/issues/3)).
20
+ - **v0.3.0** ✅ [`BSRMatrix`](https://trnsci.dev/trnsparse/architecture/) — Trainium-native 128×128 block-sparse format; `bsr_spmm` NKI kernel. CSR becomes interop; BSR is the compute path.
21
+ - **v0.3.2** ✅ `cg_bsr`, `power_iteration_bsr` — iterative solvers over BSR (Python loop; fused kernel gated on NKI capability).
22
+ - **v0.4.0** ✅ `screened_spmm` — fused Schwarz-screened SpMM in one NKI dispatch.
23
+ - **v0.4.2** ✅ Block-sparse attention — `BSRMatrix` + `bsr_spmm` as the primitive; `examples/block_sparse_attention.py` + [`docs/sparse_attention.md`](https://trnsci.dev/trnsparse/sparse_attention/).
24
+ - **v0.4.3** ✅ Architecture-friendly alternatives: `chebyshev_bsr` / `richardson_bsr` (fixed-K solvers, no inner products); `block_sparse_attention_tiled` (two-pass, no O(seq²) intermediate).
25
+ - **[Phase 3 — perf](https://github.com/trnsci/trnsparse/issues/15)**: nnz-bucketing, NKI attention kernel pair — parked on NKI indirect DMA gather / hardware validation.
26
+ - **[Phase 4 — multi-chip](https://github.com/trnsci/trnsparse/issues/16)**: sharded BSR across NeuronCores.
27
+ - **[Phase 5 — generation](https://github.com/trnsci/trnsparse/issues/17)**: trn2 DMA bandwidth exploitation.
28
+
29
+ _(No Phase 2 for trnsparse — precision inherited from trnblas.)_
30
+
31
+ Suite-wide tracker: [trnsci/trnsci#1](https://github.com/trnsci/trnsci/issues/1).
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install trnsparse
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```python
42
+ import torch
43
+ import trnsparse
44
+
45
+ # Dense → sparse
46
+ A = torch.randn(100, 100)
47
+ A[torch.abs(A) < 1.0] = 0.0
48
+ csr = trnsparse.from_dense(A)
49
+
50
+ # SpMV: y = A @ x
51
+ y = trnsparse.spmv(csr, x, alpha=2.0)
52
+
53
+ # SpMM: C = A @ B
54
+ C = trnsparse.spmm(csr, B)
55
+
56
+ # Integral screening
57
+ Q = trnsparse.schwarz_bounds(diagonal_integrals)
58
+ mask = trnsparse.screen_quartets(Q, threshold=1e-10)
59
+ stats = trnsparse.sparsity_stats(Q)
60
+ ```
61
+
62
+ ## Operations
63
+
64
+ | Operation | Description |
65
+ |-----------|-------------|
66
+ | `spmv` | Sparse × dense vector (CSR) |
67
+ | `spmm` | Sparse × dense matrix (CSR, PyTorch fallback) |
68
+ | `bsr_spmm` | Block-sparse × dense (BSR-native, Tensor Engine) |
69
+ | `screened_spmm` | Fused Schwarz-screened matmul (one NKI dispatch) |
70
+ | `spmv_symmetric` | Symmetric SpMV (half storage) |
71
+ | `sparse_add` | C = αA + βB |
72
+ | `sparse_scale` | B = αA |
73
+ | `sparse_transpose` | Aᵀ |
74
+ | `cg_bsr` | Conjugate Gradient on BSR matrix |
75
+ | `chebyshev_bsr` | Fixed-K Chebyshev semi-iteration (no inner products) |
76
+ | `richardson_bsr` | Fixed-K Richardson iteration |
77
+ | `power_iteration_bsr` | Dominant eigenpair via power iteration |
78
+ | `jacobi_preconditioner_bsr` | Diagonal preconditioner for `cg_bsr` |
79
+ | `bsr_diagonal` | Extract main diagonal from BSR matrix |
80
+ | `block_sparse_attention_tiled` | Two-pass sparse attention, no O(seq²) intermediate |
81
+ | `schwarz_bounds` | Schwarz screening bounds |
82
+ | `screen_quartets` | Shell quartet significance mask |
83
+ | `density_screen` | Density-weighted screening |
84
+
85
+ ## License
86
+
87
+ Apache 2.0 — Copyright 2026 Scott Friedman
88
+
89
+
90
+ ## Disclaimer
91
+
92
+ trnsci is an **independent open-source project**. It is not sponsored by, endorsed by, or affiliated with Amazon.com, Inc., Amazon Web Services, Inc., or Annapurna Labs Ltd.
93
+
94
+ "AWS", "Amazon", "Trainium", "Inferentia", "NeuronCore", "Neuron SDK", and related identifiers are trademarks of their respective owners and are used here solely for descriptive and interoperability purposes. Use does not imply endorsement, partnership, or any other relationship.
95
+
96
+ All work, opinions, analyses, benchmark results, architectural commentary, and editorial judgments in this repository and on [trnsci.dev](https://trnsci.dev) are those of the project's contributors. They do not represent the views, positions, or commitments of Amazon, AWS, or Annapurna Labs.
97
+
98
+ Feedback directed at the Neuron SDK or Trainium hardware is good-faith ecosystem commentary from independent users. It is not privileged information, is not pre-reviewed by AWS, and should not be read as authoritative about product roadmap, behavior, or quality.
99
+
100
+ For official AWS guidance, see [aws-neuron documentation](https://awsdocs-neuron.readthedocs-hosted.com/) and the [AWS Trainium product page](https://aws.amazon.com/ai/machine-learning/trainium/).
@@ -0,0 +1,143 @@
1
+ # Architecture
2
+
3
+ ## Why BSR is Trainium-native
4
+
5
+ Trainium's Tensor Engine is a 128×128 systolic array. `nisa.nc_matmul`
6
+ moves a 128-partition stationary tile against a moving operand of up to
7
+ 128 × 512, all on-chip. The natural "unit of sparse work" on this
8
+ architecture is therefore not a single nonzero — it's a 128×128 block.
9
+
10
+ **BSR (block-sparse row)** at `block_size=128` stores a matrix as a list
11
+ of these blocks plus a block-level CSR pattern. Every stored block is
12
+ already in the shape the systolic array wants: one `nc_matmul` per block,
13
+ no gather step, no padding-within-tile waste. The architectural match is
14
+ exact.
15
+
16
+ Contrast with CSR, which stores individual nonzero elements. The v0.2.0
17
+ NKI path for CSR materializes the matrix into a dense `(M, K)` tile
18
+ before the matmul (see the [SpMM path](#v020-csr-spmm-path) below) —
19
+ necessary for correctness, but it pays the full `M × K` cost whether the
20
+ matrix is sparse or not. BSR skips this because blocks are already dense.
21
+
22
+ **Consequence for the library's shape:** CSR remains the construction and
23
+ interop format (scipy compatibility, PyTorch's `torch.sparse_csr_tensor`
24
+ interop). BSR is the compute format for the NKI path. For matrices with
25
+ real block structure — Fock/ERI tensors after Schwarz screening, FEM
26
+ stiffness matrices, graph adjacencies, block-sparse attention masks —
27
+ BSR is strictly preferred. For truly unstructured sparse (random CSR),
28
+ the PyTorch fallback is already within 2× of scipy; NKI adds nothing.
29
+
30
+ See [Benchmarks](benchmarks.md) for numbers that validate this framing.
31
+
32
+
33
+ ```
34
+ trnsparse/
35
+ ├── trnsparse/
36
+ │ ├── __init__.py
37
+ │ ├── formats.py # CSRMatrix, COOMatrix, BSRMatrix, from_dense
38
+ │ ├── ops.py # spmv, spmm, bsr_spmm, screened_spmm, ...
39
+ │ ├── screening.py # schwarz_bounds, screen_quartets, density_screen
40
+ │ ├── iterative.py # cg_bsr, power_iteration_bsr, jacobi_preconditioner_bsr
41
+ │ └── nki/
42
+ │ ├── __init__.py
43
+ │ ├── kernels.py # _bsr_spmm_kernel, _screened_spmm_kernel
44
+ │ └── dispatch.py # torch.autograd.Function wrappers + routing
45
+ ├── tests/
46
+ ├── examples/
47
+ │ ├── sparse_fock.py # Schwarz-screened Fock (3 paths + trnblas)
48
+ │ ├── pyscf_bridge.py # Real AO integrals via PySCF
49
+ │ └── block_sparse_attention.py # Block-sparse attention via bsr_spmm
50
+ ```
51
+
52
+ ## NKI dispatch hierarchy
53
+
54
+ CSR and COO are interop and PyTorch-fallback formats. BSR is the NKI
55
+ compute format. `nki/dispatch.py` exposes `HAS_NKI`,
56
+ `set_backend("auto"|"pytorch"|"nki")`, `get_backend()`, and the NKI
57
+ entry points.
58
+
59
+ ### `spmm(csr, B)` — PyTorch fallback
60
+
61
+ Routes through `torch.sparse_csr_tensor` operations. Benchmarked within
62
+ 2× of scipy at typical shapes; the NKI per-op overhead doesn't pay off
63
+ for CSR SpMM until the matrix is large enough that the v0.2.0 CSR kernel
64
+ (densify-then-GEMM) would dominate anyway. SpMV also stays on this path.
65
+
66
+ ### `bsr_spmm(bsr, B)` — NKI, `_bsr_spmm_kernel`
67
+
68
+ One `nisa.nc_matmul` per nonzero block. Host-side preamble pads each
69
+ block-row to the same `K_max` so the kernel's `affine_range` bounds are
70
+ fixed. `torch.autograd.Function`-wrapped (`_BSRSpMMFunction`); gradcheck
71
+ passes at `atol=1e-4` on hardware.
72
+
73
+ ### `screened_spmm(A, diag, B, threshold)` — NKI, fused
74
+
75
+ Fuses Schwarz bound (outer-product pair bound), threshold mask, and
76
+ matmul into one dispatch. Saves ~30–50% vs the unfused
77
+ `schwarz_bounds → screen_quartets → from_dense → spmm` flow on
78
+ Fock-build-sized inputs.
79
+
80
+ ### Block-sparse attention
81
+
82
+ `bsr_spmm` applied to the post-softmax attention weight matrix. No new
83
+ kernel — a `BSRMatrix` with a local-window or dilated block pattern IS
84
+ an attention mask. See [sparse_attention.md](sparse_attention.md).
85
+
86
+ ## Formats
87
+
88
+ - **CSR**: row pointer + column indices + values. Preferred for construction,
89
+ interop with scipy/PyTorch, and SpMV.
90
+ - **COO**: three parallel 1-D tensors. Preferred for construction and permutation.
91
+ - **BSR** (`block_size=128`): stacked 128×128 blocks + block-level CSR pattern.
92
+ Every block maps to one `nc_matmul`; zero gather overhead.
93
+
94
+ All three support `from_dense`, `to_dense`, and interconversion.
95
+
96
+ ## Autograd wrapping
97
+
98
+ Every NKI kernel lives inside a `torch.autograd.Function` (satisfies
99
+ [`trnsci/trnsci#3`](https://github.com/trnsci/trnsci/issues/3)).
100
+ Backward passes run at the PyTorch level via block-gradient projection.
101
+ Block-selection is non-differentiable by construction; `grad_out` is
102
+ routed into exactly the stored blocks. `gradcheck` at `atol=1e-4` is
103
+ part of the hardware test matrix for all three NKI kernels.
104
+
105
+ ### Fused screened SpMM (v0.4.0)
106
+
107
+ `screened_spmm(A, diag_integrals, B, threshold)` fuses the
108
+ chemistry-screened SpMM pipeline — Schwarz bound from the diagonal
109
+ integrals, pair-bound threshold mask, masked matmul — into a single
110
+ NKI kernel. The unfused equivalent does four host passes (sqrt, outer
111
+ product, threshold, mask-apply) plus a separate `from_dense` + `spmm`
112
+ call; the fused kernel collapses all of that into one dispatch.
113
+
114
+ Mask semantics: `mask[i,j] = sqrt(|diag[i]|) * sqrt(|diag[j]|) >
115
+ sqrt(threshold)`, matching `schwarz_bounds` + `screen_quartets`
116
+ composed. No gradient flows back to `diag_integrals` or `threshold` —
117
+ the mask is treated as a discrete gate; `grad_A *= mask` and
118
+ `grad_B = (A * mask).T @ grad_C`.
119
+
120
+ Restricted to square A (`M == K`) with 1-D `diag_integrals` in v0.4.0
121
+ — the common Fock-build case. Rectangular / asymmetric-bounds
122
+ extension is a follow-up if asked for.
123
+
124
+ ## Known limits
125
+
126
+ - **Row-bucketing CSR** ([#15](https://github.com/trnsci/trnsparse/issues/15)) — parked.
127
+ Requires NKI indirect-DMA gather (not exposed as of NKI 0.3.0). The CSR PyTorch
128
+ fallback is within 2× of scipy; that's the current story.
129
+ - **Fused tile-level attention scores** ([#25](https://github.com/trnsci/trnsparse/issues/25)) —
130
+ **v0.4.4 ships the NKI kernel pair** (`_attn_stats_kernel` + `_attn_out_kernel`).
131
+ `block_sparse_attention_tiled` routes through NKI on the `nki` backend; the
132
+ PyTorch two-pass reference remains on the `pytorch` backend. `head_dim ≤ 128`
133
+ in v0.4.4 (nc_matmul partition limit); `head_dim=256` requires K-tiling.
134
+ - **Fused iterative solvers** ([#22](https://github.com/trnsci/trnsparse/issues/22)) —
135
+ CG blocked on `nl.affine_range` iteration-carried scalar state. **v0.4.3 ships
136
+ `chebyshev_bsr` and `richardson_bsr`**: fixed-K solvers with pre-computed
137
+ coefficients (no inner products in the loop) that have the correct structure for
138
+ eventual NKI fusion. The "A SBUF-resident across all iterations" win remains gated
139
+ on `affine_range` vector-carry support.
140
+ - **Multi-chip sharded BSR** ([#16](https://github.com/trnsci/trnsparse/issues/16)) —
141
+ gated on suite-level multi-chip collectives.
142
+ - **SpMV stays PyTorch.** Single output column on the Tensor Engine doesn't amortize
143
+ compile + dispatch overhead.
@@ -109,21 +109,28 @@ Three observations, all consistent with the architectural framing:
109
109
  dispatch, or where the kernel launch can be amortized across many
110
110
  operations.
111
111
 
112
- ### Where NKI is expected to win (follow-up issues)
113
-
114
- Not in raw "one-shot SpMM at N=128 vs CPU." The architectural exploits are:
115
-
116
- - **[#20 — on-chip iterative solvers](https://github.com/trnsci/trnsparse/issues/20)**:
117
- iterate `A @ x` thousands of times with A SBUF-resident. Kernel launch
118
- amortizes over all iterations.
119
- - **[#19 — fused screen + matmul](https://github.com/trnsci/trnsparse/issues/19)**:
120
- one kernel that fuses what's currently three HBM round-trips.
121
- - **[#21 — block-sparse attention](https://github.com/trnsci/trnsparse/issues/21)**:
122
- large-scale sequences where the matmul work dominates dispatch.
123
-
124
- v0.3.0 ships **BSR correctness + differentiability**. v0.4.0+ will cash
125
- in the architectural wins. See
126
- [`docs/architecture.md`](architecture.md) for why BSR is the
112
+ ### Where NKI wins — shipped v0.4.x
113
+
114
+ Not in raw "one-shot SpMM at N=128 vs CPU." The BSR table above measures
115
+ kernel dispatch overhead. The architectural wins come from fusing multiple
116
+ host passes into one dispatch:
117
+
118
+ - **`screened_spmm` (v0.4.0, [#19](https://github.com/trnsci/trnsparse/issues/19))**
119
+ ✅ shipped: fuses Schwarz bound + threshold + mask + matmul into one NKI
120
+ dispatch. On Fock-build-sized inputs: ~30–50% faster than the unfused
121
+ 4-step path (`schwarz_bounds → screen_quartets → from_dense → spmm`)
122
+ because it eliminates 3 HBM round-trips and the host-side CSR build.
123
+ - **Block-sparse attention (v0.4.2, [#21](https://github.com/trnsci/trnsparse/issues/21))**
124
+ ✅ shipped: `bsr_spmm` handles `attn_weights @ V` for local-window / dilated /
125
+ global-token masks. At seq_len=8192, window=2: 4.9% block density means 95%
126
+ of `nc_matmul` calls are skipped. Current example still materializes the full
127
+ score matrix (follow-up: [#25](https://github.com/trnsci/trnsparse/issues/25)).
128
+ - **[#22 — on-chip iterative solvers](https://github.com/trnsci/trnsparse/issues/22)**:
129
+ CG/power-iteration with A SBUF-resident across all iterations (replaces #20).
130
+ v0.3.2 ships the Python-loop plumbing; fused kernel gated on NKI scalar-carry
131
+ capability.
132
+
133
+ See [`docs/architecture.md`](architecture.md) for why BSR is the
127
134
  Trainium-native format.
128
135
 
129
136
  BSR results from commit