Xenova HF Staff commited on
Commit
59216c2
·
verified ·
1 Parent(s): 2a3f031

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,21 +18,21 @@ See the [ONNX Runtime `LinearAttention` contrib-operator spec](https://github.co
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `query` | `queryT` | `T` | `3` | — | Query vectors with 3D packed shape `(B, T, H_q * d_k)`; heads are packed into the last dimension. | required |
24
- | `key` | `keyT` | `T` | `3` | — | Key vectors with 3D packed shape `(B, T, H_k * d_k)`, where positive `H_k` divides `H_kv`; `H_k < H_kv` shares each key head across multiple KV-state heads. Keys should be L2-normalized for `delta`/`gated_delta` modes. | required |
25
- | `value` | `valueT` | `T` | `3` | — | Value vectors with 3D packed shape `(B, T, H_kv * d_v)`. | required |
26
- | `past_state` | `pastStateT` | `S` | derived | derived; see description | Recurrent state from the previous step with shape `(B, H_kv, d_k, d_v)`, or `(W, B, H_kv, d_k, d_v)` when `state_window = W > 0`; defaults to zeros if absent. | optional |
27
- | `decay` | `decayT` | `T` | `3` | — | Exponential decay gate in log-space with shape `(B, T, H_kv * d_k)` or `(B, T, H_kv)`; required for `gated` and `gated_delta` modes. | optional |
28
- | `beta` | `betaT` | `T` | `3` | — | Update rate (sigmoid output) with shape `(B, T, H_kv)` or `(B, T, 1)`; required for `delta` and `gated_delta` modes. | optional |
29
 
30
  ## Outputs
31
 
32
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
33
  | --- | --- | --- | --- | --- | --- | --- |
34
- | `output` | `outputT` | `T` | `3` | derived; see description | Attention output with 3D packed shape `(B, T, max(H_q, H_kv) * d_v)`. | required |
35
- | `present_state` | `presentStateT` | `S` | derived | derived; see description | Updated recurrent state with shape `(B, H_kv, d_k, d_v)`, or `(W, B, H_kv, d_k, d_v)` when `state_window = W > 0`. | required |
36
 
37
  ## Attributes
38
 
@@ -41,11 +41,11 @@ Attributes and default values (overridable per request):
41
  | Attribute | Default | Description |
42
  | --- | --- | --- |
43
  | `chunk_size` | `64` | Accepted for schema compatibility; does not affect the result. |
 
 
44
  | `scale` | `0` | Scale applied to query-key products. Zero selects `1 / sqrt(d_k)`. |
45
  | `state_window` | `0` | Number of recent recurrent states retained in `present_state`, in the supported range 0 to 8; zero returns only the current state. |
46
  | `update_rule` | `"gated_delta"` | Recurrent update rule: `linear`, `gated`, `delta`, or `gated_delta`. |
47
- | `kv_num_heads` | — | Number of key/value heads. |
48
- | `q_num_heads` | — | Number of query heads. |
49
 
50
  ## Type constraints
51
 
@@ -54,9 +54,18 @@ Attributes and default values (overridable per request):
54
  | `T` | `float32`, `float16` |
55
  | `S` | `float32`, `float16` |
56
 
 
 
 
 
 
 
 
 
 
57
  ## Files
58
 
59
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
60
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
61
  - [`test.json`](build/webgpu/test.json) — correctness cases
62
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -70,10 +79,14 @@ Attributes and default values (overridable per request):
70
 
71
  ## Use with `@huggingface/kernels`
72
 
73
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
74
- It then allocates the result tensors automatically.
 
 
 
75
 
76
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
77
 
78
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
79
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `queryT` | `query` | `T` | `3` | — | Query vectors with 3D packed shape `(B, T, H_q * d_k)`; heads are packed into the last dimension. | required |
24
+ | `keyT` | `key` | `T` | `3` | — | Key vectors with 3D packed shape `(B, T, H_k * d_k)`, where positive `H_k` divides `H_kv`; `H_k < H_kv` shares each key head across multiple KV-state heads. Keys should be L2-normalized for `delta`/`gated_delta` modes. | required |
25
+ | `valueT` | `value` | `T` | `3` | — | Value vectors with 3D packed shape `(B, T, H_kv * d_v)`. | required |
26
+ | `pastStateT` | `past_state` | `S` | derived | derived | Recurrent state from the previous step with shape `(B, H_kv, d_k, d_v)`, or `(W, B, H_kv, d_k, d_v)` when `state_window = W > 0`; defaults to zeros if absent. | optional |
27
+ | `decayT` | `decay` | `T` | `3` | — | Exponential decay gate in log-space with shape `(B, T, H_kv * d_k)` or `(B, T, H_kv)`; required for `gated` and `gated_delta` modes. | optional |
28
+ | `betaT` | `beta` | `T` | `3` | — | Update rate (sigmoid output) with shape `(B, T, H_kv)` or `(B, T, 1)`; required for `delta` and `gated_delta` modes. | optional |
29
 
30
  ## Outputs
31
 
32
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
33
  | --- | --- | --- | --- | --- | --- | --- |
34
+ | `outputT` | `output` | `T` | `3` | derived | Attention output with 3D packed shape `(B, T, max(H_q, H_kv) * d_v)`. | required |
35
+ | `presentStateT` | `present_state` | `S` | derived | derived | Updated recurrent state with shape `(B, H_kv, d_k, d_v)`, or `(W, B, H_kv, d_k, d_v)` when `state_window = W > 0`. | required |
36
 
37
  ## Attributes
38
 
 
41
  | Attribute | Default | Description |
42
  | --- | --- | --- |
43
  | `chunk_size` | `64` | Accepted for schema compatibility; does not affect the result. |
44
+ | `kv_num_heads` | — | Number of key/value heads. |
45
+ | `q_num_heads` | — | Number of query heads. |
46
  | `scale` | `0` | Scale applied to query-key products. Zero selects `1 / sqrt(d_k)`. |
47
  | `state_window` | `0` | Number of recent recurrent states retained in `present_state`, in the supported range 0 to 8; zero returns only the current state. |
48
  | `update_rule` | `"gated_delta"` | Recurrent update rule: `linear`, `gated`, `delta`, or `gated_delta`. |
 
 
49
 
50
  ## Type constraints
51
 
 
54
  | `T` | `float32`, `float16` |
55
  | `S` | `float32`, `float16` |
56
 
57
+ ## Implementation variants
58
+
59
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
60
+
61
+ - `linear_zero_serial_small_dk` — Small key dimensions keep each state column private across the sequence. Linear updates specialize shape-known indexing and loop bounds, while gated-delta updates retain their uniform-driven recurrence.
62
+ - `linear_state_serial_small_dk` — Small key dimensions keep each state column private across the sequence. Linear updates specialize shape-known indexing and loop bounds, while gated-delta updates retain their uniform-driven recurrence.
63
+ - `gated_delta_zero_serial_small_dk` — Small key dimensions keep each state column private across the sequence. Linear updates specialize shape-known indexing and loop bounds, while gated-delta updates retain their uniform-driven recurrence.
64
+ - `gated_delta_state_serial_small_dk` — Small key dimensions keep each state column private across the sequence. Linear updates specialize shape-known indexing and loop bounds, while gated-delta updates retain their uniform-driven recurrence.
65
+
66
  ## Files
67
 
68
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
69
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
70
  - [`test.json`](build/webgpu/test.json) — correctness cases
71
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
79
 
80
  ## Use with `@huggingface/kernels`
81
 
82
+ ```sh
83
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
84
+ ```
85
+
86
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
87
 
88
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
89
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
90
 
91
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
92
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.LinearAttention",
3
  "tunableSpace": {
4
  "dvGroups": [2, 4, 8],
5
  "tileV": [4, 8, 16],
@@ -81,7 +80,7 @@
81
  "name": "linear-attention-linear-state-scalar-f16-seq1536-pathology",
82
  "preset": "stress",
83
  "provenance": {
84
- "source": "authored for branch coverage",
85
  "notes": "Long-sequence supplied-state case at head_dim_k 16. It exercises the recurrent small-dk route's serial token recurrence and distinguishes it from the chunked prefill decomposition."
86
  },
87
  "vars": { "batch": 4, "seq": 1536, "qHeads": 4, "kvHeads": 2, "dk": 16, "dv": 16 },
@@ -151,8 +150,8 @@
151
  ]
152
  },
153
  "provenance": {
154
- "source": "authored for branch coverage",
155
- "notes": "Zero-state sibling of the supplied-state long-sequence case, covering the same branch and shape without an entry state."
156
  }
157
  },
158
  {
@@ -213,7 +212,7 @@
213
  "name": "linear-attention-qwen3next-decode-s1",
214
  "preset": "model",
215
  "provenance": {
216
- "notes": "Qwen3-Next class defaults (linear_num_value_heads 32, linear_num_key_heads 16, linear_key_head_dim 128, linear_value_head_dim 128) at a decode step, where the recurrence carries the whole cost."
217
  },
218
  "vars": { "batch": 1, "seq": 1, "qHeads": 32, "kvHeads": 16, "dk": 128, "dv": 128 },
219
  "attrs": {
@@ -225,11 +224,33 @@
225
  },
226
  "inputs": {
227
  "queryT": { "shape": [1, 1, 4096], "dtype": "float32", "dist": "normal", "seed": 8100, "scale": 0.3 },
228
- "keyT": { "shape": [1, 1, 2048], "dtype": "float32", "dist": "normal", "seed": 8101, "scale": 0.3 },
 
 
 
 
 
 
229
  "valueT": { "shape": [1, 1, 2048], "dtype": "float32", "dist": "normal", "seed": 8102, "scale": 0.3 },
230
  "pastStateT": { "shape": [1, 16, 128, 128], "dtype": "float32", "dist": "normal", "seed": 8103, "scale": 0.1 },
231
- "decayT": { "shape": [1, 1, 2048], "dtype": "float32", "dist": "uniform", "seed": 8104, "min": 0.9, "max": 1 },
232
- "betaT": { "shape": [1, 1, 16], "dtype": "float32", "dist": "uniform", "seed": 8105, "min": 0.1, "max": 0.9 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  },
234
  "outputs": {
235
  "outputT": { "shape": [1, 1, 4096], "dtype": "float32" },
@@ -247,7 +268,9 @@
247
  {
248
  "name": "linear-attention-qwen3next-prefill-s512",
249
  "preset": "model",
250
- "provenance": { "notes": "Qwen3-Next class defaults over a 512-token prefill chunk." },
 
 
251
  "vars": { "batch": 1, "seq": 512, "qHeads": 32, "kvHeads": 16, "dk": 128, "dv": 128 },
252
  "attrs": {
253
  "q_num_heads": 32,
@@ -258,11 +281,33 @@
258
  },
259
  "inputs": {
260
  "queryT": { "shape": [1, 512, 4096], "dtype": "float32", "dist": "normal", "seed": 8200, "scale": 0.3 },
261
- "keyT": { "shape": [1, 512, 2048], "dtype": "float32", "dist": "normal", "seed": 8201, "scale": 0.3 },
 
 
 
 
 
 
262
  "valueT": { "shape": [1, 512, 2048], "dtype": "float32", "dist": "normal", "seed": 8202, "scale": 0.3 },
263
  "pastStateT": { "shape": [1, 16, 128, 128], "dtype": "float32", "dist": "normal", "seed": 8203, "scale": 0.1 },
264
- "decayT": { "shape": [1, 512, 2048], "dtype": "float32", "dist": "uniform", "seed": 8204, "min": 0.9, "max": 1 },
265
- "betaT": { "shape": [1, 512, 16], "dtype": "float32", "dist": "uniform", "seed": 8205, "min": 0.1, "max": 0.9 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  },
267
  "outputs": {
268
  "outputT": { "shape": [1, 512, 4096], "dtype": "float32" },
@@ -281,7 +326,7 @@
281
  "name": "linear-attention-qwen3next-prefill-s2048",
282
  "preset": "model",
283
  "provenance": {
284
- "notes": "Qwen3-Next class defaults over a 2048-token prefill chunk, eight chunk_size 64 blocks per workgroup pass."
285
  },
286
  "vars": { "batch": 1, "seq": 2048, "qHeads": 32, "kvHeads": 16, "dk": 128, "dv": 128 },
287
  "attrs": {
@@ -293,7 +338,13 @@
293
  },
294
  "inputs": {
295
  "queryT": { "shape": [1, 2048, 4096], "dtype": "float32", "dist": "normal", "seed": 8300, "scale": 0.3 },
296
- "keyT": { "shape": [1, 2048, 2048], "dtype": "float32", "dist": "normal", "seed": 8301, "scale": 0.3 },
 
 
 
 
 
 
297
  "valueT": { "shape": [1, 2048, 2048], "dtype": "float32", "dist": "normal", "seed": 8302, "scale": 0.3 },
298
  "pastStateT": { "shape": [1, 16, 128, 128], "dtype": "float32", "dist": "normal", "seed": 8303, "scale": 0.1 },
299
  "decayT": {
@@ -301,10 +352,19 @@
301
  "dtype": "float32",
302
  "dist": "uniform",
303
  "seed": 8304,
304
- "min": 0.9,
305
- "max": 1
 
306
  },
307
- "betaT": { "shape": [1, 2048, 16], "dtype": "float32", "dist": "uniform", "seed": 8305, "min": 0.1, "max": 0.9 }
 
 
 
 
 
 
 
 
308
  },
309
  "outputs": {
310
  "outputT": { "shape": [1, 2048, 4096], "dtype": "float32" },
@@ -318,6 +378,654 @@
318
  }
319
  ]
320
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  }
322
  ]
323
  }
 
1
  {
 
2
  "tunableSpace": {
3
  "dvGroups": [2, 4, 8],
4
  "tileV": [4, 8, 16],
 
80
  "name": "linear-attention-linear-state-scalar-f16-seq1536-pathology",
81
  "preset": "stress",
82
  "provenance": {
83
+ "source": "synthetic benchmark",
84
  "notes": "Long-sequence supplied-state case at head_dim_k 16. It exercises the recurrent small-dk route's serial token recurrence and distinguishes it from the chunked prefill decomposition."
85
  },
86
  "vars": { "batch": 4, "seq": 1536, "qHeads": 4, "kvHeads": 2, "dk": 16, "dv": 16 },
 
150
  ]
151
  },
152
  "provenance": {
153
+ "source": "synthetic benchmark",
154
+ "notes": "A long-sequence scalar recurrence uses the zero-state branch with no entry state."
155
  }
156
  },
157
  {
 
212
  "name": "linear-attention-qwen3next-decode-s1",
213
  "preset": "model",
214
  "provenance": {
215
+ "notes": "Qwen3-Next class defaults (linear_num_value_heads 32, linear_num_key_heads 16, linear_key_head_dim 128, linear_value_head_dim 128) at a decode step, where the recurrence carries the whole cost. Float uniforms use scale/offset: log-decay spans log(0.9) to 0, beta spans 0.1 to 0.9, and keys are bounded by 1/sqrt(dk) so squared key norms do not exceed one."
216
  },
217
  "vars": { "batch": 1, "seq": 1, "qHeads": 32, "kvHeads": 16, "dk": 128, "dv": 128 },
218
  "attrs": {
 
224
  },
225
  "inputs": {
226
  "queryT": { "shape": [1, 1, 4096], "dtype": "float32", "dist": "normal", "seed": 8100, "scale": 0.3 },
227
+ "keyT": {
228
+ "shape": [1, 1, 2048],
229
+ "dtype": "float32",
230
+ "dist": "uniform",
231
+ "seed": 8101,
232
+ "scale": 0.08838834764831843
233
+ },
234
  "valueT": { "shape": [1, 1, 2048], "dtype": "float32", "dist": "normal", "seed": 8102, "scale": 0.3 },
235
  "pastStateT": { "shape": [1, 16, 128, 128], "dtype": "float32", "dist": "normal", "seed": 8103, "scale": 0.1 },
236
+ "decayT": {
237
+ "shape": [1, 1, 2048],
238
+ "dtype": "float32",
239
+ "dist": "uniform",
240
+ "seed": 8104,
241
+ "signed": false,
242
+ "offset": -0.10536051565782628,
243
+ "scale": 0.10536051565782628
244
+ },
245
+ "betaT": {
246
+ "shape": [1, 1, 16],
247
+ "dtype": "float32",
248
+ "dist": "uniform",
249
+ "seed": 8105,
250
+ "signed": false,
251
+ "offset": 0.1,
252
+ "scale": 0.8
253
+ }
254
  },
255
  "outputs": {
256
  "outputT": { "shape": [1, 1, 4096], "dtype": "float32" },
 
268
  {
269
  "name": "linear-attention-qwen3next-prefill-s512",
270
  "preset": "model",
271
+ "provenance": {
272
+ "notes": "Qwen3-Next class defaults over a 512-token prefill chunk. Float uniforms use scale/offset: log-decay spans log(0.9) to 0, beta spans 0.1 to 0.9, and keys are bounded by 1/sqrt(dk) so squared key norms do not exceed one."
273
+ },
274
  "vars": { "batch": 1, "seq": 512, "qHeads": 32, "kvHeads": 16, "dk": 128, "dv": 128 },
275
  "attrs": {
276
  "q_num_heads": 32,
 
281
  },
282
  "inputs": {
283
  "queryT": { "shape": [1, 512, 4096], "dtype": "float32", "dist": "normal", "seed": 8200, "scale": 0.3 },
284
+ "keyT": {
285
+ "shape": [1, 512, 2048],
286
+ "dtype": "float32",
287
+ "dist": "uniform",
288
+ "seed": 8201,
289
+ "scale": 0.08838834764831843
290
+ },
291
  "valueT": { "shape": [1, 512, 2048], "dtype": "float32", "dist": "normal", "seed": 8202, "scale": 0.3 },
292
  "pastStateT": { "shape": [1, 16, 128, 128], "dtype": "float32", "dist": "normal", "seed": 8203, "scale": 0.1 },
293
+ "decayT": {
294
+ "shape": [1, 512, 2048],
295
+ "dtype": "float32",
296
+ "dist": "uniform",
297
+ "seed": 8204,
298
+ "signed": false,
299
+ "offset": -0.10536051565782628,
300
+ "scale": 0.10536051565782628
301
+ },
302
+ "betaT": {
303
+ "shape": [1, 512, 16],
304
+ "dtype": "float32",
305
+ "dist": "uniform",
306
+ "seed": 8205,
307
+ "signed": false,
308
+ "offset": 0.1,
309
+ "scale": 0.8
310
+ }
311
  },
312
  "outputs": {
313
  "outputT": { "shape": [1, 512, 4096], "dtype": "float32" },
 
326
  "name": "linear-attention-qwen3next-prefill-s2048",
327
  "preset": "model",
328
  "provenance": {
329
+ "notes": "Qwen3-Next class defaults over a 2048-token prefill chunk, eight chunk_size 64 blocks per workgroup pass. Float uniforms use scale/offset: log-decay spans log(0.9) to 0, beta spans 0.1 to 0.9, and keys are bounded by 1/sqrt(dk) so squared key norms do not exceed one."
330
  },
331
  "vars": { "batch": 1, "seq": 2048, "qHeads": 32, "kvHeads": 16, "dk": 128, "dv": 128 },
332
  "attrs": {
 
338
  },
339
  "inputs": {
340
  "queryT": { "shape": [1, 2048, 4096], "dtype": "float32", "dist": "normal", "seed": 8300, "scale": 0.3 },
341
+ "keyT": {
342
+ "shape": [1, 2048, 2048],
343
+ "dtype": "float32",
344
+ "dist": "uniform",
345
+ "seed": 8301,
346
+ "scale": 0.08838834764831843
347
+ },
348
  "valueT": { "shape": [1, 2048, 2048], "dtype": "float32", "dist": "normal", "seed": 8302, "scale": 0.3 },
349
  "pastStateT": { "shape": [1, 16, 128, 128], "dtype": "float32", "dist": "normal", "seed": 8303, "scale": 0.1 },
350
  "decayT": {
 
352
  "dtype": "float32",
353
  "dist": "uniform",
354
  "seed": 8304,
355
+ "signed": false,
356
+ "offset": -0.10536051565782628,
357
+ "scale": 0.10536051565782628
358
  },
359
+ "betaT": {
360
+ "shape": [1, 2048, 16],
361
+ "dtype": "float32",
362
+ "dist": "uniform",
363
+ "seed": 8305,
364
+ "signed": false,
365
+ "offset": 0.1,
366
+ "scale": 0.8
367
+ }
368
  },
369
  "outputs": {
370
  "outputT": { "shape": [1, 2048, 4096], "dtype": "float32" },
 
378
  }
379
  ]
380
  }
381
+ },
382
+ {
383
+ "name": "bench-linear_geometry_float32_float32_g0_zero",
384
+ "provenance": {
385
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
386
+ },
387
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
388
+ "inputs": {
389
+ "queryT": {
390
+ "dtype": "float32",
391
+ "shape": [2, 33, 32],
392
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
393
+ },
394
+ "keyT": {
395
+ "dtype": "float32",
396
+ "shape": [2, 33, 8],
397
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
398
+ },
399
+ "valueT": {
400
+ "dtype": "float32",
401
+ "shape": [2, 33, 14],
402
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
403
+ }
404
+ },
405
+ "outputs": {
406
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
407
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
408
+ },
409
+ "preset": "stress",
410
+ "bench": {
411
+ "primary": true,
412
+ "metrics": [
413
+ {
414
+ "type": "gflops",
415
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
416
+ }
417
+ ]
418
+ }
419
+ },
420
+ {
421
+ "name": "bench-linear_geometry_float32_float32_g0_state",
422
+ "provenance": {
423
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
424
+ },
425
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
426
+ "inputs": {
427
+ "queryT": {
428
+ "dtype": "float32",
429
+ "shape": [2, 33, 32],
430
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
431
+ },
432
+ "keyT": {
433
+ "dtype": "float32",
434
+ "shape": [2, 33, 8],
435
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
436
+ },
437
+ "valueT": {
438
+ "dtype": "float32",
439
+ "shape": [2, 33, 14],
440
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
441
+ },
442
+ "pastStateT": {
443
+ "dtype": "float32",
444
+ "shape": [3, 2, 2, 8, 7],
445
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
446
+ }
447
+ },
448
+ "outputs": {
449
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
450
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
451
+ },
452
+ "preset": "stress",
453
+ "bench": {
454
+ "primary": true,
455
+ "metrics": [
456
+ {
457
+ "type": "gflops",
458
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
459
+ }
460
+ ]
461
+ }
462
+ },
463
+ {
464
+ "name": "bench-linear_geometry_float32_float32_g1_zero",
465
+ "provenance": {
466
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
467
+ },
468
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
469
+ "inputs": {
470
+ "queryT": {
471
+ "dtype": "float32",
472
+ "shape": [1, 17, 12],
473
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
474
+ },
475
+ "keyT": {
476
+ "dtype": "float32",
477
+ "shape": [1, 17, 12],
478
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
479
+ },
480
+ "valueT": {
481
+ "dtype": "float32",
482
+ "shape": [1, 17, 20],
483
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
484
+ }
485
+ },
486
+ "outputs": {
487
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
488
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
489
+ },
490
+ "preset": "stress",
491
+ "bench": {
492
+ "primary": true,
493
+ "metrics": [
494
+ {
495
+ "type": "gflops",
496
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
497
+ }
498
+ ]
499
+ }
500
+ },
501
+ {
502
+ "name": "bench-linear_geometry_float32_float32_g1_state",
503
+ "provenance": {
504
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
505
+ },
506
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
507
+ "inputs": {
508
+ "queryT": {
509
+ "dtype": "float32",
510
+ "shape": [1, 17, 12],
511
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
512
+ },
513
+ "keyT": {
514
+ "dtype": "float32",
515
+ "shape": [1, 17, 12],
516
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
517
+ },
518
+ "valueT": {
519
+ "dtype": "float32",
520
+ "shape": [1, 17, 20],
521
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
522
+ },
523
+ "pastStateT": {
524
+ "dtype": "float32",
525
+ "shape": [1, 4, 6, 5],
526
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
527
+ }
528
+ },
529
+ "outputs": {
530
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
531
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
532
+ },
533
+ "preset": "stress",
534
+ "bench": {
535
+ "primary": true,
536
+ "metrics": [
537
+ {
538
+ "type": "gflops",
539
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
540
+ }
541
+ ]
542
+ }
543
+ },
544
+ {
545
+ "name": "bench-linear_geometry_float16_float16_g0_zero",
546
+ "provenance": {
547
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
548
+ },
549
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
550
+ "inputs": {
551
+ "queryT": {
552
+ "dtype": "float16",
553
+ "shape": [2, 33, 32],
554
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
555
+ },
556
+ "keyT": {
557
+ "dtype": "float16",
558
+ "shape": [2, 33, 8],
559
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
560
+ },
561
+ "valueT": {
562
+ "dtype": "float16",
563
+ "shape": [2, 33, 14],
564
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
565
+ }
566
+ },
567
+ "outputs": {
568
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28] },
569
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7] }
570
+ },
571
+ "preset": "stress",
572
+ "bench": {
573
+ "primary": true,
574
+ "metrics": [
575
+ {
576
+ "type": "gflops",
577
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
578
+ }
579
+ ]
580
+ }
581
+ },
582
+ {
583
+ "name": "bench-linear_geometry_float16_float16_g0_state",
584
+ "provenance": {
585
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
586
+ },
587
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
588
+ "inputs": {
589
+ "queryT": {
590
+ "dtype": "float16",
591
+ "shape": [2, 33, 32],
592
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
593
+ },
594
+ "keyT": {
595
+ "dtype": "float16",
596
+ "shape": [2, 33, 8],
597
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
598
+ },
599
+ "valueT": {
600
+ "dtype": "float16",
601
+ "shape": [2, 33, 14],
602
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
603
+ },
604
+ "pastStateT": {
605
+ "dtype": "float16",
606
+ "shape": [3, 2, 2, 8, 7],
607
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
608
+ }
609
+ },
610
+ "outputs": {
611
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28] },
612
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7] }
613
+ },
614
+ "preset": "stress",
615
+ "bench": {
616
+ "primary": true,
617
+ "metrics": [
618
+ {
619
+ "type": "gflops",
620
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
621
+ }
622
+ ]
623
+ }
624
+ },
625
+ {
626
+ "name": "bench-linear_geometry_float16_float16_g1_zero",
627
+ "provenance": {
628
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
629
+ },
630
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
631
+ "inputs": {
632
+ "queryT": {
633
+ "dtype": "float16",
634
+ "shape": [1, 17, 12],
635
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
636
+ },
637
+ "keyT": {
638
+ "dtype": "float16",
639
+ "shape": [1, 17, 12],
640
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
641
+ },
642
+ "valueT": {
643
+ "dtype": "float16",
644
+ "shape": [1, 17, 20],
645
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
646
+ }
647
+ },
648
+ "outputs": {
649
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20] },
650
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5] }
651
+ },
652
+ "preset": "stress",
653
+ "bench": {
654
+ "primary": true,
655
+ "metrics": [
656
+ {
657
+ "type": "gflops",
658
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
659
+ }
660
+ ]
661
+ }
662
+ },
663
+ {
664
+ "name": "bench-linear_geometry_float16_float16_g1_state",
665
+ "provenance": {
666
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
667
+ },
668
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
669
+ "inputs": {
670
+ "queryT": {
671
+ "dtype": "float16",
672
+ "shape": [1, 17, 12],
673
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
674
+ },
675
+ "keyT": {
676
+ "dtype": "float16",
677
+ "shape": [1, 17, 12],
678
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
679
+ },
680
+ "valueT": {
681
+ "dtype": "float16",
682
+ "shape": [1, 17, 20],
683
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
684
+ },
685
+ "pastStateT": {
686
+ "dtype": "float16",
687
+ "shape": [1, 4, 6, 5],
688
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
689
+ }
690
+ },
691
+ "outputs": {
692
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20] },
693
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5] }
694
+ },
695
+ "preset": "stress",
696
+ "bench": {
697
+ "primary": true,
698
+ "metrics": [
699
+ {
700
+ "type": "gflops",
701
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
702
+ }
703
+ ]
704
+ }
705
+ },
706
+ {
707
+ "name": "bench-linear_geometry_float16_float32_g0_zero",
708
+ "provenance": {
709
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
710
+ },
711
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
712
+ "inputs": {
713
+ "queryT": {
714
+ "dtype": "float16",
715
+ "shape": [2, 33, 32],
716
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
717
+ },
718
+ "keyT": {
719
+ "dtype": "float16",
720
+ "shape": [2, 33, 8],
721
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
722
+ },
723
+ "valueT": {
724
+ "dtype": "float16",
725
+ "shape": [2, 33, 14],
726
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
727
+ }
728
+ },
729
+ "outputs": {
730
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28] },
731
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
732
+ },
733
+ "preset": "stress",
734
+ "bench": {
735
+ "primary": true,
736
+ "metrics": [
737
+ {
738
+ "type": "gflops",
739
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
740
+ }
741
+ ]
742
+ }
743
+ },
744
+ {
745
+ "name": "bench-linear_geometry_float16_float32_g0_state",
746
+ "provenance": {
747
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
748
+ },
749
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
750
+ "inputs": {
751
+ "queryT": {
752
+ "dtype": "float16",
753
+ "shape": [2, 33, 32],
754
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
755
+ },
756
+ "keyT": {
757
+ "dtype": "float16",
758
+ "shape": [2, 33, 8],
759
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
760
+ },
761
+ "valueT": {
762
+ "dtype": "float16",
763
+ "shape": [2, 33, 14],
764
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
765
+ },
766
+ "pastStateT": {
767
+ "dtype": "float32",
768
+ "shape": [3, 2, 2, 8, 7],
769
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
770
+ }
771
+ },
772
+ "outputs": {
773
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28] },
774
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
775
+ },
776
+ "preset": "stress",
777
+ "bench": {
778
+ "primary": true,
779
+ "metrics": [
780
+ {
781
+ "type": "gflops",
782
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
783
+ }
784
+ ]
785
+ }
786
+ },
787
+ {
788
+ "name": "bench-linear_geometry_float16_float32_g1_zero",
789
+ "provenance": {
790
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
791
+ },
792
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
793
+ "inputs": {
794
+ "queryT": {
795
+ "dtype": "float16",
796
+ "shape": [1, 17, 12],
797
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
798
+ },
799
+ "keyT": {
800
+ "dtype": "float16",
801
+ "shape": [1, 17, 12],
802
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
803
+ },
804
+ "valueT": {
805
+ "dtype": "float16",
806
+ "shape": [1, 17, 20],
807
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
808
+ }
809
+ },
810
+ "outputs": {
811
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20] },
812
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
813
+ },
814
+ "preset": "stress",
815
+ "bench": {
816
+ "primary": true,
817
+ "metrics": [
818
+ {
819
+ "type": "gflops",
820
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
821
+ }
822
+ ]
823
+ }
824
+ },
825
+ {
826
+ "name": "bench-linear_geometry_float16_float32_g1_state",
827
+ "provenance": {
828
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
829
+ },
830
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
831
+ "inputs": {
832
+ "queryT": {
833
+ "dtype": "float16",
834
+ "shape": [1, 17, 12],
835
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
836
+ },
837
+ "keyT": {
838
+ "dtype": "float16",
839
+ "shape": [1, 17, 12],
840
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
841
+ },
842
+ "valueT": {
843
+ "dtype": "float16",
844
+ "shape": [1, 17, 20],
845
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
846
+ },
847
+ "pastStateT": {
848
+ "dtype": "float32",
849
+ "shape": [1, 4, 6, 5],
850
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
851
+ }
852
+ },
853
+ "outputs": {
854
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20] },
855
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
856
+ },
857
+ "preset": "stress",
858
+ "bench": {
859
+ "primary": true,
860
+ "metrics": [
861
+ {
862
+ "type": "gflops",
863
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
864
+ }
865
+ ]
866
+ }
867
+ },
868
+ {
869
+ "name": "bench-linear_geometry_float32_float16_g0_zero",
870
+ "provenance": {
871
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
872
+ },
873
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
874
+ "inputs": {
875
+ "queryT": {
876
+ "dtype": "float32",
877
+ "shape": [2, 33, 32],
878
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
879
+ },
880
+ "keyT": {
881
+ "dtype": "float32",
882
+ "shape": [2, 33, 8],
883
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
884
+ },
885
+ "valueT": {
886
+ "dtype": "float32",
887
+ "shape": [2, 33, 14],
888
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
889
+ }
890
+ },
891
+ "outputs": {
892
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
893
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7] }
894
+ },
895
+ "preset": "stress",
896
+ "bench": {
897
+ "primary": true,
898
+ "metrics": [
899
+ {
900
+ "type": "gflops",
901
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
902
+ }
903
+ ]
904
+ }
905
+ },
906
+ {
907
+ "name": "bench-linear_geometry_float32_float16_g0_state",
908
+ "provenance": {
909
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
910
+ },
911
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
912
+ "inputs": {
913
+ "queryT": {
914
+ "dtype": "float32",
915
+ "shape": [2, 33, 32],
916
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
917
+ },
918
+ "keyT": {
919
+ "dtype": "float32",
920
+ "shape": [2, 33, 8],
921
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
922
+ },
923
+ "valueT": {
924
+ "dtype": "float32",
925
+ "shape": [2, 33, 14],
926
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
927
+ },
928
+ "pastStateT": {
929
+ "dtype": "float16",
930
+ "shape": [3, 2, 2, 8, 7],
931
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
932
+ }
933
+ },
934
+ "outputs": {
935
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
936
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7] }
937
+ },
938
+ "preset": "stress",
939
+ "bench": {
940
+ "primary": true,
941
+ "metrics": [
942
+ {
943
+ "type": "gflops",
944
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
945
+ }
946
+ ]
947
+ }
948
+ },
949
+ {
950
+ "name": "bench-linear_geometry_float32_float16_g1_zero",
951
+ "provenance": {
952
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
953
+ },
954
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
955
+ "inputs": {
956
+ "queryT": {
957
+ "dtype": "float32",
958
+ "shape": [1, 17, 12],
959
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
960
+ },
961
+ "keyT": {
962
+ "dtype": "float32",
963
+ "shape": [1, 17, 12],
964
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
965
+ },
966
+ "valueT": {
967
+ "dtype": "float32",
968
+ "shape": [1, 17, 20],
969
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
970
+ }
971
+ },
972
+ "outputs": {
973
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
974
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5] }
975
+ },
976
+ "preset": "stress",
977
+ "bench": {
978
+ "primary": true,
979
+ "metrics": [
980
+ {
981
+ "type": "gflops",
982
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
983
+ }
984
+ ]
985
+ }
986
+ },
987
+ {
988
+ "name": "bench-linear_geometry_float32_float16_g1_state",
989
+ "provenance": {
990
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
991
+ },
992
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
993
+ "inputs": {
994
+ "queryT": {
995
+ "dtype": "float32",
996
+ "shape": [1, 17, 12],
997
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
998
+ },
999
+ "keyT": {
1000
+ "dtype": "float32",
1001
+ "shape": [1, 17, 12],
1002
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
1003
+ },
1004
+ "valueT": {
1005
+ "dtype": "float32",
1006
+ "shape": [1, 17, 20],
1007
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
1008
+ },
1009
+ "pastStateT": {
1010
+ "dtype": "float16",
1011
+ "shape": [1, 4, 6, 5],
1012
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
1013
+ }
1014
+ },
1015
+ "outputs": {
1016
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
1017
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5] }
1018
+ },
1019
+ "preset": "stress",
1020
+ "bench": {
1021
+ "primary": true,
1022
+ "metrics": [
1023
+ {
1024
+ "type": "gflops",
1025
+ "value": "dim(shapes.queryT, 0) * dim(shapes.queryT, 1) * (dim(shapes.valueT, 2) / attrs.kv_num_heads) * (2 * (dim(shapes.queryT, 2) / attrs.q_num_heads) * (attrs.kv_num_heads + max(attrs.q_num_heads, attrs.kv_num_heads)) + max(attrs.q_num_heads, attrs.kv_num_heads))"
1026
+ }
1027
+ ]
1028
+ }
1029
  }
1030
  ]
1031
  }
build/webgpu/chunk-out.wgsl.jinja CHANGED
@@ -89,11 +89,10 @@ var<workgroup> pmrow: array<f32, TOKEN_ROWS * CHUNK>;
89
  @compute @workgroup_size(WG, 1, 1)
90
  fn main(
91
  @builtin(workgroup_id) wg: vec3<u32>,
92
- @builtin(num_workgroups) nwg: vec3<u32>,
93
  @builtin(local_invocation_id) lid: vec3<u32>,
94
  ) {
95
  let tid = lid.x;
96
- let flat = wg.x + wg.y * nwg.x;
97
  {{ emit_chunk_head_setup(needHeads=true) }}
98
  let out_heads = max(params.qNumHeads, params.kvNumHeads);
99
  let chunk = flat % num_chunks;
 
89
  @compute @workgroup_size(WG, 1, 1)
90
  fn main(
91
  @builtin(workgroup_id) wg: vec3<u32>,
 
92
  @builtin(local_invocation_id) lid: vec3<u32>,
93
  ) {
94
  let tid = lid.x;
95
+ let flat = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
96
  {{ emit_chunk_head_setup(needHeads=true) }}
97
  let out_heads = max(params.qNumHeads, params.kvNumHeads);
98
  let chunk = flat % num_chunks;
build/webgpu/chunk-prep.wgsl.jinja CHANGED
@@ -18,12 +18,11 @@ const WG: u32 = {{ workgroupSize }}u;
18
  @compute @workgroup_size(WG, 1, 1)
19
  fn main(
20
  @builtin(workgroup_id) wg: vec3<u32>,
21
- @builtin(num_workgroups) nwg: vec3<u32>,
22
  @builtin(local_invocation_id) lid: vec3<u32>,
23
  ) {
24
  // 2D-folded flat (batch * chunk) index: wg.y carries the high bits past the
25
- // maxComputeWorkgroupsPerDimension dispatch limit.
26
- let flat = wg.x + wg.y * nwg.x;
27
  let num_chunks = (params.seqLength + CHUNK - 1u) / CHUNK;
28
  let chunk = flat % num_chunks;
29
  let batch = flat / num_chunks;
 
18
  @compute @workgroup_size(WG, 1, 1)
19
  fn main(
20
  @builtin(workgroup_id) wg: vec3<u32>,
 
21
  @builtin(local_invocation_id) lid: vec3<u32>,
22
  ) {
23
  // 2D-folded flat (batch * chunk) index: wg.y carries the high bits past the
24
+ // per-axis dispatch fold width.
25
+ let flat = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
26
  let num_chunks = (params.seqLength + CHUNK - 1u) / CHUNK;
27
  let chunk = flat % num_chunks;
28
  let batch = flat / num_chunks;
build/webgpu/chunk-scan.wgsl.jinja CHANGED
@@ -94,13 +94,12 @@ var<workgroup> stage: array<f32, TOKEN_TILE * HEAD_DIM_K>;
94
  @compute @workgroup_size(WG, 1, 1)
95
  fn main(
96
  @builtin(workgroup_id) wg: vec3<u32>,
97
- @builtin(num_workgroups) nwg: vec3<u32>,
98
  @builtin(local_invocation_id) lid: vec3<u32>,
99
  ) {
100
  let tid = lid.x;
101
  let col = tid % TILE_V;
102
  let group = tid / TILE_V;
103
- let flat = wg.x + wg.y * nwg.x;
104
  {{ emit_chunk_head_setup() }}
105
  let v_tiles = HEAD_DIM_V / TILE_V;
106
  let v_tile = flat % v_tiles;
 
94
  @compute @workgroup_size(WG, 1, 1)
95
  fn main(
96
  @builtin(workgroup_id) wg: vec3<u32>,
 
97
  @builtin(local_invocation_id) lid: vec3<u32>,
98
  ) {
99
  let tid = lid.x;
100
  let col = tid % TILE_V;
101
  let group = tid / TILE_V;
102
+ let flat = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
103
  {{ emit_chunk_head_setup() }}
104
  let v_tiles = HEAD_DIM_V / TILE_V;
105
  let v_tile = flat % v_tiles;
build/webgpu/chunk-ut.wgsl.jinja CHANGED
@@ -89,11 +89,10 @@ var<workgroup> tile_til: array<f32, CHUNK * TK>;
89
  @compute @workgroup_size(WG, 1, 1)
90
  fn main(
91
  @builtin(workgroup_id) wg: vec3<u32>,
92
- @builtin(num_workgroups) nwg: vec3<u32>,
93
  @builtin(local_invocation_id) lid: vec3<u32>,
94
  ) {
95
  let tid = lid.x;
96
- let flat = wg.x + wg.y * nwg.x;
97
  {{ emit_chunk_head_setup() }}
98
  let chunk = flat % num_chunks;
99
  let head = (flat / num_chunks) % params.kvNumHeads;
 
89
  @compute @workgroup_size(WG, 1, 1)
90
  fn main(
91
  @builtin(workgroup_id) wg: vec3<u32>,
 
92
  @builtin(local_invocation_id) lid: vec3<u32>,
93
  ) {
94
  let tid = lid.x;
95
+ let flat = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
96
  {{ emit_chunk_head_setup() }}
97
  let chunk = flat % num_chunks;
98
  let head = (flat / num_chunks) % params.kvNumHeads;
build/webgpu/linear-attention.scalar.wgsl.jinja CHANGED
@@ -21,9 +21,9 @@ f16({{ expr }}){% else %}{{ expr }}{% endif %}
21
  let scale = select(inverseSqrt(f32(head_dim_k)), params.scale, params.scale != 0.0);
22
 
23
  // 2D-folded flat (batch*head*dv_tile) index: wg.y carries the high bits past
24
- // the maxComputeWorkgroupsPerDimension dispatch limit. Reduces to wg.x when nwg.y == 1; the batch_idx >=
25
  // params.batchSize guard drops the over-dispatched tail.
26
- let workgroup_idx = wg.x + wg.y * nwg.x;
27
  let dv_tile_idx = workgroup_idx % dv_tiles;
28
  let bh = workgroup_idx / dv_tiles;
29
  let head_idx = bh % params.kvNumHeads;
@@ -128,7 +128,6 @@ var<workgroup> broadcast_delta: array<f32, TILE_V>;
128
  @compute @workgroup_size(WG, 1, 1)
129
  fn main(
130
  @builtin(workgroup_id) wg: vec3<u32>,
131
- @builtin(num_workgroups) nwg: vec3<u32>,
132
  @builtin(local_invocation_id) lid: vec3<u32>{% if useSubgroups %},
133
  @builtin(subgroup_invocation_id) sg_lid: u32,
134
  @builtin(subgroup_size) sg_size: u32{% endif %}
 
21
  let scale = select(inverseSqrt(f32(head_dim_k)), params.scale, params.scale != 0.0);
22
 
23
  // 2D-folded flat (batch*head*dv_tile) index: wg.y carries the high bits past
24
+ // the per-axis dispatch fold width. Reduces to wg.x with no fold; the batch_idx >=
25
  // params.batchSize guard drops the over-dispatched tail.
26
+ let workgroup_idx = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
27
  let dv_tile_idx = workgroup_idx % dv_tiles;
28
  let bh = workgroup_idx / dv_tiles;
29
  let head_idx = bh % params.kvNumHeads;
 
128
  @compute @workgroup_size(WG, 1, 1)
129
  fn main(
130
  @builtin(workgroup_id) wg: vec3<u32>,
 
131
  @builtin(local_invocation_id) lid: vec3<u32>{% if useSubgroups %},
132
  @builtin(subgroup_invocation_id) sg_lid: u32,
133
  @builtin(subgroup_size) sg_size: u32{% endif %}
build/webgpu/linear-attention.serial.wgsl.jinja CHANGED
@@ -25,18 +25,43 @@ enable f16;
25
  {% endif %}
26
  {{ env.wgsl.resourceDeclarations }}
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  const HEAD_DIM_K: u32 = {{ headDimK }}u;
29
 
30
  // Barrier-free small-dk recurrence. One invocation owns one
31
  // (batch, kv-head, value-dimension) state column and keeps its complete dk
32
- // slice private across the sequence. This trades dk-lane parallelism for zero
33
- // workgroup synchronization. Selection limits this route to small dk.
34
  @compute @workgroup_size(1, 1, 1)
35
  fn main(
36
  @builtin(workgroup_id) wg: vec3<u32>,
37
- @builtin(num_workgroups) nwg: vec3<u32>,
38
  ) {
39
- let flat_idx = wg.x + wg.y * nwg.x;
40
  let head_dim_v = params.vPackedDim / params.kvNumHeads;
41
  let dv_idx = flat_idx % head_dim_v;
42
  let bh = flat_idx / head_dim_v;
 
25
  {% endif %}
26
  {{ env.wgsl.resourceDeclarations }}
27
 
28
+ {% if updateRule == "linear" %}
29
+ {% set geometry = [
30
+ ["batchSize", "u32", serialBatchSize],
31
+ ["seqLength", "u32", serialSeqLength],
32
+ ["qNumHeads", "u32", serialQNumHeads],
33
+ ["kvNumHeads", "u32", serialKvNumHeads],
34
+ ["qPackedDim", "u32", serialQPackedDim],
35
+ ["kPackedDim", "u32", serialKPackedDim],
36
+ ["vPackedDim", "u32", serialVPackedDim],
37
+ ["scale", "f32", serialScale]
38
+ ] %}
39
+ {% if hasStateWindow %}
40
+ {% set geometry = geometry + [["stateWindow", "u32", serialStateWindow], ["stateSlotStride", "u32", serialStateSlotStride]] %}
41
+ {% endif %}
42
+ struct SerialGeometry {
43
+ {% for field in geometry %}
44
+ {{ field[0] }}: {{ field[1] }},
45
+ {% endfor %}
46
+ }
47
+ const params = SerialGeometry(
48
+ {% for field in geometry %}
49
+ {% if field[1] == "f32" %}f32({{ field[2] }}){% else %}{{ field[2] }}u{% endif %},
50
+ {% endfor %}
51
+ );
52
+ {% endif %}
53
+
54
  const HEAD_DIM_K: u32 = {{ headDimK }}u;
55
 
56
  // Barrier-free small-dk recurrence. One invocation owns one
57
  // (batch, kv-head, value-dimension) state column and keeps its complete dk
58
+ // slice private across the sequence without workgroup synchronization. Small dk
59
+ // bounds the private state retained by each invocation.
60
  @compute @workgroup_size(1, 1, 1)
61
  fn main(
62
  @builtin(workgroup_id) wg: vec3<u32>,
 
63
  ) {
64
+ let flat_idx = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
65
  let head_dim_v = params.vPackedDim / params.kvNumHeads;
66
  let dv_idx = flat_idx % head_dim_v;
67
  let bh = flat_idx / head_dim_v;
build/webgpu/linear-attention.vec4.wgsl.jinja CHANGED
@@ -21,9 +21,9 @@ f16({{ expr }}){% else %}{{ expr }}{% endif %}
21
  let scale = select(inverseSqrt(f32(head_dim_k)), params.scale, params.scale != 0.0);
22
 
23
  // 2D-folded flat (batch*head*dv_tile) index: wg.y carries the high bits past
24
- // the maxComputeWorkgroupsPerDimension dispatch limit. Reduces to wg.x when nwg.y == 1; the batch_idx >=
25
  // params.batchSize guard drops the over-dispatched tail.
26
- let workgroup_idx = wg.x + wg.y * nwg.x;
27
  let dv_tile_idx = workgroup_idx % dv_tiles;
28
  let bh = workgroup_idx / dv_tiles;
29
  let head_idx = bh % params.kvNumHeads;
@@ -97,6 +97,38 @@ enable f16;
97
  enable subgroups;
98
  {% endif %}
99
  {{ env.wgsl.resourceDeclarations }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  // LANES threads cooperate on one value tile's reduction axis; DV_GROUPS such groups
102
  // share a workgroup so the per-token key/query/decay stream is fetched once for all of
@@ -117,10 +149,9 @@ const VEC4_LANES: u32 = 4u;
117
  // No-subgroup tier: shared-memory linear folds replace subgroupAdd. Every quantity a
118
  // token reduces is staged before one barrier and read back after it, so the barrier
119
  // count is a property of the token and not of how many quantities it reduces.
120
- // A lane's TILE_V components are adjacent, so they travel as one {{ redT }} word and
121
- // fold with {{ redWidth }}-wide adds -- the same per-component summation order as a
122
- // scalar fold, at a {{ redWidth }}th of the shared-memory transactions.
123
- // Folds stay within the caller's own lane group: groups own disjoint value tiles and
124
  // must not see each other's partials.
125
  var<workgroup> wg_fold: array<{{ redT }}, WG * {{ redSlots }}u>;
126
  var<workgroup> wg_fold_out: array<{{ redT }}, DV_GROUPS * {{ redSlots }}u>;
@@ -128,16 +159,26 @@ var<workgroup> wg_fold_out: array<{{ redT }}, DV_GROUPS * {{ redSlots }}u>;
128
  @compute @workgroup_size(WG, 1, 1)
129
  fn main(
130
  @builtin(workgroup_id) wg: vec3<u32>,
131
- @builtin(num_workgroups) nwg: vec3<u32>,
132
  @builtin(local_invocation_id) lid: vec3<u32>,
 
133
  ) {
 
 
 
 
 
 
 
 
 
 
 
134
  let tid = lid.x;
135
  let lane = tid % LANES;
136
- {% if dvGroups > 1 or not useSubgroups %}
137
  let dv_group = tid / LANES;
138
  {% endif %}
139
- let dk_base = lane * VEC4_LANES;
140
- {{ emit_tiled_setup(dvGroups=dvGroups) }}
141
  let lane_active = dk_base < head_dim_k;
142
 
143
  // state[j] holds 4 consecutive dk rows (the 4 components) for dv slot j.
@@ -183,10 +224,9 @@ fn main(
183
  }
184
  }
185
  {% endif %}
186
- // A token's key and query do not depend on the recurrent state, so they are fetched one
187
- // iteration ahead: the fetch for t+1 is issued before the reductions for t, and its memory
188
- // latency overlaps the dependent chain instead of stalling in front of it. The state
189
- // recurrence is what serializes this loop, and it leaves the load unit idle otherwise.
190
  {% macro load_key(token) %}
191
  if (lane_active) {
192
  let k_base = ({{ token }} * params.kPackedDim + key_head_idx * head_dim_k) / VEC4_LANES + lane;
 
21
  let scale = select(inverseSqrt(f32(head_dim_k)), params.scale, params.scale != 0.0);
22
 
23
  // 2D-folded flat (batch*head*dv_tile) index: wg.y carries the high bits past
24
+ // the per-axis dispatch fold width. Reduces to wg.x with no fold; the batch_idx >=
25
  // params.batchSize guard drops the over-dispatched tail.
26
+ let workgroup_idx = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
27
  let dv_tile_idx = workgroup_idx % dv_tiles;
28
  let bh = workgroup_idx / dv_tiles;
29
  let head_idx = bh % params.kvNumHeads;
 
97
  enable subgroups;
98
  {% endif %}
99
  {{ env.wgsl.resourceDeclarations }}
100
+ {% if useSubgroups %}
101
+ {% set skipLogicalLaneCount = true %}
102
+ {% set skipLogicalLaneCount = skipLogicalLaneCount is defined and skipLogicalLaneCount %}
103
+ var<workgroup> sgLaneClaims: atomic<u32>;
104
+
105
+ struct SubgroupLogicalLanes {
106
+ ltid: u32,
107
+ ord: u32,
108
+ rank: u32,
109
+ count: u32,
110
+ }
111
+
112
+ fn subgroup_logical_lanes() -> SubgroupLogicalLanes {
113
+ let rank = subgroupExclusiveAdd(1u);
114
+ let count = subgroupAdd(1u);
115
+ // Every lane performs the atomic so no collective follows a lane guard (a
116
+ // subgroup op after a closed `if (rank == 0u)` block is the reconvergence
117
+ // hazard the render gate flags): only the rank-0 lane adds its subgroup's
118
+ // claim, every other lane adds 0 and discards its snapshot. The rank-0 lane is
119
+ // the lowest active lane, which is the lane `subgroupBroadcastFirst` reads.
120
+ let ticket = atomicAdd(&sgLaneClaims, select(0u, count | (1u << 16u), rank == 0u));
121
+ let claim = subgroupBroadcastFirst(ticket);
122
+ return SubgroupLogicalLanes((claim & 0xffffu) + rank, claim >> 16u, rank, count);
123
+ }
124
+ {% if not skipLogicalLaneCount %}
125
+
126
+ fn subgroup_logical_count() -> u32 {
127
+ return atomicLoad(&sgLaneClaims) >> 16u;
128
+ }
129
+ {% endif %}
130
+
131
+ {% endif %}
132
 
133
  // LANES threads cooperate on one value tile's reduction axis; DV_GROUPS such groups
134
  // share a workgroup so the per-token key/query/decay stream is fetched once for all of
 
149
  // No-subgroup tier: shared-memory linear folds replace subgroupAdd. Every quantity a
150
  // token reduces is staged before one barrier and read back after it, so the barrier
151
  // count is a property of the token and not of how many quantities it reduces.
152
+ // A lane's TILE_V components are adjacent, so they are stored as one {{ redT }} word
153
+ // and fold with {{ redWidth }}-wide adds in the scalar fold's per-component order.
154
+ // Folds stay within each lane group: groups own disjoint value tiles and
 
155
  // must not see each other's partials.
156
  var<workgroup> wg_fold: array<{{ redT }}, WG * {{ redSlots }}u>;
157
  var<workgroup> wg_fold_out: array<{{ redT }}, DV_GROUPS * {{ redSlots }}u>;
 
159
  @compute @workgroup_size(WG, 1, 1)
160
  fn main(
161
  @builtin(workgroup_id) wg: vec3<u32>,
162
+ {% if not useSubgroups %}
163
  @builtin(local_invocation_id) lid: vec3<u32>,
164
+ {% endif %}
165
  ) {
166
+ {% if useSubgroups %}
167
+ // Subgroup tier: selection admits this arm only on adapters whose fixed subgroup
168
+ // width equals LANES, so every subgroup of the workgroup is exactly one lane group
169
+ // and subgroupAdd over it is the group's reduction. The group and the lane come from
170
+ // the subgroup itself -- the dense claim order of the subgroup and the invocation's
171
+ // dense rank among its active lanes -- not from a partition of local_invocation_id,
172
+ // because WGSL does not promise which invocations share a subgroup.
173
+ let L = subgroup_logical_lanes();
174
+ let lane = L.rank;
175
+ let dv_group = L.ord;
176
+ {% else %}
177
  let tid = lid.x;
178
  let lane = tid % LANES;
 
179
  let dv_group = tid / LANES;
180
  {% endif %}
181
+ let dk_base = lane * VEC4_LANES;{{ emit_tiled_setup(dvGroups=(2 if useSubgroups else dvGroups)) }}
 
182
  let lane_active = dk_base < head_dim_k;
183
 
184
  // state[j] holds 4 consecutive dk rows (the 4 components) for dv slot j.
 
224
  }
225
  }
226
  {% endif %}
227
+ // A token's key and query do not depend on the recurrent state. Fetch t+1
228
+ // before completing the reductions for t so the independent load is issued
229
+ // ahead of its first use.
 
230
  {% macro load_key(token) %}
231
  if (lane_active) {
232
  let k_base = ({{ token }} * params.kPackedDim + key_head_idx * head_dim_k) / VEC4_LANES + lane;
build/webgpu/manifest.json CHANGED
The diff for this file is too large to render. See raw diff
 
build/webgpu/metadata.json CHANGED
@@ -1,24 +1,56 @@
1
  {
2
  "name": "com.microsoft.LinearAttention",
3
- "id": "_com_microsoft_linearattention_webgpu_dc21501",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "UnbCbJjzzX17ihI6vVaaXbbgxyNRzghqQ9jFtny3sZs=",
11
- "chunk-out.wgsl.jinja": "n2BVpE+dJE8bWsb3Vv9EZalVcE6Bpr0+RYnUxCliSSQ=",
12
- "chunk-prep.wgsl.jinja": "U9V7Cld2Zou0sX77ohI+0hmq+UZUsMQohiWrCLFrFm0=",
13
- "chunk-scan.wgsl.jinja": "4yrz7CYz19GJAxJ5cBmc4XJt6aXa1ICUi+fg4ma2+mM=",
14
- "chunk-ut.wgsl.jinja": "9KqJJCt5PwygvCrBjjy8oglG1ly4HiumH2Im/4Zlz5M=",
15
- "linear-attention.scalar.wgsl.jinja": "mc7lY/Xgl1LkvRxiCeaSZ2DwldVlIeoTti2QFqhvCiI=",
16
- "linear-attention.serial.wgsl.jinja": "p2AF3fRzazYsMPvGijvV3UyJhxvVxdZJyOhCYLRCJgI=",
17
- "linear-attention.vec4.wgsl.jinja": "heG1tNzKu8g1M6PqKtDFED/CzGMDA/qWvlTuZ4Z557M=",
18
- "manifest.json": "75pbsCWWrC10tm4NEaVyyoM7nDVgCqxwvj8Bh568M2g=",
19
- "test.json": "NtZMMzB3k6UYTuxbvHod7FJlq+HqMV0oYuZi/qhW8sM="
20
  }
21
  },
22
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
23
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.LinearAttention" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
 
1
  {
2
  "name": "com.microsoft.LinearAttention",
3
+ "id": "_com_microsoft_linearattention_webgpu_13a2aaf",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "SADv8TDNnXFGZdNUCqGMV5D9W9IKy2+gSRRNOZvxh/4=",
11
+ "chunk-out.wgsl.jinja": "VyrRYpaK7TsAfpKBMZpBiusQfnbDGhS5QIvnA/Vk6Tg=",
12
+ "chunk-prep.wgsl.jinja": "Iq5ZLzNK/CtE7/i7Tm4pZi5rxMKq6dZ4wEpBqrapUdQ=",
13
+ "chunk-scan.wgsl.jinja": "aPOMJxL7BhXVa05PwX9Uxl86dRbnuc9DkEwoFZq1Vvo=",
14
+ "chunk-ut.wgsl.jinja": "MfZk82OOr4oJCJWi4vU/54BLerVSyS31uRsWeGDakw0=",
15
+ "linear-attention.scalar.wgsl.jinja": "O8NRGtK2wpemLnSk96xiGRl50mqbqHQ3TiEkZpCGlOc=",
16
+ "linear-attention.serial.wgsl.jinja": "gWOSDZOYAlaSi4wPdGqEq1UWfGxpVDxYTFeUdMDp6lw=",
17
+ "linear-attention.vec4.wgsl.jinja": "bUlUYYjiumu6uCY/V7+EnbuhmBljoZXUnskKNYS0Hac=",
18
+ "manifest.json": "7SA3QNshHZfuljHXCA9dVbCwtV4wiiH+4EO78NGq9mU=",
19
+ "test.json": "NQ2VDXoy+rsZMH/agLHNEpOXHOydLQq+C2veCcWdpyk="
20
  }
21
  },
22
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
23
+ "webgpu": {
24
+ "manifestSpec": "2.0",
25
+ "variants": {
26
+ "linear_zero_chunked": ["chunk-out.wgsl.jinja", "chunk-scan.wgsl.jinja"],
27
+ "linear_state_chunked": ["chunk-out.wgsl.jinja", "chunk-scan.wgsl.jinja"],
28
+ "gated_zero_chunked": ["chunk-out.wgsl.jinja", "chunk-prep.wgsl.jinja", "chunk-scan.wgsl.jinja"],
29
+ "gated_state_chunked": ["chunk-out.wgsl.jinja", "chunk-prep.wgsl.jinja", "chunk-scan.wgsl.jinja"],
30
+ "delta_zero_chunked": ["chunk-out.wgsl.jinja", "chunk-scan.wgsl.jinja", "chunk-ut.wgsl.jinja"],
31
+ "delta_state_chunked": ["chunk-out.wgsl.jinja", "chunk-scan.wgsl.jinja", "chunk-ut.wgsl.jinja"],
32
+ "gated_delta_zero_chunked": ["chunk-out.wgsl.jinja", "chunk-prep.wgsl.jinja", "chunk-scan.wgsl.jinja", "chunk-ut.wgsl.jinja"],
33
+ "gated_delta_state_chunked": ["chunk-out.wgsl.jinja", "chunk-prep.wgsl.jinja", "chunk-scan.wgsl.jinja", "chunk-ut.wgsl.jinja"],
34
+ "linear_zero_serial_small_dk": ["linear-attention.serial.wgsl.jinja"],
35
+ "linear_state_serial_small_dk": ["linear-attention.serial.wgsl.jinja"],
36
+ "gated_delta_zero_serial_small_dk": ["linear-attention.serial.wgsl.jinja"],
37
+ "gated_delta_state_serial_small_dk": ["linear-attention.serial.wgsl.jinja"],
38
+ "linear_zero_scalar": ["linear-attention.scalar.wgsl.jinja"],
39
+ "linear_state_scalar": ["linear-attention.scalar.wgsl.jinja"],
40
+ "gated_delta_zero_scalar": ["linear-attention.scalar.wgsl.jinja"],
41
+ "gated_delta_state_scalar": ["linear-attention.scalar.wgsl.jinja"],
42
+ "gated_zero_scalar": ["linear-attention.scalar.wgsl.jinja"],
43
+ "gated_state_scalar": ["linear-attention.scalar.wgsl.jinja"],
44
+ "delta_zero_scalar": ["linear-attention.scalar.wgsl.jinja"],
45
+ "delta_state_scalar": ["linear-attention.scalar.wgsl.jinja"],
46
+ "linear_zero_vec4": ["linear-attention.vec4.wgsl.jinja"],
47
+ "linear_state_vec4": ["linear-attention.vec4.wgsl.jinja"],
48
+ "gated_delta_zero_vec4": ["linear-attention.vec4.wgsl.jinja"],
49
+ "gated_delta_state_vec4": ["linear-attention.vec4.wgsl.jinja"],
50
+ "gated_zero_vec4": ["linear-attention.vec4.wgsl.jinja"],
51
+ "gated_state_vec4": ["linear-attention.vec4.wgsl.jinja"],
52
+ "delta_zero_vec4": ["linear-attention.vec4.wgsl.jinja"],
53
+ "delta_state_vec4": ["linear-attention.vec4.wgsl.jinja"]
54
+ }
55
+ }
56
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.LinearAttention",
3
  "cases": [
4
  {
5
  "name": "linear_zero_state",
@@ -328,7 +327,7 @@
328
  {
329
  "name": "gated_delta_scalar_headdim6_seq128_state",
330
  "provenance": {
331
- "notes": "Compact sibling for the long-sequence scalar gated-delta benchmark; preserves qHeads=kvHeads=4, headDimK=6 (non-vec4), past state, per-head decay/beta, and a multi-token recurrence loop."
332
  },
333
  "attrs": { "q_num_heads": 4, "kv_num_heads": 4, "update_rule": "gated_delta", "scale": 0.25 },
334
  "inputs": {
@@ -364,8 +363,8 @@
364
  }
365
  },
366
  "outputs": {
367
- "outputT": { "dtype": "float32", "shape": [1, 128, 48], "tolerance": 0.0005, "relTolerance": 0.0005 },
368
- "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 12], "tolerance": 0.0005, "relTolerance": 0.0005 }
369
  }
370
  },
371
  {
@@ -534,7 +533,7 @@
534
  {
535
  "name": "linear_zero_scalar_f16_seq128",
536
  "provenance": {
537
- "notes": "Compact sibling for the f16 linear-rule scalar benchmark; f16 query/state force the scalar implementation while seq=128 keeps the zero-state recurrence loop model-shaped."
538
  },
539
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
540
  "inputs": {
@@ -555,14 +554,14 @@
555
  }
556
  },
557
  "outputs": {
558
- "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.04, "relTolerance": 0.04 },
559
- "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.04, "relTolerance": 0.04 }
560
  }
561
  },
562
  {
563
  "name": "linear_zero_f16_seq128_offset_value_scale_lock",
564
  "provenance": {
565
- "notes": "Scale lock for the zero-past-state linear recurrence over 128 tokens (the \"zero\" in linear_zero names the absent past_state, not the output). Its sibling linear_zero_scalar_f16_seq128 drives q/k/v at amplitude 0.03 about zero, so the state is a 128-term random walk of zero-mean outer products and the output peaks at 1.7e-3 against a 0.04 absolute tolerance - blind to any multiplicative error below 24x, and 13.5x on present_state. Here the key carries a small positive DC and V oscillates about 0.5, so the accumulated state peaks at 1.5 and the output at 2.0; halving either output now scores ~30x the allowed error. Same shape, args and dtypes, so it still covers linear_zero_serial_small_dk, linear_zero_scalar and linear_zero_vec4."
566
  },
567
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
568
  "inputs": {
@@ -590,7 +589,7 @@
590
  {
591
  "name": "linear_state_scalar_f16_seq128",
592
  "provenance": {
593
- "notes": "Compact correctness sibling for the long supplied-state f16 pathology benchmark. It selects linear_state_serial_small_dk and validates that the initial state is incorporated rather than silently treated as zero."
594
  },
595
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
596
  "inputs": {
@@ -616,14 +615,14 @@
616
  }
617
  },
618
  "outputs": {
619
- "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.04, "relTolerance": 0.04 },
620
- "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.04, "relTolerance": 0.04 }
621
  }
622
  },
623
  {
624
  "name": "linear_state_f16_seq128_offset_value_scale_lock",
625
  "provenance": {
626
- "notes": "Supplied-past-state arm of the same lock. linear_state_scalar_f16_seq128 claims to validate that the initial state is incorporated rather than treated as zero, but at amplitude 0.03 its output peaks at 4.4e-3 against a 0.04 absolute tolerance (9.1x blind) and the past state itself is scaled 0.01, so zeroing past_state entirely stays well inside the tolerance. With the past state at amplitude 0.3, a keyed DC and V about 0.5, the output peaks at 1.3 and the state at 1.5: dropping past_state now scores 11x the allowed error on the output and 35x on present_state, and halving either scores ~28x."
627
  },
628
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
629
  "inputs": {
@@ -804,7 +803,7 @@
804
  {
805
  "name": "gated_delta_default_rule_no_updateRule_arg",
806
  "provenance": {
807
- "notes": "updateRule OMITTED -> manifest derive.effRule defaults to gated_delta (host and reference agree). Requires decay+beta+past_state. headDimK=4 (%4==0) so vec4 path. Verifies the default-rule contract across manifest/host/reference."
808
  },
809
  "attrs": { "q_num_heads": 2, "kv_num_heads": 1 },
810
  "inputs": {
@@ -847,7 +846,7 @@
847
  {
848
  "name": "gated_delta_f32_dk128_dv128_compact",
849
  "provenance": {
850
- "notes": "Compact correctness lock for the Bonsai-shaped dK=dV=128 gated-delta benchmark. qHeads=4, kvHeads=2 and seq=2 bound CPU reference work while preserving GQA state sharing and the f32 vec4 path."
851
  },
852
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 },
853
  "inputs": {
@@ -883,8 +882,13 @@
883
  }
884
  },
885
  "outputs": {
886
- "outputT": { "dtype": "float32", "shape": [1, 2, 512], "tolerance": 0.0008, "relTolerance": 0.0008 },
887
- "presentStateT": { "dtype": "float32", "shape": [1, 2, 128, 128], "tolerance": 0.0008, "relTolerance": 0.0008 }
 
 
 
 
 
888
  }
889
  },
890
  {
@@ -933,7 +937,7 @@
933
  {
934
  "name": "gated_delta_f16_dk128_dv128_compact",
935
  "provenance": {
936
- "notes": "Float16 sibling of the dK=dV=128 gated-delta lock. It preserves the model-shaped head dimensions and scalar f16 route while remaining small enough for CPU reference validation."
937
  },
938
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 },
939
  "inputs": {
@@ -969,8 +973,8 @@
969
  }
970
  },
971
  "outputs": {
972
- "outputT": { "dtype": "float16", "shape": [1, 2, 512], "tolerance": 0.08, "relTolerance": 0.08 },
973
- "presentStateT": { "dtype": "float16", "shape": [1, 2, 128, 128], "tolerance": 0.08, "relTolerance": 0.08 }
974
  }
975
  },
976
  {
@@ -1097,7 +1101,7 @@
1097
  "provenance": {
1098
  "source": "onnxruntime/contrib_ops/webgpu/bert/linear_attention.cc",
1099
  "test": "head-size-driven workgroup sizing",
1100
- "notes": "One vec4 lane per four dk rows, so head_dim_k = 2048 needs 512 lanes. The width was pinned to 256 and the head dim to 1024 to match; both are now taken from the device, which is what the second half of the old guard already did. A device that cannot run 512 invocations per workgroup still cannot serve this shape, so the case declares that limit rather than claiming to be universal. Query and key are positive cycles of length 5 and 6 rather than sinusoids: a signed 2048-term dot product cancels down to ~1e-4, where no tolerance the f32 summation order allows can see a scale error. Both cycle lengths are coprime with the four-lane vec4 stride, so every lane still sees every value."
1101
  },
1102
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear" },
1103
  "inputs": {
@@ -1153,7 +1157,7 @@
1153
  {
1154
  "name": "linear_state_scalar_dk17_above_serial_cap_unaligned",
1155
  "provenance": {
1156
- "notes": "Supplied-state sibling of the dk=17 scalar lock: headDimK=17 defeats serialHeadDimFits (dk<=16) and the %4 vec4 guard, so the scalar kernel runs with a past state and must fold it into the recurrence instead of starting from zeros."
1157
  },
1158
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 },
1159
  "inputs": {
@@ -1186,7 +1190,7 @@
1186
  {
1187
  "name": "linear_state_vec4_dk20_above_serial_cap",
1188
  "provenance": {
1189
- "notes": "headDimK=20 is %4 (vec4-eligible) but above serialHeadDimFits (dk<=16), so the supplied-state vec4 kernel wins instead of linear_state_serial_small_dk. WG=pow2ceil(ceil(20/4))=8 leaves 3 idle lanes, and head_dim_v=3 exercises a partial dv tile on both output and present_state."
1190
  },
1191
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 },
1192
  "inputs": {
@@ -1298,7 +1302,7 @@
1298
  {
1299
  "name": "linear_state_scalar_dk17_window3",
1300
  "provenance": {
1301
- "notes": "Windowed twin of linear_state_scalar_dk17_above_serial_cap_unaligned, which is the case that selects this variant. The pinned serial window pair anchors the reference; this one checks that the same variant's per-token snapshot and slot indexing agree with it."
1302
  },
1303
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 3 },
1304
  "inputs": {
@@ -1331,7 +1335,7 @@
1331
  {
1332
  "name": "linear_state_vec4_dk20_window2",
1333
  "provenance": {
1334
- "notes": "Windowed twin of linear_state_vec4_dk20_above_serial_cap, which is the case that selects this variant. The pinned serial window pair anchors the reference; this one checks that the same variant's per-token snapshot and slot indexing agree with it."
1335
  },
1336
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 },
1337
  "inputs": {
@@ -1393,7 +1397,7 @@
1393
  {
1394
  "name": "linear_zero_vec4_window2",
1395
  "provenance": {
1396
- "notes": "Windowed zero-state coverage for the linear vec4 route, including per-token present-state slots."
1397
  },
1398
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 },
1399
  "inputs": {
@@ -1420,7 +1424,9 @@
1420
  },
1421
  {
1422
  "name": "gated_zero_window2_all_routes",
1423
- "provenance": { "notes": "Windowed zero-state coverage for every eligible gated serial, scalar, and vec4 route." },
 
 
1424
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5, "state_window": 2 },
1425
  "inputs": {
1426
  "queryT": {
@@ -1448,7 +1454,7 @@
1448
  {
1449
  "name": "gated_state_window2_all_routes",
1450
  "provenance": {
1451
- "notes": "Windowed carried-state coverage for every eligible gated serial, scalar, and vec4 route."
1452
  },
1453
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5, "state_window": 2 },
1454
  "inputs": {
@@ -1481,7 +1487,9 @@
1481
  },
1482
  {
1483
  "name": "delta_zero_window2_all_routes",
1484
- "provenance": { "notes": "Windowed zero-state coverage for every eligible delta scalar and vec4 route." },
 
 
1485
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5, "state_window": 2 },
1486
  "inputs": {
1487
  "queryT": {
@@ -1508,7 +1516,9 @@
1508
  },
1509
  {
1510
  "name": "delta_state_window2_all_routes",
1511
- "provenance": { "notes": "Windowed carried-state coverage for every eligible delta scalar and vec4 route." },
 
 
1512
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5, "state_window": 2 },
1513
  "inputs": {
1514
  "queryT": {
@@ -1541,7 +1551,7 @@
1541
  {
1542
  "name": "gated_delta_zero_window2_all_routes",
1543
  "provenance": {
1544
- "notes": "Windowed zero-state coverage for every eligible gated-delta serial, scalar, and vec4 route."
1545
  },
1546
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5, "state_window": 2 },
1547
  "inputs": {
@@ -1571,7 +1581,7 @@
1571
  {
1572
  "name": "gated_delta_state_window2_all_routes",
1573
  "provenance": {
1574
- "notes": "Windowed carried-state coverage for every eligible gated-delta serial, scalar, and vec4 route."
1575
  },
1576
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5, "state_window": 2 },
1577
  "inputs": {
@@ -1839,7 +1849,7 @@
1839
  {
1840
  "name": "delta_with_initial_state",
1841
  "provenance": {
1842
- "notes": "Covers the schema-valid delta recurrence with a supplied past_state; providers consume the same optional state for all four update rules."
1843
  },
1844
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5 },
1845
  "inputs": {
@@ -1939,7 +1949,7 @@
1939
  {
1940
  "name": "gated_delta_zero_scalar_dk17_no_past",
1941
  "provenance": {
1942
- "notes": "Omitted past_state with headDimK=17 crosses the serial-kernel ceiling and is not divisible by four, selecting the scalar zero-state recurrence rather than a state-bearing or vec4 sibling."
1943
  },
1944
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.25 },
1945
  "inputs": {
@@ -2293,7 +2303,7 @@
2293
  {
2294
  "name": "gated_delta_state_chunked_f16_seq1024",
2295
  "provenance": {
2296
- "notes": "float16 activations and state through the chunked prefill decomposition, which needs seqLength >= 1024. The chunk passes accumulate in float32 and only their loads and stores are narrowed, so this is the fixture that exercises the f16 enable in every pass."
2297
  },
2298
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.35 },
2299
  "inputs": {
@@ -2332,6 +2342,802 @@
2332
  "outputT": { "dtype": "float16", "shape": [1, 1024, 32], "tolerance": 0.04, "relTolerance": 0.04 },
2333
  "presentStateT": { "dtype": "float16", "shape": [1, 2, 8, 8], "tolerance": 0.04, "relTolerance": 0.04 }
2334
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2335
  }
2336
  ]
2337
  }
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "linear_zero_state",
 
327
  {
328
  "name": "gated_delta_scalar_headdim6_seq128_state",
329
  "provenance": {
330
+ "notes": "A compact multi-token gated-delta recurrence uses four query/KV heads, non-four-wide key head size 6, past state, and per-head decay and beta."
331
  },
332
  "attrs": { "q_num_heads": 4, "kv_num_heads": 4, "update_rule": "gated_delta", "scale": 0.25 },
333
  "inputs": {
 
363
  }
364
  },
365
  "outputs": {
366
+ "outputT": { "dtype": "float32", "shape": [1, 128, 48], "tolerance": 0.00005, "relTolerance": 0.00005 },
367
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 12], "tolerance": 0.00005, "relTolerance": 0.00005 }
368
  }
369
  },
370
  {
 
533
  {
534
  "name": "linear_zero_scalar_f16_seq128",
535
  "provenance": {
536
+ "notes": "Float16 query and state select the scalar linear-rule implementation over a model-shaped 128-token zero-state recurrence."
537
  },
538
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
539
  "inputs": {
 
554
  }
555
  },
556
  "outputs": {
557
+ "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.00004, "relTolerance": 0.002 },
558
+ "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.00004, "relTolerance": 0.002 }
559
  }
560
  },
561
  {
562
  "name": "linear_zero_f16_seq128_offset_value_scale_lock",
563
  "provenance": {
564
+ "notes": "A 128-token float16 recurrence omits past state but does not produce zero output. Positive-offset keys and values around 0.5 keep output and present state at order-one magnitude, making multiplicative errors observable on the serial, scalar, and vec4 zero-state routes."
565
  },
566
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
567
  "inputs": {
 
589
  {
590
  "name": "linear_state_scalar_f16_seq128",
591
  "provenance": {
592
+ "notes": "A compact float16 recurrence selects the serial small-key-dimension route and verifies that its supplied initial state is incorporated."
593
  },
594
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
595
  "inputs": {
 
615
  }
616
  },
617
  "outputs": {
618
+ "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.0002, "relTolerance": 0.004 },
619
+ "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.0002, "relTolerance": 0.004 }
620
  }
621
  },
622
  {
623
  "name": "linear_state_f16_seq128_offset_value_scale_lock",
624
  "provenance": {
625
+ "notes": "A supplied past state at amplitude 0.3, positive-offset keys, and values around 0.5 keep both outputs at order-one magnitude. Dropping the initial state or uniformly rescaling either output therefore exceeds tolerance."
626
  },
627
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 },
628
  "inputs": {
 
803
  {
804
  "name": "gated_delta_default_rule_no_updateRule_arg",
805
  "provenance": {
806
+ "notes": "Omitting `updateRule` selects the schema-default gated-delta recurrence with decay, beta, and past state. Key head size 4 exercises the vec4 route."
807
  },
808
  "attrs": { "q_num_heads": 2, "kv_num_heads": 1 },
809
  "inputs": {
 
846
  {
847
  "name": "gated_delta_f32_dk128_dv128_compact",
848
  "provenance": {
849
+ "notes": "A compact gated-delta case preserves Bonsai's 128-wide key/value heads, four query heads, two KV heads, shared GQA state, and the float32 vec4 path."
850
  },
851
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 },
852
  "inputs": {
 
882
  }
883
  },
884
  "outputs": {
885
+ "outputT": { "dtype": "float32", "shape": [1, 2, 512], "tolerance": 0.000005, "relTolerance": 0.0005 },
886
+ "presentStateT": {
887
+ "dtype": "float32",
888
+ "shape": [1, 2, 128, 128],
889
+ "tolerance": 0.000005,
890
+ "relTolerance": 0.0005
891
+ }
892
  }
893
  },
894
  {
 
937
  {
938
  "name": "gated_delta_f16_dk128_dv128_compact",
939
  "provenance": {
940
+ "notes": "Float16 tensors with 128-wide key/value heads exercise the scalar gated-delta route at a compact sequence length."
941
  },
942
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 },
943
  "inputs": {
 
973
  }
974
  },
975
  "outputs": {
976
+ "outputT": { "dtype": "float16", "shape": [1, 2, 512], "tolerance": 0.000015, "relTolerance": 0.003 },
977
+ "presentStateT": { "dtype": "float16", "shape": [1, 2, 128, 128], "tolerance": 0.0002, "relTolerance": 0.003 }
978
  }
979
  },
980
  {
 
1101
  "provenance": {
1102
  "source": "onnxruntime/contrib_ops/webgpu/bert/linear_attention.cc",
1103
  "test": "head-size-driven workgroup sizing",
1104
+ "notes": "One vec4 lane handles four key rows, so key head size 2048 requires a 512-invocation workgroup and declares that adapter limit. Positive query/key cycles of coprime lengths 5 and 6 avoid cancellation while ensuring every four-wide lane sees every value."
1105
  },
1106
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear" },
1107
  "inputs": {
 
1157
  {
1158
  "name": "linear_state_scalar_dk17_above_serial_cap_unaligned",
1159
  "provenance": {
1160
+ "notes": "Key head size 17 exceeds the serial limit and is not divisible by four, selecting the scalar recurrence with a supplied past state."
1161
  },
1162
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 },
1163
  "inputs": {
 
1190
  {
1191
  "name": "linear_state_vec4_dk20_above_serial_cap",
1192
  "provenance": {
1193
+ "notes": "A key head dimension of 20 uses five four-value vectors in an eight-lane workgroup, while value head dimension 3 produces a partial output and present-state tile."
1194
  },
1195
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 },
1196
  "inputs": {
 
1302
  {
1303
  "name": "linear_state_scalar_dk17_window3",
1304
  "provenance": {
1305
+ "notes": "A three-slot window on the scalar, key-head-size-17 route verifies per-token state snapshots and slot indexing with a supplied initial state."
1306
  },
1307
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 3 },
1308
  "inputs": {
 
1335
  {
1336
  "name": "linear_state_vec4_dk20_window2",
1337
  "provenance": {
1338
+ "notes": "A two-slot window on the vec4, key-head-size-20 route verifies per-token state snapshots and slot indexing with a supplied initial state."
1339
  },
1340
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 },
1341
  "inputs": {
 
1397
  {
1398
  "name": "linear_zero_vec4_window2",
1399
  "provenance": {
1400
+ "notes": "A zero-state linear recurrence writes per-token present-state slots on the vec4 route."
1401
  },
1402
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 },
1403
  "inputs": {
 
1424
  },
1425
  {
1426
  "name": "gated_zero_window2_all_routes",
1427
+ "provenance": {
1428
+ "notes": "A zero-state gated recurrence exercises window publication on its serial, scalar, and vec4 routes."
1429
+ },
1430
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5, "state_window": 2 },
1431
  "inputs": {
1432
  "queryT": {
 
1454
  {
1455
  "name": "gated_state_window2_all_routes",
1456
  "provenance": {
1457
+ "notes": "A carried-state gated recurrence exercises window publication on its serial, scalar, and vec4 routes."
1458
  },
1459
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5, "state_window": 2 },
1460
  "inputs": {
 
1487
  },
1488
  {
1489
  "name": "delta_zero_window2_all_routes",
1490
+ "provenance": {
1491
+ "notes": "A zero-state delta recurrence exercises window publication on its scalar and vec4 routes."
1492
+ },
1493
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5, "state_window": 2 },
1494
  "inputs": {
1495
  "queryT": {
 
1516
  },
1517
  {
1518
  "name": "delta_state_window2_all_routes",
1519
+ "provenance": {
1520
+ "notes": "A carried-state delta recurrence exercises window publication on its scalar and vec4 routes."
1521
+ },
1522
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5, "state_window": 2 },
1523
  "inputs": {
1524
  "queryT": {
 
1551
  {
1552
  "name": "gated_delta_zero_window2_all_routes",
1553
  "provenance": {
1554
+ "notes": "A zero-state gated-delta recurrence exercises window publication on its serial, scalar, and vec4 routes."
1555
  },
1556
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5, "state_window": 2 },
1557
  "inputs": {
 
1581
  {
1582
  "name": "gated_delta_state_window2_all_routes",
1583
  "provenance": {
1584
+ "notes": "A carried-state gated-delta recurrence exercises window publication on its serial, scalar, and vec4 routes."
1585
  },
1586
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5, "state_window": 2 },
1587
  "inputs": {
 
1849
  {
1850
  "name": "delta_with_initial_state",
1851
  "provenance": {
1852
+ "notes": "A schema-valid delta recurrence consumes the optional `past_state`, which has consistent meaning across all four update rules."
1853
  },
1854
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5 },
1855
  "inputs": {
 
1949
  {
1950
  "name": "gated_delta_zero_scalar_dk17_no_past",
1951
  "provenance": {
1952
+ "notes": "With past state omitted, key head size 17 exceeds the serial limit and is not divisible by four, selecting the scalar zero-state recurrence."
1953
  },
1954
  "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.25 },
1955
  "inputs": {
 
2303
  {
2304
  "name": "gated_delta_state_chunked_f16_seq1024",
2305
  "provenance": {
2306
+ "notes": "A 1,024-token chunked prefill uses float16 activations and state. Every pass accumulates in float32 and narrows only loads and stores."
2307
  },
2308
  "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.35 },
2309
  "inputs": {
 
2342
  "outputT": { "dtype": "float16", "shape": [1, 1024, 32], "tolerance": 0.04, "relTolerance": 0.04 },
2343
  "presentStateT": { "dtype": "float16", "shape": [1, 2, 8, 8], "tolerance": 0.04, "relTolerance": 0.04 }
2344
  }
2345
+ },
2346
+ {
2347
+ "name": "gated_delta_chunked_strong_decay_prefix_underflow",
2348
+ "provenance": {
2349
+ "notes": "Per-head decay -8 on every token of a 1024-token prefill: over a 16-token chunk the within-chunk decay prefix reaches exp(-128), below the f32 subnormal floor, so a chunked formulation that divides keys by exp(prefix) produces Inf/NaN while the recurrence itself applies a finite exp(-8) per token. Bounded Q/K/V, beta 0.5, no past state."
2350
+ },
2351
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta" },
2352
+ "inputs": {
2353
+ "queryT": {
2354
+ "dtype": "float32",
2355
+ "shape": [1, 1024, 128],
2356
+ "data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.033, "scale": 1.0 }
2357
+ },
2358
+ "keyT": {
2359
+ "dtype": "float32",
2360
+ "shape": [1, 1024, 64],
2361
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.027, "scale": 1.0 }
2362
+ },
2363
+ "valueT": {
2364
+ "dtype": "float32",
2365
+ "shape": [1, 1024, 64],
2366
+ "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 1.0 }
2367
+ },
2368
+ "decayT": { "dtype": "float32", "shape": [1, 1024, 2], "data": { "kind": "constant", "value": -8.0 } },
2369
+ "betaT": { "dtype": "float32", "shape": [1, 1024, 2], "data": { "kind": "constant", "value": 0.5 } }
2370
+ },
2371
+ "outputs": {
2372
+ "outputT": { "dtype": "float32", "shape": [1, 1024, 128], "tolerance": 0.0002, "relTolerance": 0.0005 },
2373
+ "presentStateT": { "dtype": "float32", "shape": [1, 2, 32, 32], "tolerance": 0.0002, "relTolerance": 0.0005 }
2374
+ },
2375
+ "skipGpu": {
2376
+ "category": "todo",
2377
+ "reason": "The chunked prefill decomposition materializes a normalized key as k divided by exp(the within-chunk decay prefix). A sustained decay of -8 drives that prefix to -128 over a 16-token chunk, so the exponential underflows to zero and the division returns Inf/NaN, while every pairwise term the decomposition actually needs carries the bounded ratio exp(p_t - p_s) <= 1. The serial route applies a finite exp(-8) per token and stays finite. Fixing this needs the chunk operands re-anchored to a per-chunk pivot, with the entry-state terms kept in their absolute form."
2378
+ }
2379
+ },
2380
+ {
2381
+ "name": "gated_delta_chunked_moderate_decay_control",
2382
+ "provenance": {
2383
+ "notes": "Decay -4 keeps each 16-token chunk prefix at -64, within the float32 exponent range, so `k / exp(prefix)` remains finite. It is the finite-prefix control for `gated_delta_chunked_strong_decay_prefix_underflow`."
2384
+ },
2385
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta" },
2386
+ "inputs": {
2387
+ "queryT": {
2388
+ "dtype": "float32",
2389
+ "shape": [1, 1024, 128],
2390
+ "data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.033, "scale": 1.0 }
2391
+ },
2392
+ "keyT": {
2393
+ "dtype": "float32",
2394
+ "shape": [1, 1024, 64],
2395
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.027, "scale": 1.0 }
2396
+ },
2397
+ "valueT": {
2398
+ "dtype": "float32",
2399
+ "shape": [1, 1024, 64],
2400
+ "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 1.0 }
2401
+ },
2402
+ "decayT": { "dtype": "float32", "shape": [1, 1024, 2], "data": { "kind": "constant", "value": -4.0 } },
2403
+ "betaT": { "dtype": "float32", "shape": [1, 1024, 2], "data": { "kind": "constant", "value": 0.5 } }
2404
+ },
2405
+ "outputs": {
2406
+ "outputT": { "dtype": "float32", "shape": [1, 1024, 128], "tolerance": 0.0002, "relTolerance": 0.0005 },
2407
+ "presentStateT": { "dtype": "float32", "shape": [1, 2, 32, 32], "tolerance": 0.0002, "relTolerance": 0.0005 }
2408
+ }
2409
+ },
2410
+ {
2411
+ "name": "linear_geometry_float32_float32_g0_zero",
2412
+ "provenance": {
2413
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2414
+ },
2415
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2416
+ "inputs": {
2417
+ "queryT": {
2418
+ "dtype": "float32",
2419
+ "shape": [2, 33, 32],
2420
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2421
+ },
2422
+ "keyT": {
2423
+ "dtype": "float32",
2424
+ "shape": [2, 33, 8],
2425
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2426
+ },
2427
+ "valueT": {
2428
+ "dtype": "float32",
2429
+ "shape": [2, 33, 14],
2430
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2431
+ }
2432
+ },
2433
+ "outputs": {
2434
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
2435
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
2436
+ }
2437
+ },
2438
+ {
2439
+ "name": "linear_geometry_float32_float32_g0_state",
2440
+ "provenance": {
2441
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2442
+ },
2443
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2444
+ "inputs": {
2445
+ "queryT": {
2446
+ "dtype": "float32",
2447
+ "shape": [2, 33, 32],
2448
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2449
+ },
2450
+ "keyT": {
2451
+ "dtype": "float32",
2452
+ "shape": [2, 33, 8],
2453
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2454
+ },
2455
+ "valueT": {
2456
+ "dtype": "float32",
2457
+ "shape": [2, 33, 14],
2458
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2459
+ },
2460
+ "pastStateT": {
2461
+ "dtype": "float32",
2462
+ "shape": [3, 2, 2, 8, 7],
2463
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2464
+ }
2465
+ },
2466
+ "outputs": {
2467
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
2468
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
2469
+ }
2470
+ },
2471
+ {
2472
+ "name": "linear_geometry_float32_float32_g1_zero",
2473
+ "provenance": {
2474
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2475
+ },
2476
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
2477
+ "inputs": {
2478
+ "queryT": {
2479
+ "dtype": "float32",
2480
+ "shape": [1, 17, 12],
2481
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2482
+ },
2483
+ "keyT": {
2484
+ "dtype": "float32",
2485
+ "shape": [1, 17, 12],
2486
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2487
+ },
2488
+ "valueT": {
2489
+ "dtype": "float32",
2490
+ "shape": [1, 17, 20],
2491
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2492
+ }
2493
+ },
2494
+ "outputs": {
2495
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
2496
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
2497
+ }
2498
+ },
2499
+ {
2500
+ "name": "linear_geometry_float32_float32_g1_state",
2501
+ "provenance": {
2502
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2503
+ },
2504
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
2505
+ "inputs": {
2506
+ "queryT": {
2507
+ "dtype": "float32",
2508
+ "shape": [1, 17, 12],
2509
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2510
+ },
2511
+ "keyT": {
2512
+ "dtype": "float32",
2513
+ "shape": [1, 17, 12],
2514
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2515
+ },
2516
+ "valueT": {
2517
+ "dtype": "float32",
2518
+ "shape": [1, 17, 20],
2519
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2520
+ },
2521
+ "pastStateT": {
2522
+ "dtype": "float32",
2523
+ "shape": [1, 4, 6, 5],
2524
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2525
+ }
2526
+ },
2527
+ "outputs": {
2528
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
2529
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
2530
+ }
2531
+ },
2532
+ {
2533
+ "name": "linear_geometry_float32_float32_g2_zero",
2534
+ "provenance": {
2535
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2536
+ },
2537
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
2538
+ "inputs": {
2539
+ "queryT": {
2540
+ "dtype": "float32",
2541
+ "shape": [2, 0, 48],
2542
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2543
+ },
2544
+ "keyT": {
2545
+ "dtype": "float32",
2546
+ "shape": [2, 0, 16],
2547
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2548
+ },
2549
+ "valueT": {
2550
+ "dtype": "float32",
2551
+ "shape": [2, 0, 27],
2552
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2553
+ }
2554
+ },
2555
+ "outputs": {
2556
+ "outputT": { "dtype": "float32", "shape": [2, 0, 27] },
2557
+ "presentStateT": { "dtype": "float32", "shape": [2, 2, 3, 16, 9] }
2558
+ }
2559
+ },
2560
+ {
2561
+ "name": "linear_geometry_float32_float32_g2_state",
2562
+ "provenance": {
2563
+ "notes": "A one-token update inside a two-slot state window verifies zero-fill of the leading slot and a nonuniform updated state in the final slot, with shared keys, odd value dimensions, and independent activation/state dtypes."
2564
+ },
2565
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
2566
+ "inputs": {
2567
+ "queryT": {
2568
+ "dtype": "float32",
2569
+ "shape": [2, 1, 48],
2570
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2571
+ },
2572
+ "keyT": {
2573
+ "dtype": "float32",
2574
+ "shape": [2, 1, 16],
2575
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2576
+ },
2577
+ "valueT": {
2578
+ "dtype": "float32",
2579
+ "shape": [2, 1, 27],
2580
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2581
+ },
2582
+ "pastStateT": {
2583
+ "dtype": "float32",
2584
+ "shape": [2, 2, 3, 16, 9],
2585
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2586
+ }
2587
+ },
2588
+ "outputs": {
2589
+ "outputT": { "dtype": "float32", "shape": [2, 1, 27] },
2590
+ "presentStateT": { "dtype": "float32", "shape": [2, 2, 3, 16, 9] }
2591
+ }
2592
+ },
2593
+ {
2594
+ "name": "linear_geometry_float16_float16_g0_zero",
2595
+ "provenance": {
2596
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2597
+ },
2598
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2599
+ "inputs": {
2600
+ "queryT": {
2601
+ "dtype": "float16",
2602
+ "shape": [2, 33, 32],
2603
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2604
+ },
2605
+ "keyT": {
2606
+ "dtype": "float16",
2607
+ "shape": [2, 33, 8],
2608
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2609
+ },
2610
+ "valueT": {
2611
+ "dtype": "float16",
2612
+ "shape": [2, 33, 14],
2613
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2614
+ }
2615
+ },
2616
+ "outputs": {
2617
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28], "tolerance": 6e-8, "relTolerance": 0.0005 },
2618
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7], "tolerance": 6e-8, "relTolerance": 0.0005 }
2619
+ }
2620
+ },
2621
+ {
2622
+ "name": "linear_geometry_float16_float16_g0_state",
2623
+ "provenance": {
2624
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2625
+ },
2626
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2627
+ "inputs": {
2628
+ "queryT": {
2629
+ "dtype": "float16",
2630
+ "shape": [2, 33, 32],
2631
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2632
+ },
2633
+ "keyT": {
2634
+ "dtype": "float16",
2635
+ "shape": [2, 33, 8],
2636
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2637
+ },
2638
+ "valueT": {
2639
+ "dtype": "float16",
2640
+ "shape": [2, 33, 14],
2641
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2642
+ },
2643
+ "pastStateT": {
2644
+ "dtype": "float16",
2645
+ "shape": [3, 2, 2, 8, 7],
2646
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2647
+ }
2648
+ },
2649
+ "outputs": {
2650
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28], "tolerance": 6e-8, "relTolerance": 0.0005 },
2651
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7], "tolerance": 6e-8, "relTolerance": 0.0005 }
2652
+ }
2653
+ },
2654
+ {
2655
+ "name": "linear_geometry_float16_float16_g1_zero",
2656
+ "provenance": {
2657
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2658
+ },
2659
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
2660
+ "inputs": {
2661
+ "queryT": {
2662
+ "dtype": "float16",
2663
+ "shape": [1, 17, 12],
2664
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2665
+ },
2666
+ "keyT": {
2667
+ "dtype": "float16",
2668
+ "shape": [1, 17, 12],
2669
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2670
+ },
2671
+ "valueT": {
2672
+ "dtype": "float16",
2673
+ "shape": [1, 17, 20],
2674
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2675
+ }
2676
+ },
2677
+ "outputs": {
2678
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20], "tolerance": 6e-8, "relTolerance": 0.0005 },
2679
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5], "tolerance": 6e-8, "relTolerance": 0.0005 }
2680
+ }
2681
+ },
2682
+ {
2683
+ "name": "linear_geometry_float16_float16_g1_state",
2684
+ "provenance": {
2685
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2686
+ },
2687
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
2688
+ "inputs": {
2689
+ "queryT": {
2690
+ "dtype": "float16",
2691
+ "shape": [1, 17, 12],
2692
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2693
+ },
2694
+ "keyT": {
2695
+ "dtype": "float16",
2696
+ "shape": [1, 17, 12],
2697
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2698
+ },
2699
+ "valueT": {
2700
+ "dtype": "float16",
2701
+ "shape": [1, 17, 20],
2702
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2703
+ },
2704
+ "pastStateT": {
2705
+ "dtype": "float16",
2706
+ "shape": [1, 4, 6, 5],
2707
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2708
+ }
2709
+ },
2710
+ "outputs": {
2711
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20], "tolerance": 6e-8, "relTolerance": 0.0005 },
2712
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5], "tolerance": 6e-8, "relTolerance": 0.0005 }
2713
+ }
2714
+ },
2715
+ {
2716
+ "name": "linear_geometry_float16_float16_g2_zero",
2717
+ "provenance": {
2718
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2719
+ },
2720
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
2721
+ "inputs": {
2722
+ "queryT": {
2723
+ "dtype": "float16",
2724
+ "shape": [2, 0, 48],
2725
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2726
+ },
2727
+ "keyT": {
2728
+ "dtype": "float16",
2729
+ "shape": [2, 0, 16],
2730
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2731
+ },
2732
+ "valueT": {
2733
+ "dtype": "float16",
2734
+ "shape": [2, 0, 27],
2735
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2736
+ }
2737
+ },
2738
+ "outputs": {
2739
+ "outputT": { "dtype": "float16", "shape": [2, 0, 27], "tolerance": 6e-8, "relTolerance": 0.0005 },
2740
+ "presentStateT": { "dtype": "float16", "shape": [2, 2, 3, 16, 9], "tolerance": 6e-8, "relTolerance": 0.0005 }
2741
+ }
2742
+ },
2743
+ {
2744
+ "name": "linear_geometry_float16_float16_g2_state",
2745
+ "provenance": {
2746
+ "notes": "A one-token update inside a two-slot state window verifies zero-fill of the leading slot and a nonuniform updated state in the final slot, with shared keys, odd value dimensions, and independent activation/state dtypes."
2747
+ },
2748
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
2749
+ "inputs": {
2750
+ "queryT": {
2751
+ "dtype": "float16",
2752
+ "shape": [2, 1, 48],
2753
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2754
+ },
2755
+ "keyT": {
2756
+ "dtype": "float16",
2757
+ "shape": [2, 1, 16],
2758
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2759
+ },
2760
+ "valueT": {
2761
+ "dtype": "float16",
2762
+ "shape": [2, 1, 27],
2763
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2764
+ },
2765
+ "pastStateT": {
2766
+ "dtype": "float16",
2767
+ "shape": [2, 2, 3, 16, 9],
2768
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2769
+ }
2770
+ },
2771
+ "outputs": {
2772
+ "outputT": { "dtype": "float16", "shape": [2, 1, 27], "tolerance": 6e-8, "relTolerance": 0.0005 },
2773
+ "presentStateT": { "dtype": "float16", "shape": [2, 2, 3, 16, 9], "tolerance": 6e-8, "relTolerance": 0.0005 }
2774
+ }
2775
+ },
2776
+ {
2777
+ "name": "linear_geometry_float16_float32_g0_zero",
2778
+ "provenance": {
2779
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2780
+ },
2781
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2782
+ "inputs": {
2783
+ "queryT": {
2784
+ "dtype": "float16",
2785
+ "shape": [2, 33, 32],
2786
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2787
+ },
2788
+ "keyT": {
2789
+ "dtype": "float16",
2790
+ "shape": [2, 33, 8],
2791
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2792
+ },
2793
+ "valueT": {
2794
+ "dtype": "float16",
2795
+ "shape": [2, 33, 14],
2796
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2797
+ }
2798
+ },
2799
+ "outputs": {
2800
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28], "tolerance": 6e-8, "relTolerance": 0.0005 },
2801
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
2802
+ }
2803
+ },
2804
+ {
2805
+ "name": "linear_geometry_float16_float32_g0_state",
2806
+ "provenance": {
2807
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2808
+ },
2809
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2810
+ "inputs": {
2811
+ "queryT": {
2812
+ "dtype": "float16",
2813
+ "shape": [2, 33, 32],
2814
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2815
+ },
2816
+ "keyT": {
2817
+ "dtype": "float16",
2818
+ "shape": [2, 33, 8],
2819
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2820
+ },
2821
+ "valueT": {
2822
+ "dtype": "float16",
2823
+ "shape": [2, 33, 14],
2824
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2825
+ },
2826
+ "pastStateT": {
2827
+ "dtype": "float32",
2828
+ "shape": [3, 2, 2, 8, 7],
2829
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2830
+ }
2831
+ },
2832
+ "outputs": {
2833
+ "outputT": { "dtype": "float16", "shape": [2, 33, 28], "tolerance": 6e-8, "relTolerance": 0.0005 },
2834
+ "presentStateT": { "dtype": "float32", "shape": [3, 2, 2, 8, 7] }
2835
+ }
2836
+ },
2837
+ {
2838
+ "name": "linear_geometry_float16_float32_g1_zero",
2839
+ "provenance": {
2840
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2841
+ },
2842
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
2843
+ "inputs": {
2844
+ "queryT": {
2845
+ "dtype": "float16",
2846
+ "shape": [1, 17, 12],
2847
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2848
+ },
2849
+ "keyT": {
2850
+ "dtype": "float16",
2851
+ "shape": [1, 17, 12],
2852
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2853
+ },
2854
+ "valueT": {
2855
+ "dtype": "float16",
2856
+ "shape": [1, 17, 20],
2857
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2858
+ }
2859
+ },
2860
+ "outputs": {
2861
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20], "tolerance": 6e-8, "relTolerance": 0.0005 },
2862
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
2863
+ }
2864
+ },
2865
+ {
2866
+ "name": "linear_geometry_float16_float32_g1_state",
2867
+ "provenance": {
2868
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2869
+ },
2870
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
2871
+ "inputs": {
2872
+ "queryT": {
2873
+ "dtype": "float16",
2874
+ "shape": [1, 17, 12],
2875
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2876
+ },
2877
+ "keyT": {
2878
+ "dtype": "float16",
2879
+ "shape": [1, 17, 12],
2880
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2881
+ },
2882
+ "valueT": {
2883
+ "dtype": "float16",
2884
+ "shape": [1, 17, 20],
2885
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2886
+ },
2887
+ "pastStateT": {
2888
+ "dtype": "float32",
2889
+ "shape": [1, 4, 6, 5],
2890
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2891
+ }
2892
+ },
2893
+ "outputs": {
2894
+ "outputT": { "dtype": "float16", "shape": [1, 17, 20], "tolerance": 6e-8, "relTolerance": 0.0005 },
2895
+ "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 5] }
2896
+ }
2897
+ },
2898
+ {
2899
+ "name": "linear_geometry_float16_float32_g2_zero",
2900
+ "provenance": {
2901
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2902
+ },
2903
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
2904
+ "inputs": {
2905
+ "queryT": {
2906
+ "dtype": "float16",
2907
+ "shape": [2, 0, 48],
2908
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2909
+ },
2910
+ "keyT": {
2911
+ "dtype": "float16",
2912
+ "shape": [2, 0, 16],
2913
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2914
+ },
2915
+ "valueT": {
2916
+ "dtype": "float16",
2917
+ "shape": [2, 0, 27],
2918
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2919
+ }
2920
+ },
2921
+ "outputs": {
2922
+ "outputT": { "dtype": "float16", "shape": [2, 0, 27], "tolerance": 6e-8, "relTolerance": 0.0005 },
2923
+ "presentStateT": { "dtype": "float32", "shape": [2, 2, 3, 16, 9] }
2924
+ }
2925
+ },
2926
+ {
2927
+ "name": "linear_geometry_float16_float32_g2_state",
2928
+ "provenance": {
2929
+ "notes": "A one-token update inside a two-slot state window verifies zero-fill of the leading slot and a nonuniform updated state in the final slot, with shared keys, odd value dimensions, and independent activation/state dtypes."
2930
+ },
2931
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
2932
+ "inputs": {
2933
+ "queryT": {
2934
+ "dtype": "float16",
2935
+ "shape": [2, 1, 48],
2936
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2937
+ },
2938
+ "keyT": {
2939
+ "dtype": "float16",
2940
+ "shape": [2, 1, 16],
2941
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2942
+ },
2943
+ "valueT": {
2944
+ "dtype": "float16",
2945
+ "shape": [2, 1, 27],
2946
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2947
+ },
2948
+ "pastStateT": {
2949
+ "dtype": "float32",
2950
+ "shape": [2, 2, 3, 16, 9],
2951
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
2952
+ }
2953
+ },
2954
+ "outputs": {
2955
+ "outputT": { "dtype": "float16", "shape": [2, 1, 27], "tolerance": 6e-8, "relTolerance": 0.0005 },
2956
+ "presentStateT": { "dtype": "float32", "shape": [2, 2, 3, 16, 9] }
2957
+ }
2958
+ },
2959
+ {
2960
+ "name": "linear_geometry_float32_float16_g0_zero",
2961
+ "provenance": {
2962
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2963
+ },
2964
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2965
+ "inputs": {
2966
+ "queryT": {
2967
+ "dtype": "float32",
2968
+ "shape": [2, 33, 32],
2969
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2970
+ },
2971
+ "keyT": {
2972
+ "dtype": "float32",
2973
+ "shape": [2, 33, 8],
2974
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
2975
+ },
2976
+ "valueT": {
2977
+ "dtype": "float32",
2978
+ "shape": [2, 33, 14],
2979
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
2980
+ }
2981
+ },
2982
+ "outputs": {
2983
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
2984
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7], "tolerance": 6e-8, "relTolerance": 0.0005 }
2985
+ }
2986
+ },
2987
+ {
2988
+ "name": "linear_geometry_float32_float16_g0_state",
2989
+ "provenance": {
2990
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
2991
+ },
2992
+ "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "state_window": 3, "scale": 0.375 },
2993
+ "inputs": {
2994
+ "queryT": {
2995
+ "dtype": "float32",
2996
+ "shape": [2, 33, 32],
2997
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
2998
+ },
2999
+ "keyT": {
3000
+ "dtype": "float32",
3001
+ "shape": [2, 33, 8],
3002
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
3003
+ },
3004
+ "valueT": {
3005
+ "dtype": "float32",
3006
+ "shape": [2, 33, 14],
3007
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
3008
+ },
3009
+ "pastStateT": {
3010
+ "dtype": "float16",
3011
+ "shape": [3, 2, 2, 8, 7],
3012
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
3013
+ }
3014
+ },
3015
+ "outputs": {
3016
+ "outputT": { "dtype": "float32", "shape": [2, 33, 28] },
3017
+ "presentStateT": { "dtype": "float16", "shape": [3, 2, 2, 8, 7], "tolerance": 6e-8, "relTolerance": 0.0005 }
3018
+ }
3019
+ },
3020
+ {
3021
+ "name": "linear_geometry_float32_float16_g1_zero",
3022
+ "provenance": {
3023
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
3024
+ },
3025
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
3026
+ "inputs": {
3027
+ "queryT": {
3028
+ "dtype": "float32",
3029
+ "shape": [1, 17, 12],
3030
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
3031
+ },
3032
+ "keyT": {
3033
+ "dtype": "float32",
3034
+ "shape": [1, 17, 12],
3035
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
3036
+ },
3037
+ "valueT": {
3038
+ "dtype": "float32",
3039
+ "shape": [1, 17, 20],
3040
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
3041
+ }
3042
+ },
3043
+ "outputs": {
3044
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
3045
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5], "tolerance": 6e-8, "relTolerance": 0.0005 }
3046
+ }
3047
+ },
3048
+ {
3049
+ "name": "linear_geometry_float32_float16_g1_state",
3050
+ "provenance": {
3051
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
3052
+ },
3053
+ "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "state_window": 0, "scale": 0 },
3054
+ "inputs": {
3055
+ "queryT": {
3056
+ "dtype": "float32",
3057
+ "shape": [1, 17, 12],
3058
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
3059
+ },
3060
+ "keyT": {
3061
+ "dtype": "float32",
3062
+ "shape": [1, 17, 12],
3063
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
3064
+ },
3065
+ "valueT": {
3066
+ "dtype": "float32",
3067
+ "shape": [1, 17, 20],
3068
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
3069
+ },
3070
+ "pastStateT": {
3071
+ "dtype": "float16",
3072
+ "shape": [1, 4, 6, 5],
3073
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
3074
+ }
3075
+ },
3076
+ "outputs": {
3077
+ "outputT": { "dtype": "float32", "shape": [1, 17, 20] },
3078
+ "presentStateT": { "dtype": "float16", "shape": [1, 4, 6, 5], "tolerance": 6e-8, "relTolerance": 0.0005 }
3079
+ }
3080
+ },
3081
+ {
3082
+ "name": "linear_geometry_float32_float16_g2_zero",
3083
+ "provenance": {
3084
+ "notes": "Small-head linear recurrence with shared keys, standard or inverse query grouping, odd value dimensions, independent state dtype, and retained or empty-sequence state windows."
3085
+ },
3086
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
3087
+ "inputs": {
3088
+ "queryT": {
3089
+ "dtype": "float32",
3090
+ "shape": [2, 0, 48],
3091
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
3092
+ },
3093
+ "keyT": {
3094
+ "dtype": "float32",
3095
+ "shape": [2, 0, 16],
3096
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
3097
+ },
3098
+ "valueT": {
3099
+ "dtype": "float32",
3100
+ "shape": [2, 0, 27],
3101
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
3102
+ }
3103
+ },
3104
+ "outputs": {
3105
+ "outputT": { "dtype": "float32", "shape": [2, 0, 27] },
3106
+ "presentStateT": { "dtype": "float16", "shape": [2, 2, 3, 16, 9], "tolerance": 6e-8, "relTolerance": 0.0005 }
3107
+ }
3108
+ },
3109
+ {
3110
+ "name": "linear_geometry_float32_float16_g2_state",
3111
+ "provenance": {
3112
+ "notes": "A one-token update inside a two-slot state window verifies zero-fill of the leading slot and a nonuniform updated state in the final slot, with shared keys, odd value dimensions, and independent activation/state dtypes."
3113
+ },
3114
+ "attrs": { "q_num_heads": 3, "kv_num_heads": 3, "update_rule": "linear", "state_window": 2, "scale": 0.375 },
3115
+ "inputs": {
3116
+ "queryT": {
3117
+ "dtype": "float32",
3118
+ "shape": [2, 1, 48],
3119
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.05 }
3120
+ },
3121
+ "keyT": {
3122
+ "dtype": "float32",
3123
+ "shape": [2, 1, 16],
3124
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.31, "scale": 0.05 }
3125
+ },
3126
+ "valueT": {
3127
+ "dtype": "float32",
3128
+ "shape": [2, 1, 27],
3129
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.31, "scale": 0.05 }
3130
+ },
3131
+ "pastStateT": {
3132
+ "dtype": "float16",
3133
+ "shape": [2, 2, 3, 16, 9],
3134
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31, "scale": 0.05 }
3135
+ }
3136
+ },
3137
+ "outputs": {
3138
+ "outputT": { "dtype": "float32", "shape": [2, 1, 27] },
3139
+ "presentStateT": { "dtype": "float16", "shape": [2, 2, 3, 16, 9], "tolerance": 6e-8, "relTolerance": 0.0005 }
3140
+ }
3141
  }
3142
  ]
3143
  }