trnsparse 0.4.2__tar.gz → 0.7.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.
- {trnsparse-0.4.2 → trnsparse-0.7.0}/.github/workflows/ci.yml +3 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/.pre-commit-config.yaml +1 -1
- {trnsparse-0.4.2 → trnsparse-0.7.0}/CHANGELOG.md +208 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/CLAUDE.md +23 -22
- {trnsparse-0.4.2 → trnsparse-0.7.0}/PKG-INFO +22 -8
- {trnsparse-0.4.2 → trnsparse-0.7.0}/README.md +21 -7
- trnsparse-0.7.0/docs/architecture.md +143 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/benchmarks.md +22 -15
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/iterative_solvers.md +1 -1
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/migration_scipy.md +4 -4
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/quickstart.md +2 -2
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/sparse_attention.md +87 -15
- {trnsparse-0.4.2 → trnsparse-0.7.0}/examples/block_sparse_attention.py +25 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/pyproject.toml +1 -1
- trnsparse-0.7.0/tests/test_attention.py +327 -0
- trnsparse-0.7.0/tests/test_attn_gather.py +49 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_iterative.py +91 -0
- trnsparse-0.7.0/tests/test_nki_attn.py +201 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_nki_bsr.py +5 -1
- trnsparse-0.7.0/tests/test_nki_compile.py +143 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_nki_screened_spmm.py +5 -1
- trnsparse-0.7.0/tests/test_nki_sim.py +492 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_nki_spmm.py +5 -1
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse/__init__.py +15 -2
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse/iterative.py +150 -0
- trnsparse-0.7.0/trnsparse/nki/dispatch.py +994 -0
- trnsparse-0.7.0/trnsparse/nki/kernels.py +592 -0
- trnsparse-0.7.0/trnsparse/ops.py +625 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse.egg-info/PKG-INFO +22 -8
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse.egg-info/SOURCES.txt +3 -0
- trnsparse-0.4.2/docs/architecture.md +0 -109
- trnsparse-0.4.2/tests/test_attention.py +0 -93
- trnsparse-0.4.2/tests/test_nki_sim.py +0 -153
- trnsparse-0.4.2/trnsparse/nki/dispatch.py +0 -487
- trnsparse-0.4.2/trnsparse/nki/kernels.py +0 -192
- trnsparse-0.4.2/trnsparse/ops.py +0 -277
- {trnsparse-0.4.2 → trnsparse-0.7.0}/.github/workflows/notify-umbrella.yml +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/.github/workflows/publish.yml +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/.gitignore +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/CODE_OF_CONDUCT.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/CONTRIBUTING.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/LICENSE +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/benchmarks/bench_bsr_spmm.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/benchmarks/bench_iterative.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/benchmarks/bench_screening.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/benchmarks/bench_spmm.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/benchmarks/bench_spmv.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/benchmarks/conftest.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/api.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/aws_setup.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/index.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/docs/installation.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/examples/sparse_fock.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/infra/terraform/.terraform.lock.hcl +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/infra/terraform/README.md +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/infra/terraform/main.tf +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/mkdocs.yml +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/scripts/bench_to_md.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/scripts/run_benchmarks.sh +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/scripts/run_neuron_tests.sh +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/scripts/run_simulator_tests.sh +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/setup.cfg +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/conftest.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_bsr.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_examples.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_formats.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_ops.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/tests/test_screening.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse/formats.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse/nki/__init__.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse/screening.py +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse.egg-info/dependency_links.txt +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.0}/trnsparse.egg-info/requires.txt +0 -0
- {trnsparse-0.4.2 → trnsparse-0.7.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
|
|
@@ -5,6 +5,214 @@ 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
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.7.0] — 2026-10-28
|
|
11
|
+
|
|
12
|
+
Validated on Trn2 (trn2.3xlarge, Neuron SDK 2.33.0, native TorchNeuron device) — see trnsci/trnsci#35.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **NKI 0.7.0 / neuronx-cc 2.28 kernel compatibility (#29).** Kernels that
|
|
17
|
+
passed `nki.simulate` failed to compile on Trn2 (Neuron SDK 2.33):
|
|
18
|
+
- `_attn_out_kernel`, `_attn_bwd_dq_kernel`, `_attn_bwd_dkdv_kernel`:
|
|
19
|
+
softmax normalisation `nl.divide(exp, row_denom)` by a `(128, 1)`
|
|
20
|
+
denominator lowered to `tensor_scalar_arith`, which no longer supports
|
|
21
|
+
`divide`. Now multiplies by `nl.reciprocal(row_denom)` (hoisted per
|
|
22
|
+
block-row in the forward and dQ kernels).
|
|
23
|
+
- `_screened_spmm_kernel`: the `(128, 1) * (1, 128)` Schwarz outer product
|
|
24
|
+
and the `(1, 1)` threshold subtract produced partition-count mismatches
|
|
25
|
+
(`operand0 partition total elements 128 != dst partition total elements
|
|
26
|
+
1`). `q_k` is now `nl.broadcast_to` `(128, 128)` and the threshold is
|
|
27
|
+
loaded once and broadcast to `(128, 1)`. The soft-mask tensor-tensor
|
|
28
|
+
divide (fails ISA validation `s3s3d3_tt_valid_op`) is now a reciprocal
|
|
29
|
+
multiply.
|
|
30
|
+
- `nki_bsr_attn_bwd`: the host-side `(..., b) → (..., b, 1)` unsqueeze
|
|
31
|
+
for per-row stats matched `q_scaled` / `k_blocks` / `v_blocks` by shape
|
|
32
|
+
whenever `head_dim == 128` (and in the simulator's padded path), feeding
|
|
33
|
+
the kernels a 4-D tensor. Stats are now selected by name.
|
|
34
|
+
- Dispatch: kernels always receive fp32 (Trainium has no fp64 datapath;
|
|
35
|
+
the native device downcast fp64 silently) and results are cast back to
|
|
36
|
+
the caller's dtype. Fixes `expected m1 and m2 to have the same dtype,
|
|
37
|
+
but got: float != double` in the PyTorch-level backward under fp64
|
|
38
|
+
gradcheck. The hardware gradcheck tests now use `eps=1e-2,
|
|
39
|
+
atol=rtol=1e-2`, sized for an fp32 forward (the ops are linear in each
|
|
40
|
+
input, so the larger eps adds no truncation error).
|
|
41
|
+
|
|
42
|
+
- **Block-sparse attention: two host-side gather bugs** (pre-existing, masked
|
|
43
|
+
on Trn2 by the compile failures above; found with neuronx-cc BIRSim):
|
|
44
|
+
- Rows with fewer than `K_max` blocks were padded with sentinel k-slots that
|
|
45
|
+
gathered column block 0, so block 0 was counted again in the softmax and
|
|
46
|
+
in `weights @ V` (local-window masks: ~49% of outputs wrong). Padded slots
|
|
47
|
+
now have K/V (forward, dQ) or Q/dO (dK/dV) zeroed, and their stats are
|
|
48
|
+
dropped in `_attn_host_reduction`.
|
|
49
|
+
- `_attn_bwd_gather` gathered `dO` by column block instead of the row block
|
|
50
|
+
`dO_m`, breaking dQ whenever a row attends to a block other than itself
|
|
51
|
+
(~99% of dQ wrong). The two simulator xfails attributed to a "PSUM
|
|
52
|
+
accumulate bug" (`test_bwd_dq_parity_kmax2`, `test_backward_head_dim_256`)
|
|
53
|
+
were this bug and now pass.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- `tests/test_attn_gather.py`: CPU regression tests for both gather bugs.
|
|
58
|
+
- `tests/test_nki_compile.py` (`nki_simulator` marker): CPU MLIR
|
|
59
|
+
verification (`source_to_mlir(...).verify()`) of every kernel for trn1
|
|
60
|
+
and trn2, attention kernels at head_dim 64, 128 and 256; plus a full NEFF
|
|
61
|
+
compile per kernel/target when `neuronx-cc` is on PATH.
|
|
62
|
+
|
|
63
|
+
## [0.6.0] — 2026-04-17
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
|
|
67
|
+
- **K-tiling for head_dim > 128** — NKI attention kernels now support
|
|
68
|
+
`head_dim=256` and `head_dim=512` via an inner K-tile loop that splits
|
|
69
|
+
the `Q @ K.T` score computation across `TILE_K=128`-sized chunks of
|
|
70
|
+
`head_dim`, accumulating each into the existing PSUM.
|
|
71
|
+
- All 4 NKI kernels updated: `_attn_stats_kernel`, `_attn_out_kernel`,
|
|
72
|
+
`_attn_bwd_dq_kernel`, `_attn_bwd_dkdv_kernel`.
|
|
73
|
+
- For `head_dim ≤ 128`: single-tile path preserved unchanged — Q is
|
|
74
|
+
loaded once per block-row as a stationary tile (no regression).
|
|
75
|
+
- For `head_dim > 128`: K-tile inner loop (`affine_range(head_dim //
|
|
76
|
+
128)`). `head_dim` must be a multiple of 128 (covers 256, 512).
|
|
77
|
+
- `weights @ V` and all dK/dV accumulation matmuls are unchanged (K=128
|
|
78
|
+
block dimension, unaffected by head_dim growth).
|
|
79
|
+
- `tests/test_attention.py`: `test_head_dim_256_parity`,
|
|
80
|
+
`test_head_dim_256_dilated_parity`, `test_gradcheck_head_dim_256`,
|
|
81
|
+
`test_backward_shapes_head_dim_256` in PyTorch path.
|
|
82
|
+
- `tests/test_nki_sim.py`: `TestAttnKTilingSimulator` — simulator parity
|
|
83
|
+
tests for forward and backward at head_dim=256.
|
|
84
|
+
- `tests/test_nki_attn.py`: `TestAttnHardwareLargeDim` — hardware parity
|
|
85
|
+
tests at seq_len=512, head_dim=256.
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- `_attn_gather` assertion relaxed from `head_dim ≤ 128` to `head_dim ≤ 128
|
|
90
|
+
or head_dim % 128 == 0`.
|
|
91
|
+
- `block_sparse_attention_tiled` and `nki_bsr_attn_tiled` docstrings updated
|
|
92
|
+
to document K-tiling support.
|
|
93
|
+
|
|
94
|
+
## [0.5.1] — 2026-04-16
|
|
95
|
+
|
|
96
|
+
### Added
|
|
97
|
+
|
|
98
|
+
- **NKI backward kernel pair** (`_attn_bwd_dq_kernel` + `_attn_bwd_dkdv_kernel`)
|
|
99
|
+
in `trnsparse/nki/kernels.py`. When the `nki` backend is active,
|
|
100
|
+
`_AttnTiledFunction.backward` now dispatches to these kernels instead of
|
|
101
|
+
the Python-loop backward.
|
|
102
|
+
- `_attn_bwd_dq_kernel`: row-first traversal, mirrors `_attn_out_kernel`.
|
|
103
|
+
Accumulates `dQ_m += dS @ K_ki * scale` in a PSUM tile per block-row.
|
|
104
|
+
- `_attn_bwd_dkdv_kernel`: column-first traversal (BSC view built on host).
|
|
105
|
+
For each column block ki, iterates over all block-rows m that attend to ki,
|
|
106
|
+
accumulating `dK_ki += dS.T @ Q_m * scale` and `dV_ki += P.T @ dO_m`.
|
|
107
|
+
- No atomic scatter required — column-first gather on host eliminates NKI's
|
|
108
|
+
lack of cross-block accumulation primitives.
|
|
109
|
+
- **Stats saving in forward** — `_AttnTiledFunction.forward` now saves
|
|
110
|
+
`(Q, K, V, O, row_max, row_denom)` in ctx (6 tensors vs 4). Backward
|
|
111
|
+
receives the pre-computed row stats and skips the two recomputation passes.
|
|
112
|
+
Applies to both PyTorch and NKI paths.
|
|
113
|
+
- `_attn_bwd_gather` and `nki_bsr_attn_bwd` in `trnsparse/nki/dispatch.py`
|
|
114
|
+
— host-side BSC construction and NKI backward orchestration.
|
|
115
|
+
- `tests/test_nki_sim.py`: `TestAttnBwdSimulator` — simulator correctness tests
|
|
116
|
+
for dQ/dK/dV against PyTorch backward (local window + dilated patterns).
|
|
117
|
+
- `tests/test_nki_attn.py`: `TestAttnBwdHardware` — hardware parity tests
|
|
118
|
+
at seq_len=512, head_dim=64.
|
|
119
|
+
|
|
120
|
+
### Changed
|
|
121
|
+
|
|
122
|
+
- `nki_bsr_attn_tiled` accepts `return_stats=True` kwarg (default False) to
|
|
123
|
+
return `(out, row_max, row_denom)`.
|
|
124
|
+
- `_block_sparse_attn_pytorch` accepts `return_stats=True` kwarg similarly.
|
|
125
|
+
- `_block_sparse_attn_backward` accepts optional `row_max` / `row_denom`
|
|
126
|
+
kwargs; when provided, skips the two recomputation BSR passes.
|
|
127
|
+
|
|
128
|
+
## [0.5.0] — 2026-04-16
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
|
|
132
|
+
- **Block-sparse attention autograd** — `block_sparse_attention_tiled` is now
|
|
133
|
+
differentiable. When any of Q/K/V has `requires_grad=True`, the call routes
|
|
134
|
+
through `_AttnTiledFunction`, which provides a tiled backward using the Flash
|
|
135
|
+
Attention delta identity (`D_i = dO_i · O_i`).
|
|
136
|
+
- **`_block_sparse_attn_backward(Q, K, V, O, dO, mask_bsr, scale)`** — internal
|
|
137
|
+
backward function. Single pass over nonzero BSR blocks; no O(seq_len²)
|
|
138
|
+
intermediate. Row stats (row_max/row_denom) are recomputed in backward.
|
|
139
|
+
- `gradcheck` passes at `atol=1e-3` for local-window and dilated patterns
|
|
140
|
+
(float64, seq_len=256, head_dim=32). See `tests/test_attention.py::TestAttnTiledGrad`.
|
|
141
|
+
|
|
142
|
+
### Notes
|
|
143
|
+
|
|
144
|
+
- NKI backward kernel is a follow-up (v0.5.x). The current backward runs
|
|
145
|
+
on PyTorch even when the NKI forward path is active.
|
|
146
|
+
- Row stats (row_max/row_denom) are recomputed in backward rather than saved
|
|
147
|
+
from forward — avoids O(n_blocks × b) storage in the autograd graph at the
|
|
148
|
+
cost of two extra BSR passes.
|
|
149
|
+
|
|
150
|
+
## [0.4.4] — 2026-04-16
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
|
|
154
|
+
- **NKI attention kernel pair** (`_attn_stats_kernel` + `_attn_out_kernel`,
|
|
155
|
+
closes [#25](https://github.com/trnsci/trnsparse/issues/25)). Two-pass
|
|
156
|
+
block-sparse attention now runs on the Tensor Engine via these kernels when
|
|
157
|
+
the `nki` backend is active. The `pytorch` backend retains the Python-loop
|
|
158
|
+
reference path from v0.4.3.
|
|
159
|
+
- Pass 1 (`_attn_stats_kernel`): each `(m, ki)` block pair computed
|
|
160
|
+
independently via `nc_matmul`; `nl.max` / `nl.sum` within-tile reductions
|
|
161
|
+
produce `(tile_max, tile_sumexp)` with no carry between iterations.
|
|
162
|
+
- Pass 2 (`_attn_out_kernel`): recomputes scores, applies stable softmax
|
|
163
|
+
using `row_max` / `row_denom` loaded per block-row (static HBM offset),
|
|
164
|
+
accumulates `weights @ V` into a PSUM tile spanning all ki blocks.
|
|
165
|
+
- **Constraint:** `head_dim ≤ 128` (`nc_matmul` partition limit). `head_dim=256`
|
|
166
|
+
requires K-tiling and is a follow-up.
|
|
167
|
+
- `_attn_gather`, `_attn_host_reduction`, `nki_bsr_attn_tiled` in
|
|
168
|
+
`trnsparse/nki/dispatch.py` — host-side gather and orchestration for the kernel
|
|
169
|
+
pair, mirroring the `_bsr_pad_and_gather` pattern.
|
|
170
|
+
- `tests/test_nki_sim.py`: `TestAttnTiledSimulator` — simulator correctness
|
|
171
|
+
tests (local window, dilated; seq_len=256, head_dim=32).
|
|
172
|
+
- `tests/test_nki_attn.py`: hardware tests for local window, dilated, and
|
|
173
|
+
global-token patterns at seq_len=512, head_dim=64.
|
|
174
|
+
|
|
175
|
+
### Notes
|
|
176
|
+
|
|
177
|
+
No autograd wrapping in v0.4.4 — forward-only, matching the existing PyTorch
|
|
178
|
+
path. Attention backward (block-sparse) is a follow-up.
|
|
179
|
+
|
|
180
|
+
## [0.4.3] — 2026-04-16
|
|
181
|
+
|
|
182
|
+
### Added
|
|
183
|
+
|
|
184
|
+
- **`chebyshev_bsr(A, b, lam_min, lam_max, K)`** — fixed-K Chebyshev
|
|
185
|
+
semi-iteration. Pre-computes all step coefficients from eigenvalue bounds
|
|
186
|
+
before the loop; no inner products during iteration. Same convergence rate
|
|
187
|
+
as CG without the adaptive coefficient computation that blocks NKI fusion.
|
|
188
|
+
Returns `(x, K, rel_residual)` matching the `cg_bsr` signature.
|
|
189
|
+
- **`chebyshev_coeffs(lam_min, lam_max, K)`** — returns the `(alpha, beta)`
|
|
190
|
+
coefficient tensors for the momentum update `x_{k+1} = x_k + α_k r_k +
|
|
191
|
+
β_k (x_k - x_{k-1})`. Useful for inspecting or caching coefficients across
|
|
192
|
+
solves with the same matrix spectrum.
|
|
193
|
+
- **`richardson_bsr(A, b, omega, K)`** — fixed-K Richardson iteration; the
|
|
194
|
+
simplest fixed-point iteration with no coefficient computation at all.
|
|
195
|
+
Optimal omega = `2 / (lam_min + lam_max)`. Baseline comparator for Chebyshev.
|
|
196
|
+
- **`block_sparse_attention_tiled(Q, K, V, mask_bsr)`** — two-pass sparse
|
|
197
|
+
attention that avoids the O(seq_len²) score intermediate. Pass 1 computes
|
|
198
|
+
per-block `(tile_max, tile_sumexp)` statistics over nonzero blocks only
|
|
199
|
+
(O(n_blocks × block_size) stats array, e.g. 5 KB at seq_len=512 vs 1 MB
|
|
200
|
+
dense). Pass 2 recomputes scores, applies stable softmax using host-reduced
|
|
201
|
+
row statistics, and accumulates V. PyTorch reference for the NKI kernel pair
|
|
202
|
+
documented in [#25](https://github.com/trnsci/trnsparse/issues/25).
|
|
203
|
+
- **`docs/sparse_attention.md`**: new "Tiled two-pass path" section explaining
|
|
204
|
+
the algorithm, memory trade-offs, and the NKI kernel follow-up path.
|
|
205
|
+
- **`docs/architecture.md`**: "Known limits" updated — #22 and #25 now have
|
|
206
|
+
v0.4.3 PyTorch references with clear notes on remaining NKI gate conditions.
|
|
207
|
+
|
|
208
|
+
### Notes
|
|
209
|
+
|
|
210
|
+
Addresses the architecture-friendly workarounds identified in the analysis of
|
|
211
|
+
parked NKI-capability-gated issues (#22, #25). Both solvers and the tiled
|
|
212
|
+
attention path share the structural property of fixed-iteration loops with
|
|
213
|
+
statically-determined coefficients — the pattern that maps to `nl.affine_range`
|
|
214
|
+
without scalar carry.
|
|
215
|
+
|
|
8
216
|
## [0.4.2] — 2026-04-15
|
|
9
217
|
|
|
10
218
|
### 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,
|
|
23
|
-
│ ├── ops.py # spmv, spmm,
|
|
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
|
-
│
|
|
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
|
-
│
|
|
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
|
|
37
|
+
## NKI compute strategy (v0.4.x current)
|
|
37
38
|
|
|
38
|
-
**
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
2.
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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.
|
|
3
|
+
Version: 0.7.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
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
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 —
|
|
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
|
|
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 |
|
|
@@ -16,12 +16,17 @@ CSR/COO formats, SpMV, SpMM, and integral screening for sparse scientific comput
|
|
|
16
16
|
|
|
17
17
|
trnsparse follows the [trnsci 5-phase roadmap](https://trnsci.dev/roadmap/). Active work is tracked in phase-labeled GitHub issues:
|
|
18
18
|
|
|
19
|
-
- **[Phase 1 — correctness](https://github.com/trnsci/trnsparse/issues/14)** ✅ v0.2.0: NKI SpMM validated on trn1 via densify-then-GEMM
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
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.
|
|
22
27
|
- **[Phase 5 — generation](https://github.com/trnsci/trnsparse/issues/17)**: trn2 DMA bandwidth exploitation.
|
|
23
28
|
|
|
24
|
-
_(No Phase 2 for trnsparse —
|
|
29
|
+
_(No Phase 2 for trnsparse — precision inherited from trnblas.)_
|
|
25
30
|
|
|
26
31
|
Suite-wide tracker: [trnsci/trnsci#1](https://github.com/trnsci/trnsci/issues/1).
|
|
27
32
|
|
|
@@ -58,12 +63,21 @@ stats = trnsparse.sparsity_stats(Q)
|
|
|
58
63
|
|
|
59
64
|
| Operation | Description |
|
|
60
65
|
|-----------|-------------|
|
|
61
|
-
| `spmv` | Sparse × dense vector |
|
|
62
|
-
| `spmm` | Sparse × dense matrix |
|
|
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) |
|
|
63
70
|
| `spmv_symmetric` | Symmetric SpMV (half storage) |
|
|
64
71
|
| `sparse_add` | C = αA + βB |
|
|
65
72
|
| `sparse_scale` | B = αA |
|
|
66
|
-
| `sparse_transpose` | 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 |
|
|
67
81
|
| `schwarz_bounds` | Schwarz screening bounds |
|
|
68
82
|
| `screen_quartets` | Shell quartet significance mask |
|
|
69
83
|
| `density_screen` | Density-weighted screening |
|
|
@@ -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
|
|
113
|
-
|
|
114
|
-
Not in raw "one-shot SpMM at N=128 vs CPU." The
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
@@ -65,7 +65,7 @@ def _cg_spd_kernel(A_blocks, A_cols, A_row_ptrs, b, max_iter):
|
|
|
65
65
|
rr = nl.reduce(r * r)
|
|
66
66
|
|
|
67
67
|
for k in nl.affine_range(max_iter):
|
|
68
|
-
Ap = _bsr_matvec_sbuf(A_sbuf, A_cols, A_row_ptrs, p)
|
|
68
|
+
Ap = _bsr_matvec_sbuf(A_sbuf, A_cols, A_row_ptrs, p) # all SBUF
|
|
69
69
|
pAp = nl.reduce(p * Ap)
|
|
70
70
|
alpha = rr / pAp
|
|
71
71
|
x = x + alpha * p
|
|
@@ -60,10 +60,10 @@ argument; scipy infers it from the indices when possible. Be explicit:
|
|
|
60
60
|
|
|
61
61
|
```python
|
|
62
62
|
# scipy
|
|
63
|
-
A = csr_matrix((data, indices, indptr))
|
|
63
|
+
A = csr_matrix((data, indices, indptr)) # shape inferred
|
|
64
64
|
|
|
65
65
|
# trnsparse
|
|
66
|
-
A = CSRMatrix(data, indices, indptr, shape=(m, n))
|
|
66
|
+
A = CSRMatrix(data, indices, indptr, shape=(m, n)) # shape required
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
**Operator access.** scipy overloads `@`, `+`, `*` on its matrix
|
|
@@ -107,9 +107,9 @@ import torch
|
|
|
107
107
|
import trnsparse
|
|
108
108
|
import trnsolver
|
|
109
109
|
|
|
110
|
-
A = trnsparse.from_scipy(scipy_matrix)
|
|
110
|
+
A = trnsparse.from_scipy(scipy_matrix) # zero-copy ingestion
|
|
111
111
|
b = torch.as_tensor(b_numpy, dtype=torch.float32)
|
|
112
|
-
x = trnsolver.cg(A, b, tol=1e-6)
|
|
112
|
+
x = trnsolver.cg(A, b, tol=1e-6) # uses trnsparse.spmv
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
The hot path (`spmv` inside CG) dispatches to NKI when
|
|
@@ -42,7 +42,7 @@ keep = trnsparse.screen_quartets(Q, threshold=1e-10)
|
|
|
42
42
|
## Backend selection
|
|
43
43
|
|
|
44
44
|
```python
|
|
45
|
-
trnsparse.set_backend("auto")
|
|
45
|
+
trnsparse.set_backend("auto") # default
|
|
46
46
|
trnsparse.set_backend("pytorch") # force PyTorch fallback
|
|
47
|
-
trnsparse.set_backend("nki")
|
|
47
|
+
trnsparse.set_backend("nki") # force NKI (requires Neuron hardware)
|
|
48
48
|
```
|