trntensor 0.3.0__tar.gz → 0.17.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 (81) hide show
  1. trntensor-0.17.0/CHANGELOG.md +686 -0
  2. {trntensor-0.3.0/trntensor.egg-info → trntensor-0.17.0}/PKG-INFO +6 -5
  3. {trntensor-0.3.0 → trntensor-0.17.0}/README.md +5 -4
  4. trntensor-0.17.0/docs/api/decompose.md +106 -0
  5. trntensor-0.17.0/docs/api/einsum.md +175 -0
  6. trntensor-0.17.0/docs/api/plan.md +96 -0
  7. {trntensor-0.3.0 → trntensor-0.17.0}/docs/api/quantum.md +1 -1
  8. {trntensor-0.3.0 → trntensor-0.17.0}/docs/architecture.md +34 -3
  9. {trntensor-0.3.0 → trntensor-0.17.0}/docs/contraction_patterns.md +7 -8
  10. trntensor-0.17.0/docs/design/matmul_dispatch_findings.md +149 -0
  11. trntensor-0.17.0/docs/design/matmul_kernel_profile.md +159 -0
  12. {trntensor-0.3.0 → trntensor-0.17.0}/docs/quickstart.md +6 -6
  13. {trntensor-0.3.0 → trntensor-0.17.0}/docs/tutorial_df_mp2.md +2 -4
  14. {trntensor-0.3.0 → trntensor-0.17.0}/infra/terraform/main.tf +3 -1
  15. {trntensor-0.3.0 → trntensor-0.17.0}/pyproject.toml +5 -2
  16. trntensor-0.17.0/scripts/autotune_dispatch.py +233 -0
  17. trntensor-0.17.0/scripts/run_neuron_profile.sh +469 -0
  18. trntensor-0.17.0/scripts/run_reduce_parallel_multirank.py +527 -0
  19. trntensor-0.17.0/tests/conftest.py +43 -0
  20. trntensor-0.17.0/tests/test_einsum.py +788 -0
  21. trntensor-0.17.0/tests/test_nki_compile.py +91 -0
  22. {trntensor-0.3.0 → trntensor-0.17.0}/tests/test_nki_kernels.py +54 -7
  23. {trntensor-0.3.0 → trntensor-0.17.0}/tests/test_nki_sim.py +111 -1
  24. trntensor-0.17.0/tests/test_parallel.py +451 -0
  25. {trntensor-0.3.0 → trntensor-0.17.0}/tests/test_plan.py +27 -1
  26. trntensor-0.17.0/tests/test_sr_runtime.py +163 -0
  27. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor/__init__.py +9 -1
  28. trntensor-0.17.0/trntensor/einsum.py +637 -0
  29. trntensor-0.17.0/trntensor/nki/_kernels.py +362 -0
  30. trntensor-0.17.0/trntensor/nki/collectives.py +97 -0
  31. trntensor-0.17.0/trntensor/nki/dispatch.py +547 -0
  32. trntensor-0.17.0/trntensor/parallel.py +327 -0
  33. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor/plan.py +178 -8
  34. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor/quantum.py +4 -25
  35. {trntensor-0.3.0 → trntensor-0.17.0/trntensor.egg-info}/PKG-INFO +6 -5
  36. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor.egg-info/SOURCES.txt +10 -0
  37. {trntensor-0.3.0 → trntensor-0.17.0}/uv.lock +1 -1
  38. trntensor-0.3.0/CHANGELOG.md +0 -201
  39. trntensor-0.3.0/docs/api/decompose.md +0 -41
  40. trntensor-0.3.0/docs/api/einsum.md +0 -43
  41. trntensor-0.3.0/docs/api/plan.md +0 -36
  42. trntensor-0.3.0/tests/conftest.py +0 -12
  43. trntensor-0.3.0/tests/test_einsum.py +0 -293
  44. trntensor-0.3.0/trntensor/einsum.py +0 -228
  45. trntensor-0.3.0/trntensor/nki/_kernels.py +0 -296
  46. trntensor-0.3.0/trntensor/nki/dispatch.py +0 -343
  47. {trntensor-0.3.0 → trntensor-0.17.0}/.github/workflows/ci.yml +0 -0
  48. {trntensor-0.3.0 → trntensor-0.17.0}/.github/workflows/notify-umbrella.yml +0 -0
  49. {trntensor-0.3.0 → trntensor-0.17.0}/.github/workflows/publish.yml +0 -0
  50. {trntensor-0.3.0 → trntensor-0.17.0}/.gitignore +0 -0
  51. {trntensor-0.3.0 → trntensor-0.17.0}/.pre-commit-config.yaml +0 -0
  52. {trntensor-0.3.0 → trntensor-0.17.0}/CLAUDE.md +0 -0
  53. {trntensor-0.3.0 → trntensor-0.17.0}/CODE_OF_CONDUCT.md +0 -0
  54. {trntensor-0.3.0 → trntensor-0.17.0}/CONTRIBUTING.md +0 -0
  55. {trntensor-0.3.0 → trntensor-0.17.0}/LICENSE +0 -0
  56. {trntensor-0.3.0 → trntensor-0.17.0}/benchmarks/__init__.py +0 -0
  57. {trntensor-0.3.0 → trntensor-0.17.0}/benchmarks/bench_einsum.py +0 -0
  58. {trntensor-0.3.0 → trntensor-0.17.0}/docs/api/index.md +0 -0
  59. {trntensor-0.3.0 → trntensor-0.17.0}/docs/api/nki.md +0 -0
  60. {trntensor-0.3.0 → trntensor-0.17.0}/docs/aws_setup.md +0 -0
  61. {trntensor-0.3.0 → trntensor-0.17.0}/docs/benchmarks.md +0 -0
  62. {trntensor-0.3.0 → trntensor-0.17.0}/docs/developing_kernels.md +0 -0
  63. {trntensor-0.3.0 → trntensor-0.17.0}/docs/index.md +0 -0
  64. {trntensor-0.3.0 → trntensor-0.17.0}/docs/installation.md +0 -0
  65. {trntensor-0.3.0 → trntensor-0.17.0}/examples/df_mp2_einsum.py +0 -0
  66. {trntensor-0.3.0 → trntensor-0.17.0}/infra/terraform/README.md +0 -0
  67. {trntensor-0.3.0 → trntensor-0.17.0}/mkdocs.yml +0 -0
  68. {trntensor-0.3.0 → trntensor-0.17.0}/scripts/bench_to_md.py +0 -0
  69. {trntensor-0.3.0 → trntensor-0.17.0}/scripts/run_benchmarks.sh +0 -0
  70. {trntensor-0.3.0 → trntensor-0.17.0}/scripts/run_neuron_tests.sh +0 -0
  71. {trntensor-0.3.0 → trntensor-0.17.0}/scripts/run_simulator_tests.sh +0 -0
  72. {trntensor-0.3.0 → trntensor-0.17.0}/setup.cfg +0 -0
  73. {trntensor-0.3.0 → trntensor-0.17.0}/tests/test_decompose.py +0 -0
  74. {trntensor-0.3.0 → trntensor-0.17.0}/tests/test_nki.py +0 -0
  75. {trntensor-0.3.0 → trntensor-0.17.0}/tests/test_quantum.py +0 -0
  76. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor/decompose.py +0 -0
  77. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor/nki/__init__.py +0 -0
  78. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor/py.typed +0 -0
  79. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor.egg-info/dependency_links.txt +0 -0
  80. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor.egg-info/requires.txt +0 -0
  81. {trntensor-0.3.0 → trntensor-0.17.0}/trntensor.egg-info/top_level.txt +0 -0
@@ -0,0 +1,686 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.17.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
+ ### Added
15
+
16
+ - **Reduce-parallel sharding on real multi-rank all-reduce (Phase 4 prototype).**
17
+ Before this change, reduce-parallel and mixed output/reduce-parallel einsums
18
+ raised `NotImplementedError` whenever `HAS_NKI` was true.
19
+ - Under an initialized `torch.distributed` group with
20
+ `world_size == n_shards > 1`, rank `r` now computes only shard `r`'s
21
+ partial on its own device. The partials are combined with a real
22
+ all-reduce. Single-process runs keep `_mock_allreduce`.
23
+ - The backend is chosen by `trntensor.parallel.allreduce_backend`. `auto`
24
+ selects `nki` under a `"neuron"` process group, `torch` under any other
25
+ group, and `mock` otherwise. Override it with
26
+ `TRNTENSOR_ALLREDUCE=mock|torch|nki|auto` or `parallel.set_allreduce_backend()`.
27
+ An explicit `torch`/`nki` request that cannot be honored raises
28
+ `RuntimeError`; it never falls back to replicated compute.
29
+ - `trntensor.nki.collectives.nki_all_reduce_sum` is an `@nki.jit` kernel
30
+ using `nki.collectives.all_reduce(op=add)` on named `shared_hbm` buffers,
31
+ invoked via `torch_neuronx.wrap_nki(k)[(lnc,)]`. This is the pattern
32
+ measured bit-exact on Trn2 (SDK 2.33, NKI 0.7.0, LNC=2) in trnsci spike B4.
33
+ `private_hbm` gave wrong values at LNC=2.
34
+ - `scripts/run_reduce_parallel_multirank.py` runs multi-rank checks: one
35
+ process per rank, core pinning, hard timeouts, and per-rank logs. It
36
+ checks `ij,jk->ik` with `j` sharded at n = 512–2048 against the
37
+ single-device result (fp32 rel err ≤ 1e-5) and requires all ranks to
38
+ agree. It also reports bit-exactness against a fixed-order sum, plus
39
+ timings. Pass `--cpu` for a gloo pre-flight. Rank cores must be an
40
+ aligned pair or quad (0-1, 2-3, 0-3).
41
+
42
+ ### Fixed
43
+
44
+ - **Precision promises (Trn2 spike B5, #42).**
45
+ - `precision="kahan"` on Neuron-device (or other non-fp64) operands now
46
+ contracts on the host in true fp64 and moves the result back. Before,
47
+ the "fp64" einsum ran on the device, which has no fp64 datapath. On Trn2
48
+ fp32 results measured ~1.4 ulp (max componentwise 8.4e-8 at N=128),
49
+ above the K·2⁻⁵³ + u bound that a real fp64 accumulation plus one
50
+ rounding satisfies. The bound was right; the extra rounding was the bug.
51
+ - Error bounds now include the final rounding of the result to its dtype
52
+ (`u_out`): `"fast"` K·u + u_out, `"sr"` √K·u + u_out, `"dd"` K·u² + u_out,
53
+ `"kahan"` K·2⁻⁵³ + u_out. `select_precision_for_error` selects on these
54
+ bounds. When the target is below the floor (≈ 3.9e-3 for bf16 results,
55
+ 6.0e-8 for fp32), it emits a one-time `RuntimeWarning` and returns
56
+ `"kahan"` instead of silently missing the target. It does not raise.
57
+ Targets below K·2⁻⁵³ still raise `ValueError`. Because of the `u_out`
58
+ term, some bf16 targets near 2⁻⁸ now select a more accurate mode than
59
+ before.
60
+
61
+ - **NKI 0.7 / neuronx-cc 2.28 kernel compile on Trn2 (#41).**
62
+ - `ao_to_mo_transform_kernel`: step 1 now computes the intermediate
63
+ already transposed (`eri` tile as the stationary operand, ν-major
64
+ scratch), so step 2 loads its stationary tile with a plain DMA. The
65
+ former `nl.load_transpose2d` of a strided HBM slice was rejected by the
66
+ BIR verifier (`checkDMATranspose`: "transposing dimensions should be
67
+ continuous"). The ν loop is now `TILE_K`-wide, so the dispatcher pads
68
+ `nbasis` to a multiple of 128 instead of 512.
69
+ - `mp2_energy_kernel`: removed the `(128, 1) → (1, 128)` reshape
70
+ ("Partition dim size must be preserved") and the cross-partition
71
+ `nl.sum(axis=(0, 1))`. The dispatcher now passes `B` as
72
+ `(nocc, naux, nvir)` (plain loads, no DMA transposes) and ε_occ / ε_vir
73
+ pre-broadcast across the 128 partitions; the kernel row-reduces per
74
+ lane and finishes with a `accᵀ · ones` matmul on the Tensor Engine.
75
+ - `matmul_kernel` / `batched_matmul_kernel` `use_sr=True`: NKI 0.7's
76
+ `nisa.activation` has no `out_dtype` / `round_mode` kwargs; the
77
+ downcast now calls `nisa.activation(dst=..., op=nl.copy, data=psum)`.
78
+ Stochastic rounding is a runtime setting on this toolchain, not a
79
+ per-instruction one.
80
+ - `tests/test_nki_kernels.py`: replaced the stale `test_oversize_raises`
81
+ (nvir > 128 no longer raises since M-tiling landed) with a correctness
82
+ check at nvir = 200.
83
+
84
+ ### Added
85
+
86
+ - `trntensor.forward_error_bound(mode, dtype, K)`: the componentwise error
87
+ bound each precision mode promises, including output rounding (#42).
88
+ - **Stochastic-rounding runtime guard (#41).** On NKI 0.7 (Neuron SDK
89
+ 2.33) the `use_sr` flag no longer chooses the rounding mode. On Trn2,
90
+ with `NEURON_RT_STOCHASTIC_ROUNDING_EN` unset or `0`, both
91
+ `use_sr=True` and `use_sr=False` round to nearest even and are
92
+ deterministic. With `=1`, both round stochastically. The Neuron Runtime
93
+ sets the mode for the whole process when it initializes.
94
+ `nki_matmul` / `nki_batched_matmul` now emit a one-time `RuntimeWarning`
95
+ when SR is requested on the native NKI path without
96
+ `NEURON_RT_STOCHASTIC_ROUNDING_EN=1`, then still compute
97
+ (round-to-nearest). This covers `precision="sr"` in `einsum` /
98
+ `multi_einsum` and `target_forward_error=` when it picks `"sr"`. The CPU
99
+ fallback and `nki.simulate` paths are unaffected. New
100
+ `trntensor.nki.dispatch.sr_runtime_enabled()`.
101
+ - Docs (`docs/api/einsum.md`, `docs/api/plan.md`) and docstrings explain
102
+ that the variable has to be set at process start, and that with it set
103
+ *every* downcast in the process rounds stochastically, including
104
+ `precision="fast"`.
105
+ - Tests: `tests/test_sr_runtime.py` (CPU, native path stubbed);
106
+ hardware `TestStochasticRoundingRuntime` in `tests/test_nki_kernels.py`
107
+ (markers `neuron` + `neuron_sr`), gated by the `sr_runtime` fixture.
108
+ The fixture skips unless the variable was `1` at process start.
109
+ - `tests/test_nki_compile.py` (`nki_simulator` marker): runs the NKI
110
+ frontend (trace → MLIR → verify) on CPU for every kernel at trn1, trn2,
111
+ and trn2 lnc=2. Catches the verifier errors `nki.simulate` misses.
112
+
113
+ ## [0.16.0] — 2026-04-29
114
+
115
+ ### Added
116
+
117
+ - **`target_forward_error=` for `multi_einsum()`** — extends the automatic
118
+ precision contract from v0.15.0 to multi-contraction calls.
119
+
120
+ For the per-contraction fallback path, each ``einsum()`` call resolves its
121
+ own K independently, so heterogeneous contractions (different subscripts or
122
+ operand shapes) each get the cheapest mode that satisfies the target for
123
+ *their* K. For the homogeneous batching path (same subscript, same shapes),
124
+ the mode is resolved once from the first contraction's K and dtype — which
125
+ equals all others in the batch.
126
+
127
+ Mutually exclusive with explicit ``precision=``, same as ``einsum()``.
128
+
129
+ ## [0.15.0] — 2026-04-27
130
+
131
+ ### Added
132
+
133
+ - **`target_forward_error=` kwarg for `einsum()`** — automatic precision
134
+ selection. Instead of specifying a precision mode manually, callers supply
135
+ a relative forward error bound and trntensor picks the cheapest mode whose
136
+ theoretical error bound satisfies it:
137
+
138
+ ```python
139
+ einsum("ij,jk->ik", A, B, target_forward_error=1e-4)
140
+ ```
141
+
142
+ Selection order (cheapest first): ``"fast"`` → ``"sr"`` → ``"dd"`` →
143
+ ``"kahan"``. Error bounds used (Higham 2002 §3, Connolly–Higham–Mary 2021):
144
+
145
+ - ``"fast"``: K · u (Wilkinson worst-case)
146
+ - ``"sr"``: √K · u (expected; mean-zero — statistical, not worst-case)
147
+ - ``"dd"``: K · u² (Ozaki-2 worst-case)
148
+ - ``"kahan"``: K · u_f64 (FP64 Wilkinson worst-case)
149
+
150
+ Where K = product of contracted dimension sizes and u = unit roundoff for
151
+ the operand dtype (BF16: 2⁻⁸, FP16: 2⁻¹¹, FP32: 2⁻²⁴, FP64: 2⁻⁵³).
152
+ ``"sr"`` and ``"dd"`` are only considered for BF16 and FP16 inputs.
153
+
154
+ Raises ``ValueError`` when both ``precision=`` and ``target_forward_error=``
155
+ are given, or when no available mode can achieve the target.
156
+
157
+ - **`select_precision_for_error(dtype, K, target_forward_error)`** — the
158
+ selector as a public API function (``trntensor.select_precision_for_error``).
159
+ Useful for inspecting which mode would be chosen without running the full
160
+ contraction.
161
+
162
+ ### Notes
163
+
164
+ ``target_forward_error=`` is a first step toward the suite-level
165
+ ``target_forward_error`` contract described in the architecture brief. The
166
+ current implementation uses static bounds (Wilkinson/Connolly–Higham–Mary);
167
+ adaptive error estimation (measuring actual residuals) is future work.
168
+
169
+ ## [0.14.0] — 2026-04-24
170
+
171
+ ### Added
172
+
173
+ - **`precision="dd"` on CPU — Ozaki-2 splitting** — lifts the unconditional
174
+ ``NotImplementedError`` for double-double accumulation on CPU.
175
+
176
+ ``_ozaki_split_cpu(A, k)`` splits a FP32 tensor into ``k`` BF16-precision
177
+ summands using the Ogita–Rump–Oishi error-free split: at each step,
178
+ ``s = round_bfloat16(remainder)`` and ``remainder -= s`` (exact by Sterbenz's
179
+ lemma since ``|remainder|/|s| ∈ [0.5, 2]``). ``_ozaki_matmul_cpu(A, B, k=2)``
180
+ computes ``k²`` FP32 matmuls of the summand pairs and sums in FP32, achieving
181
+ forward error O(K · u_bf² · |C|) vs O(K · u_bf · |C|) for a single BF16
182
+ matmul — substantially more accurate for large K.
183
+
184
+ On CPU (``HAS_NKI=False``): ``precision="dd"`` routes the ``matmul`` strategy
185
+ through ``_ozaki_matmul_cpu``; the ``path`` strategy propagates ``precision="dd"``
186
+ recursively so each binary matmul step uses Ozaki; ``bmm`` and ``torch``
187
+ strategies fall back to kahan (fp64 promotion).
188
+
189
+ On Trainium (``HAS_NKI=True``): ``NotImplementedError`` preserved — the fused
190
+ NKI version requires trnblas Phase 2 double-double GEMM kernels (trnblas#22),
191
+ which hold both the main and correction PSUM tiles simultaneously in one NKI
192
+ program rather than dispatching four separate kernel calls.
193
+
194
+ Same named-mock pattern as ``_stochastic_round_cpu`` (SR, v0.11.0) and
195
+ ``_mock_allreduce`` (reduce-parallel, v0.10.0).
196
+
197
+ ### Notes
198
+
199
+ The remaining ``NotImplementedError`` for ``precision="dd"`` on Trainium gates
200
+ on trnblas#22. The CPU contract is now fully testable.
201
+
202
+ ## [0.13.0] — 2026-04-22
203
+
204
+ ### Fixed
205
+
206
+ - **`precision="sr"` now honored for the `bmm` strategy** — `_execute_bmm`
207
+ was calling ``nki_batched_matmul(A, B)`` without ``use_sr``, ignoring
208
+ ``plan.precision``. The NKI kernel already supported ``use_sr`` since
209
+ v0.11.0; the dispatch layer just didn't thread it through.
210
+
211
+ ### Added
212
+
213
+ - **`precision=` kwarg for `multi_einsum()`** — all contractions in a
214
+ ``multi_einsum`` call now share the same precision mode. The homogeneous
215
+ batching path (``_try_batched_multi_einsum``) passes ``use_sr`` to
216
+ ``nki_batched_matmul`` when ``precision="sr"``; ``"kahan"`` and ``"dd"``
217
+ fall through to the per-contraction loop which handles them correctly.
218
+
219
+ ### Notes
220
+
221
+ ``precision=`` is now uniformly honored across all dispatch paths: single
222
+ matmul (`einsum` → `_execute_matmul`), batched matmul (`einsum` →
223
+ `_execute_bmm`), path execution (`einsum` → `_execute_path` → recursive
224
+ `einsum`), and multi-contraction (`multi_einsum`).
225
+
226
+ ## [0.12.0] — 2026-04-22
227
+
228
+ ### Added
229
+
230
+ - **Mixed output-parallel + reduce-parallel sharding** — ``einsum()`` now
231
+ supports einsums where some ``ShardedTensor`` operands are partitioned along
232
+ an output dimension (output-parallel) and others along a contracted dimension
233
+ (reduce-parallel) in the same call.
234
+
235
+ The dispatch uses a nested loop: for each output shard ``s``, partial
236
+ contractions over all reduce shards are accumulated via
237
+ :func:`~trntensor.parallel._mock_allreduce` and the result becomes output
238
+ chunk ``s``. The return value is a ``ShardedTensor`` partitioned along the
239
+ output dimension.
240
+
241
+ Operands that are neither sharded but share the contracted index are
242
+ co-partitioned automatically (same behavior as pure reduce-parallel).
243
+ Output-parallel operand chunks are also co-partitioned along the contracted
244
+ index when they span it.
245
+
246
+ On Trainium (``HAS_NKI=True``) the gate is unchanged — the inner all-reduce
247
+ still requires ``nki.collectives`` (Neuron SDK 2.30+).
248
+
249
+ 3-operand and higher chains work naturally: the inner recursive ``einsum()``
250
+ call receives dense operands and dispatches through the normal path.
251
+
252
+ ### Notes
253
+
254
+ Mixed output-parallel + reduce-parallel with ``HAS_NKI=True`` raises
255
+ ``NotImplementedError`` with the same ``nki.collectives`` message as pure
256
+ reduce-parallel. SDK 2.30+ unblocks both simultaneously.
257
+
258
+ ## [0.11.0] — 2026-04-21
259
+
260
+ ### Added
261
+
262
+ - **`precision="sr"` — stochastic rounding at the PSUM→SBUF boundary** —
263
+ adds a fourth precision mode to ``einsum()`` and ``plan_contraction()``.
264
+
265
+ On Trainium, ``matmul_kernel`` and ``batched_matmul_kernel`` replace the
266
+ ``nisa.tensor_copy(psum → c_sbuf)`` downcast with
267
+ ``nisa.activation(..., round_mode="stochastic")``. This rounds each FP32
268
+ PSUM element to its upper or lower BF16 neighbor with probability
269
+ proportional to the fractional distance — mean rounding error is zero
270
+ (Connolly–Higham–Mary, *SIAM J. Sci. Comput.*, 2021), replacing Wilkinson's
271
+ worst-case n·u bias with a √n·u bound.
272
+
273
+ On CPU (``HAS_NKI=False``), ``_stochastic_round_cpu`` applies the same
274
+ rounding to the ``torch.matmul`` output, enabling ``precision="sr"`` to be
275
+ tested in CPU-only CI. This mirrors the ``_mock_allreduce`` pattern from
276
+ v0.10.0: a named, documented stand-in that makes the hardware path testable
277
+ without hardware.
278
+
279
+ ``"sr"`` applies only to the ``matmul`` strategy. For ``"bmm"``,
280
+ ``"torch"``, and ``"path"`` strategies, ``precision="sr"`` is accepted
281
+ without error and uses fast (round-nearest) rounding.
282
+
283
+ **Correctness note**: for BF16 dot products with K ≥ ~300, stochastic
284
+ rounding is a *correctness requirement* for convergent Krylov iteration
285
+ (without SR, BF16 Krylov stagnates). Use ``precision="sr"`` when K is
286
+ large or accumulation bias matters.
287
+
288
+ ### Notes
289
+
290
+ Mixed output-parallel + reduce-parallel operands in the same einsum remain
291
+ deferred to v0.12.0.
292
+
293
+ ## [0.10.0] — 2026-04-21
294
+
295
+ ### Added
296
+
297
+ - **Reduce-parallel sharding on CPU** — the ``NotImplementedError`` gate for
298
+ contracted-dimension sharding is now conditional on ``HAS_NKI``. On CPU
299
+ (``HAS_NKI=False``) reduce-parallel contractions dispatch through a mock
300
+ all-reduce (torch-based sum of per-shard partial results), making the full
301
+ sharding API testable in CI without hardware.
302
+
303
+ On Trainium (``HAS_NKI=True``) the gate is unchanged — ``nki.collectives``
304
+ is required for production reduce-parallel, arriving in Neuron SDK 2.30+.
305
+
306
+ - **Co-partitioning of plain-tensor operands** — when a reduce-parallel
307
+ ``ShardedTensor`` operand partitions along a contracted index, ``einsum()``
308
+ automatically slices any non-sharded operand that shares that index along
309
+ the matching dimension per shard. No API change required; callers can mix
310
+ sharded and dense operands in the same reduce-parallel einsum.
311
+
312
+ - **``shard_boundaries(st)``** in ``trntensor.parallel`` — returns the
313
+ ``(start, end)`` slice pairs for each shard. Used internally by the dispatch
314
+ layer; also useful for callers that need to manually co-partition ancillary
315
+ data structures.
316
+
317
+ - **``_mock_allreduce(partials)``** in ``trntensor.parallel`` — sums a list of
318
+ same-shape tensors element-wise. Named and documented to clearly identify the
319
+ operation that ``nki.collectives.allreduce`` will replace on hardware.
320
+
321
+ ### Notes
322
+
323
+ Mixed output-parallel + reduce-parallel operands in the same einsum are not
324
+ yet supported and raise ``NotImplementedError``. This covers an edge case not
325
+ present in the DF-MP2 patterns; deferred to v0.11.0.
326
+
327
+ ## [0.9.0] — 2026-04-18
328
+
329
+ ### Added
330
+
331
+ - **`trntensor.parallel` module** — Phase 4 foundation for output-parallel tensor
332
+ contractions. Introduces ``ShardedTensor``, ``scatter``, and ``gather``:
333
+ - ``ShardedTensor``: wraps a list of dense chunk tensors split along a single
334
+ ``partition_dim``. Carries the partition spec so ``einsum()`` can detect it
335
+ and classify the sharding mode.
336
+ - ``scatter(tensor, dim, n_shards)``: splits a dense tensor evenly along
337
+ ``dim``; last shard absorbs any remainder.
338
+ - ``gather(st)``: reconstructs the dense tensor via ``torch.cat``.
339
+
340
+ - **Output-parallel sharding in ``einsum()``**: when any operand is a
341
+ ``ShardedTensor``, ``einsum()`` checks whether the partition dimension maps to
342
+ an output index (output-parallel). If so, each shard's contraction is dispatched
343
+ independently (no cross-shard communication) and the result is returned as a
344
+ ``ShardedTensor``. Works for all subscript patterns handled by the existing
345
+ planner (matmul, bmm, torch, path) and propagates through multi-operand chains.
346
+
347
+ - **Reduce-parallel gate**: if the partition dimension is a contraction index
348
+ (not in the output), ``einsum()`` raises ``NotImplementedError`` with a clear
349
+ explanation: reduce-parallel sharding requires ``nki.collectives`` all-reduce,
350
+ arriving in Neuron SDK 2.30+. The gate prevents silent wrong results.
351
+
352
+ ### Notes
353
+
354
+ ``nki.collectives`` (all-reduce, all-gather) is not available in NKI 0.3.0
355
+ Stable. Reduce-parallel sharding (contracted-dimension sharding) is gated until
356
+ SDK 2.30+. This sprint covers the output-parallel subset, which handles the
357
+ common row-parallel and column-parallel matmul patterns used in DF-MP2
358
+ AO→MO transform with large basis sets (nbasis > 3000 across chips).
359
+
360
+ ## [0.8.0] — 2026-04-18
361
+
362
+ ### Fixed
363
+
364
+ - **`_execute_path` now routes binary steps through `einsum()`** — multi-operand
365
+ contractions (3+ tensors via the greedy path planner) previously called
366
+ `torch.einsum` directly for each binary step, bypassing the entire trntensor
367
+ dispatch layer (plan cache, FLOP thresholds, NKI routing). The fix is a
368
+ one-line change: each step now calls `einsum(sub, a, b, precision=plan.precision)`,
369
+ so matmul-shaped steps dispatch to NKI when operand size clears the threshold.
370
+ All correctness tests are unaffected; top-level `ContractionPlan.backend` for
371
+ strategy `"path"` remains `"pytorch"` (accurate: no single NKI kernel spans
372
+ an arbitrary path). (#18 follow-up)
373
+
374
+ ## [0.7.0] — 2026-04-17
375
+
376
+ ### Added
377
+
378
+ - **N-tiled `ao_to_mo_transform_kernel`** — lifts `nbasis ≤ 512` and
379
+ `nvir ≤ 512` single-N-tile constraints. Step 1 now tiles the ν (basis)
380
+ dimension in ``TILE_N=512``-wide chunks; step 2 tiles the virtual-orbital
381
+ a dimension similarly. Enables full DF-MP2 AO→MO transform for large basis
382
+ sets: cc-pVTZ on naphthalene (nbasis≈572), aug-cc-pVTZ virtual spaces
383
+ (nvir > 512). Dispatcher pads both `nbasis` and `nvir` to ``TILE_N``
384
+ multiples (zero-fill); result sliced back to `(nocc, nvir, naux)`.
385
+
386
+ - **Simulator tests for N-tiled shapes** (`tests/test_nki_sim.py`,
387
+ `TestAoToMoTransformSimulator`) — three new cases: `test_n_tiled_nbasis`
388
+ (nbasis=640 > TILE_N), `test_n_tiled_nvir` (nvir=600 > TILE_N),
389
+ `test_both_n_tiled` (nbasis=640, nvir=600). Each verifies against the
390
+ CPU einsum reference.
391
+
392
+ ### Changed
393
+
394
+ - **`ao_to_mo_transform` gates removed** — `NotImplementedError` for
395
+ `nbasis > 512` and `nvir > 512` removed; both constraints are now handled
396
+ by the tiled kernel + dispatcher. The `nocc > 128` gate remains (M-tiling
397
+ not yet implemented).
398
+ - **`_nki_ao_to_mo_transform` padding** — `nbasis` now padded to
399
+ `_round_up(nbasis, TILE_N)` instead of `_round_up(nbasis, TILE_K)`.
400
+ TILE_N alignment is stricter (multiples of 512 vs 128), but implies
401
+ TILE_K alignment (TILE_N = 4×TILE_K). `nvir` padding added.
402
+
403
+ ## [0.6.0] — 2026-04-17
404
+
405
+ ### Added
406
+
407
+ - **Tiled `mp2_energy_kernel`** — lifts `nvir ≤ 128` and `naux ≤ 128`
408
+ single-tile constraints. The kernel now M-tiles over the virtual-orbital
409
+ dimensions (TILE_M=128 per side) and K-tiles over the auxiliary index P
410
+ (TILE_K=128), enabling arbitrary `nvir` and `naux` (dispatcher pads to
411
+ tile multiples with zero-fill). Typical real-molecule sizes now supported:
412
+ aug-cc-pVDZ (naux≈300+), cc-pVTZ (nvir≈200+). Both `nvir=256` + `naux=256`
413
+ benchmarked at the same correctness tolerance as the single-tile path.
414
+
415
+ - **NVIR/NAUX padding in `_nki_mp2_energy` dispatch** — `_nki_mp2_energy`
416
+ now pads `B` to `(nocc, nvir_pad, naux_pad)` and `eps_vir` to
417
+ `(nvir_pad, 1)` before kernel entry. Zero-padding is safe: padded B
418
+ entries are 0, so tile contributions to `T*(2T-T.T)/Δ` are 0 regardless
419
+ of Δ.
420
+
421
+ - **Simulator tests for tiled shapes** (`tests/test_nki_sim.py`,
422
+ `TestMp2EnergySimulator`) — three new cases: `test_naux_tiled`
423
+ (nocc=4, nvir=32, naux=256), `test_nvir_tiled` (nocc=4, nvir=256,
424
+ naux=64), `test_both_tiled` (nocc=4, nvir=256, naux=256). Each verifies
425
+ against `_cpu_mp2_energy` via the NKI CPU simulator.
426
+
427
+ ### Changed
428
+
429
+ - **`mp2_energy` gate removed** — the `NotImplementedError` for
430
+ `nvir > 128 or naux > 128` is gone. The NKI path now handles all sizes
431
+ ≥ TILE_M / TILE_K via the tiled kernel; smaller sizes (padded up) are
432
+ handled identically to the original single-tile path.
433
+
434
+ ## [0.5.0] — 2026-04-17
435
+
436
+ ### Added
437
+
438
+ - **`multi_einsum` homogeneous batching** (#33 Phase 3) — when all contractions
439
+ share the same subscript, operand shapes, and resolve to a 2D matmul, they are
440
+ stacked and dispatched as a single `nki_batched_matmul` call instead of N
441
+ independent ones. Amortizes the ~0.67 ms fixed XLA dispatch overhead across the
442
+ batch (nocc²=4096 pair contractions: 2.7 s → 0.67 ms dispatch overhead). Falls
443
+ through to the per-contraction loop for heterogeneous sets.
444
+
445
+ - **`_MIN_NKI_FLOPS_PINNED` dispatch threshold** (#33) — second FLOP gate for
446
+ when operands are already XLA-resident (`to_xla` pre-applied). Profiler data
447
+ (trn1.2xlarge, `scripts/run_neuron_profile.sh --dispatch-timing`) shows the
448
+ NKI crossover drops from ~2 GFLOPs to ~900 MFLOPs when host→device transfer
449
+ is eliminated. Default: 1 GFLOPs. Override with `TRNTENSOR_MIN_NKI_FLOPS_PINNED`.
450
+ Dispatch automatically detects XLA-resident inputs and uses the lower threshold.
451
+
452
+ - **`docs/design/matmul_dispatch_findings.md`** — full per-step timing analysis
453
+ documenting XLA dispatch latency (~0.67 ms fixed overhead), transfer costs, and
454
+ the unpinned vs pre-pinned crossover. Raw JSON timing data included.
455
+
456
+ - **`docs/design/matmul_kernel_profile.md`** — Tensor Engine utilization profile
457
+ for `matmul_kernel` on trn1.2xlarge. Captures TE/VE split, DMA transfer
458
+ breakdown, and PSUM accumulation efficiency at medium (1024²) and large (2048²)
459
+ matrix sizes.
460
+
461
+ - **`infra/terraform/terraform.tfvars`** — VPC/subnet/region values for
462
+ `trntensor-ci-trn1` (us-east-1, vpc-cd49bfb0, subnet-1bd24d15).
463
+
464
+ ### Changed
465
+
466
+ - **`infra/terraform/main.tf`** — default `aws_region` corrected from `us-west-2`
467
+ to `us-east-1` to match the existing VPC/subnet. Running `terraform apply`
468
+ without overrides now selects the correct region.
469
+
470
+ ## [0.4.0] — 2026-04-16
471
+
472
+ ### Added
473
+
474
+ - **`precision=` kwarg for `einsum` and `plan_contraction`** (#28 partial) —
475
+ three modes: `"fast"` (default, no change), `"kahan"` (promotes all operands
476
+ to `float64` before contracting via `torch.einsum`, casts result back to
477
+ original dtype — gives ~15.9 significant digits; bypasses NKI dispatch for
478
+ CPU compatibility), `"dd"` (raises `NotImplementedError` pending trnblas Phase
479
+ 2 double-double GEMM). `precision` is included in the plan cache key so
480
+ `"fast"` and `"kahan"` plans cache independently. Useful for DF-MP2 / CCSD
481
+ energy convergence where fp32's ~7.2 digits are insufficient.
482
+
483
+ - **`dtype` mixed-precision override for `einsum`** (#22) — accepts string
484
+ aliases (`"bf16"`, `"fp16"`, `"f32"`) or `torch.dtype` instances. When set,
485
+ all operands are cast to the requested dtype before contracting and the
486
+ result is returned in that dtype. Matches Neuron SDK autocast recommendations;
487
+ use `dtype="bf16"` to route fp32 models through the NKI bf16 matmul path
488
+ without changing the model's weight dtype.
489
+
490
+ - **Neuron profiler script** (`scripts/run_neuron_profile.sh`) — captures a
491
+ Neuron Profiler 2.0 trace of `matmul_kernel` or `batched_matmul_kernel` via
492
+ SSM on the `trntensor-ci-trn1` instance. Supports `--probe` (API discovery),
493
+ `--kernel matmul|bmm`, and `--shape small|medium|large`. Adapted from the
494
+ trnblas profiler pattern (double-base64 encoding, auto start/stop).
495
+
496
+ - **Dispatch autotune script** (`scripts/autotune_dispatch.py`) (#33 partial)
497
+ — sweeps `(M, K, N)` shapes and measures NKI vs PyTorch wall-clock time to
498
+ find the empirical FLOP crossover on the current hardware. Reports the
499
+ recommended `TRNTENSOR_MIN_NKI_FLOPS` value; `--write-cache` persists the
500
+ result to `/var/tmp/trntensor-autotune/threshold.json`. The dispatch layer
501
+ reads this cache at startup (env var `TRNTENSOR_AUTOTUNE_CACHE` overrides
502
+ path; explicit `TRNTENSOR_MIN_NKI_FLOPS` always wins).
503
+
504
+ ## [0.3.0] — 2026-04-16
505
+
506
+ ### Added
507
+
508
+ - **Greedy contraction-path search for 3+ operand `einsum`** (#18) —
509
+ `plan_contraction()` now returns `strategy="path"` for three or more
510
+ operands. `_greedy_path_search` selects the cheapest binary contraction
511
+ order by minimizing per-step FLOP cost; the resulting
512
+ `ContractionPlan.contraction_path` drives `_execute_path`, which
513
+ routes each binary step through the full backend selection stack so
514
+ large sub-contractions still dispatch to NKI.
515
+
516
+ - **`multi_einsum` shared-operand XLA residency** (#19) — When NKI
517
+ dispatch is active, `multi_einsum` detects operand tensors that
518
+ appear in more than one contraction (by object identity) and
519
+ pre-pins them to the XLA device once before executing the loop.
520
+ Eliminates redundant host↔device transfers for workloads like DF-MP2
521
+ where the three-center integral tensor `B` feeds many pair
522
+ contractions. Falls back to the existing per-contraction loop on CPU.
523
+
524
+ - **Subscript and shape validation with descriptive errors** (#26) —
525
+ `plan_contraction()` now validates subscripts up-front and raises
526
+ `ValueError` with precise messages: wrong operand count, rank
527
+ mismatch, and inconsistent index sizes are all caught before any
528
+ torch operation runs. Eliminates cryptic downstream errors from
529
+ `torch.einsum`.
530
+
531
+ - **PEP 561 `py.typed` marker** (#25) — `trntensor` now ships a
532
+ `py.typed` file so type checkers (mypy, pyright, etc.) recognise
533
+ the package as typed and apply inline annotations.
534
+
535
+ - **`alpha`/`beta` scaling for `einsum`** (#20) — matches cuTENSOR's
536
+ GEMM-style interface:
537
+ `einsum(subscripts, A, B, alpha=α, beta=β, out=C)` returns
538
+ `α * contract(A, B) + β * C`. Defaults (`alpha=1, beta=0, out=None`)
539
+ preserve existing behaviour exactly. Useful for accumulation patterns
540
+ and in-place gradient updates without an extra allocation.
541
+
542
+ - **Contraction plan cache** (#29 partial) — `plan_contraction()` caches
543
+ results by `(subscripts, operand shapes)`. Repeated calls with the same
544
+ subscript and shapes skip replanning entirely. `clear_plan_cache()` and
545
+ `plan_cache_info()` are exported from the top-level API.
546
+
547
+ - **Tensor Train (TT) decomposition** (#23) — `tt_decompose(tensor, max_rank)`
548
+ decomposes a d-dimensional tensor into a chain of 3-tensor cores via TT-SVD
549
+ (Oseledets 2011), bond dimension capped at `max_rank`. `tt_reconstruct(cores)`
550
+ contracts the chain back. Useful for DMRG-style high-dimensional compression.
551
+
552
+ - **Non-negative CP and warm-start CP** (#24) — `cp_decompose` gains two new
553
+ keyword arguments: `nonneg=True` switches ALS to multiplicative updates
554
+ to enforce non-negative factors; `factors=` accepts a list of pre-computed
555
+ factor matrices to warm-start from, skipping random initialization.
556
+ Both options compose.
557
+
558
+ ## [0.2.0] — 2026-04-15
559
+
560
+ ### Added
561
+
562
+ - **`trntensor.to_xla(tensor)` / `trntensor.from_xla(tensor)`** — explicit
563
+ operand residency on the Trainium XLA device. Pre-pinning operands
564
+ lets repeated trntensor calls skip per-dispatch host↔device
565
+ transfer, which otherwise dominates at current kernel sizes. The
566
+ full DF-MP2 pipeline (`ao_to_mo_transform` → `mp2_energy`) with all
567
+ operands pre-pinned pays transfer cost once instead of once per
568
+ call. The dispatch layer's `_to_xla` helper takes a fast path when
569
+ every operand is already on XLA, returning the result on XLA — the
570
+ caller decides when to pull back via `from_xla`. Closes #34.
571
+ - **`trntensor.ao_to_mo_transform(eri, C_occ, C_vir)`** — fused 4-index
572
+ AO→MO integral transform with K-tiling over the basis index (#37).
573
+ One NKI program computes `B[i,a,P] = Σ_{μν} C_occ[μ,i] · C_vir[ν,a] ·
574
+ eri[μ,ν,P]`. Tiles over μ (step 1) and ν (step 2) in TILE_K=128
575
+ chunks so `nbasis` up to 512 is supported; dispatch pads to the
576
+ nearest TILE_K multiple. Shape constraints: `nbasis ≤ 512`,
577
+ `nocc ≤ 128`, `nvir ≤ 512`. Composes with `mp2_energy` for the full
578
+ DF-MP2 pipeline from AO integrals to correlation energy. Validated
579
+ on trn1 (hardware) and via the CPU simulator CI job.
580
+ - **NKI CPU simulator dispatch** via `TRNTENSOR_USE_SIMULATOR=1`.
581
+ Routes kernels through `nki.simulate(kernel)(numpy_args)` on CPU,
582
+ bypassing `torch_xla` + NEFF compile. Iteration loop drops from
583
+ ~5 min per SSM round-trip to seconds. Correctness-only — MLIR
584
+ verifier errors remain hardware-only.
585
+ - **`nki-simulator` CI job on `ubuntu-latest`** — runs the
586
+ `nki_simulator`-marked suite against `nki>=0.3.0` from the AWS pip
587
+ index on every push + PR. Zero AWS cost for the correctness gate.
588
+ - `tests/test_nki_sim.py` — simulator-backed correctness suite,
589
+ marker `nki_simulator`. Covers matmul, batched matmul,
590
+ `ao_to_mo_transform` (including K-tiled nbasis=256 and non-aligned
591
+ nbasis=200), and `mp2_energy`.
592
+ - `scripts/run_simulator_tests.sh` — SSM runner for the simulator
593
+ suite on the trn1 DLAMI.
594
+ - `docs/developing_kernels.md` — NKI kernel development guide with
595
+ trntensor-specific env vars and file locations.
596
+
597
+ ### Changed
598
+
599
+ - **Migrated to NKI 0.3.0 / Neuron SDK 2.29.** Canonical `nki.*`
600
+ namespace; the legacy `neuronxcc.nki.*` shim is no longer used.
601
+ Kernels updated for the NKI 0.3.0 breaking-change surface:
602
+ `nisa.nc_matmul(dst=, stationary=, moving=, accumulate=True)` (all
603
+ kwargs); `nl.copy(psum)` returns a view — use `nl.ndarray` +
604
+ `nisa.tensor_copy` instead; tensor-tensor `nl.divide` dropped — use
605
+ `multiply × reciprocal`.
606
+ - Dev workflow migrated to [uv](https://github.com/astral-sh/uv).
607
+ `uv sync --extra dev` replaces `pip install -e ".[dev]"`; CI uses
608
+ `astral-sh/setup-uv@v6` and `uv run pytest` / `uvx ruff`. `uv.lock`
609
+ is committed for reproducible installs.
610
+ - Removed the `[neuron]` optional-dependencies extra. `nki` is
611
+ installed from the AWS Neuron pip index in CI or provided by the
612
+ Deep Learning AMI's pre-built venv on hardware.
613
+ - `CONTRIBUTING.md` updated to reflect the uv-based setup.
614
+
615
+ ### Fixed
616
+
617
+ - `mp2_energy_kernel` 1D-load ambiguity on pre-pinned XLA ε inputs
618
+ (#38). Reshape `eps_occ` / `eps_vir` to 2D `(N, 1)` at the dispatch
619
+ boundary; partition-dim inference is unambiguous regardless of
620
+ residency state.
621
+ - `mp2_energy_kernel` 0-D SBUF rejection (`SBUF tensors must have at
622
+ least 2 dimensions`). Per-`(i,j)` reduction now uses a persistent
623
+ `(1, 1)` SBUF accumulator (`nl.zeros((1, 1), ...)`) instead of a
624
+ direct `nl.sum` store.
625
+ - `_to_xla` fast-path now calls `xm.mark_step()` when operands are
626
+ already on XLA, forcing pending lazy computations to materialize
627
+ before the next kernel dispatch.
628
+
629
+ ### Known limitation
630
+
631
+ - The full DF-MP2 pipeline (`ao_to_mo_transform` → `mp2_energy`) with
632
+ every operand pre-pinned exposes an NKI compiler bug on trn1: the
633
+ combined XLA lazy graph provokes `trn2-only shared memory`
634
+ instructions that fail verification on trn1. Workaround: `from_xla`
635
+ the intermediate `B` between the two calls. Tracked in #39 for
636
+ upstream AWS escalation.
637
+
638
+ ## [0.1.2] — 2026-04-12
639
+
640
+ ### Changed
641
+
642
+ - `set_backend("nki")` now raises `RuntimeError` on non-Neuron hosts
643
+ instead of silently accepting the backend and failing later. Matches
644
+ the sibling-suite pattern.
645
+ - CI actions bumped to `actions/checkout@v6` + `actions/setup-python@v6`
646
+ (Node.js 24), ahead of GitHub's June 2026 default switch.
647
+ - pyproject metadata normalized across the trnsci suite (author email,
648
+ URLs, classifier list).
649
+ - Standalone `docs.yml` removed — docs are now served via `trnsci.dev`
650
+ through the umbrella's combined build. `notify-umbrella.yml` pings
651
+ the umbrella on docs changes.
652
+ - `infra/terraform/main.tf`: user-data clone URL corrected to
653
+ `trnsci/trntensor`.
654
+
655
+ ### Added
656
+
657
+ - `benchmarks/bench_einsum.py` — pytest-benchmark cases for einsum
658
+ dispatch and decompositions. CPU baseline numbers populated in
659
+ `docs/benchmarks.md`.
660
+ - `tests/test_nki.py` — backend-dispatch unit tests (CPU path).
661
+
662
+ ## [0.1.1] — 2026-04-12
663
+
664
+ ### Added
665
+
666
+ - mkdocs site with `index`, `installation`, `quickstart`, `api`, `architecture`, `aws_setup`
667
+ - `infra/terraform/` for on-hardware CI instance provisioning
668
+ - `scripts/run_neuron_tests.sh` and benchmark helpers
669
+ - GitHub Actions `ci.yml`, `docs.yml`, `publish.yml`
670
+ - `Issues` and `Documentation` URLs in pyproject.toml
671
+ - `tests/test_plan.py` — dedicated planner unit tests (parsing,
672
+ strategy selection, FLOP estimates); extended CP / Tucker coverage
673
+ (all-zero tensor, rank > min dim, unequal mode ranks)
674
+
675
+ ### Changed
676
+
677
+ - Bumped `neuronxcc` floor from `>=2.15` to `>=2.24` to unify with the
678
+ rest of the trnsci suite. `torch-neuronx` floor bumped to `>=2.9`.
679
+
680
+ ## [0.1.0] — 2026-04-12
681
+
682
+ ### Added
683
+
684
+ - Initial scaffold: einsum with contraction planning, CP / Tucker decompositions
685
+ - NKI dispatch with fused-contraction kernel stubs
686
+ - `examples/df_mp2_einsum.py` — DF-MP2 energy via einsum