Xenova HF Staff commited on
Commit
83fa505
·
verified ·
1 Parent(s): 66e0e23

sync 6fdf6301e2bb

Browse files
README.md CHANGED
@@ -44,22 +44,29 @@ Default values (overridable per request):
44
  | --- | --- |
45
  | `T` | `float32` |
46
 
 
 
 
 
 
 
47
  ## Files
48
 
49
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
50
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
51
  - [`test.json`](build/webgpu/test.json) — correctness cases
52
- - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
53
  - [`scan-coop-channel.wgsl.jinja`](build/webgpu/scan-coop-channel.wgsl.jinja)
54
  - [`scan-multichunk-apply.wgsl.jinja`](build/webgpu/scan-multichunk-apply.wgsl.jinja)
55
  - [`scan-multichunk-carries.wgsl.jinja`](build/webgpu/scan-multichunk-carries.wgsl.jinja)
56
  - [`scan-multichunk-local.wgsl.jinja`](build/webgpu/scan-multichunk-local.wgsl.jinja)
57
  - [`scan-prefix-sum.wgsl.jinja`](build/webgpu/scan-prefix-sum.wgsl.jinja)
 
58
 
59
  ## Use with `@huggingface/kernels`
60
 
61
  ```sh
62
- npm install --save-exact @huggingface/kernels@0.0.1-preview.2
63
  ```
64
 
65
  Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
 
44
  | --- | --- |
45
  | `T` | `float32` |
46
 
47
+ ## Implementation variants
48
+
49
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
50
+
51
+ - `vector_channel_prefix_sum` — Scan four adjacent state channels together with float32 accumulation; bound the power-of-two workgroup by the scan length, tuning and device resources, and use subgroups only when it contains complete groups.
52
+
53
  ## Files
54
 
55
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
56
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
57
  - [`test.json`](build/webgpu/test.json) — correctness cases
58
+ - [`bench.json`](build/webgpu/bench.json) — benchmark cases
59
  - [`scan-coop-channel.wgsl.jinja`](build/webgpu/scan-coop-channel.wgsl.jinja)
60
  - [`scan-multichunk-apply.wgsl.jinja`](build/webgpu/scan-multichunk-apply.wgsl.jinja)
61
  - [`scan-multichunk-carries.wgsl.jinja`](build/webgpu/scan-multichunk-carries.wgsl.jinja)
62
  - [`scan-multichunk-local.wgsl.jinja`](build/webgpu/scan-multichunk-local.wgsl.jinja)
63
  - [`scan-prefix-sum.wgsl.jinja`](build/webgpu/scan-prefix-sum.wgsl.jinja)
64
+ - [`scan-strided-row.wgsl.jinja`](build/webgpu/scan-strided-row.wgsl.jinja)
65
 
66
  ## Use with `@huggingface/kernels`
67
 
68
  ```sh
69
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.3
70
  ```
71
 
72
  Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
build/webgpu/bench.json CHANGED
@@ -50,7 +50,7 @@
50
  "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 4 * numel(shapes.scan_input)" }] }
51
  },
52
  {
53
- "name": "coop_healthy_dim512_steps4096",
54
  "preset": "smoke",
55
  "inputs": {
56
  "initial_state": { "dtype": "float32", "shape": [512], "dist": "normal", "seed": 753, "scale": 0.1 },
@@ -140,6 +140,102 @@
140
  "scan_output": { "dtype": "float32", "shape": [131072, 8] }
141
  },
142
  "bench": { "primary": true, "metrics": [{ "type": "bandwidth", "value": "2 * 4 * numel(shapes.scan_input)" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
144
  ]
145
  }
 
50
  "bench": { "metrics": [{ "type": "bandwidth", "value": "2 * 4 * numel(shapes.scan_input)" }] }
51
  },
52
  {
53
+ "name": "coop_control_dim512_steps4096",
54
  "preset": "smoke",
55
  "inputs": {
56
  "initial_state": { "dtype": "float32", "shape": [512], "dist": "normal", "seed": 753, "scale": 0.1 },
 
140
  "scan_output": { "dtype": "float32", "shape": [131072, 8] }
141
  },
142
  "bench": { "primary": true, "metrics": [{ "type": "bandwidth", "value": "2 * 4 * numel(shapes.scan_input)" }] }
143
+ },
144
+ {
145
+ "name": "vector_channel_boundary_t63_d12_reverse0",
146
+ "attrs": { "reverse": 0 },
147
+ "inputs": {
148
+ "initial_state": { "dtype": "float32", "shape": [12], "dist": "normal", "seed": 1301, "scale": 0.1 },
149
+ "scan_input": { "dtype": "float32", "shape": [63, 12], "dist": "normal", "seed": 1302, "scale": 0.1 }
150
+ },
151
+ "outputs": {
152
+ "final_state": { "dtype": "float32", "shape": [12] },
153
+ "scan_output": { "dtype": "float32", "shape": [63, 12] }
154
+ },
155
+ "preset": "edge",
156
+ "bench": {
157
+ "metrics": [{ "type": "bandwidth", "value": "8 * numel(shapes.scan_input) + 8 * dim(shapes.initial_state, 0)" }]
158
+ }
159
+ },
160
+ {
161
+ "name": "vector_channel_boundary_t64_d12_reverse0",
162
+ "attrs": { "reverse": 0 },
163
+ "inputs": {
164
+ "initial_state": { "dtype": "float32", "shape": [12], "dist": "normal", "seed": 1301, "scale": 0.1 },
165
+ "scan_input": { "dtype": "float32", "shape": [64, 12], "dist": "normal", "seed": 1302, "scale": 0.1 }
166
+ },
167
+ "outputs": {
168
+ "final_state": { "dtype": "float32", "shape": [12] },
169
+ "scan_output": { "dtype": "float32", "shape": [64, 12] }
170
+ },
171
+ "preset": "edge",
172
+ "bench": {
173
+ "metrics": [{ "type": "bandwidth", "value": "8 * numel(shapes.scan_input) + 8 * dim(shapes.initial_state, 0)" }]
174
+ }
175
+ },
176
+ {
177
+ "name": "vector_channel_boundary_t65_d12_reverse1",
178
+ "attrs": { "reverse": 1 },
179
+ "inputs": {
180
+ "initial_state": { "dtype": "float32", "shape": [12], "dist": "normal", "seed": 1301, "scale": 0.1 },
181
+ "scan_input": { "dtype": "float32", "shape": [65, 12], "dist": "normal", "seed": 1302, "scale": 0.1 }
182
+ },
183
+ "outputs": {
184
+ "final_state": { "dtype": "float32", "shape": [12] },
185
+ "scan_output": { "dtype": "float32", "shape": [65, 12] }
186
+ },
187
+ "preset": "edge",
188
+ "bench": {
189
+ "metrics": [{ "type": "bandwidth", "value": "8 * numel(shapes.scan_input) + 8 * dim(shapes.initial_state, 0)" }]
190
+ }
191
+ },
192
+ {
193
+ "name": "vector_channel_boundary_t255_d4_reverse0",
194
+ "attrs": { "reverse": 0 },
195
+ "inputs": {
196
+ "initial_state": { "dtype": "float32", "shape": [4], "dist": "normal", "seed": 1301, "scale": 0.1 },
197
+ "scan_input": { "dtype": "float32", "shape": [255, 4], "dist": "normal", "seed": 1302, "scale": 0.1 }
198
+ },
199
+ "outputs": {
200
+ "final_state": { "dtype": "float32", "shape": [4] },
201
+ "scan_output": { "dtype": "float32", "shape": [255, 4] }
202
+ },
203
+ "preset": "edge",
204
+ "bench": {
205
+ "metrics": [{ "type": "bandwidth", "value": "8 * numel(shapes.scan_input) + 8 * dim(shapes.initial_state, 0)" }]
206
+ }
207
+ },
208
+ {
209
+ "name": "vector_channel_boundary_t257_d68_reverse1",
210
+ "attrs": { "reverse": 1 },
211
+ "inputs": {
212
+ "initial_state": { "dtype": "float32", "shape": [68], "dist": "normal", "seed": 1301, "scale": 0.1 },
213
+ "scan_input": { "dtype": "float32", "shape": [257, 68], "dist": "normal", "seed": 1302, "scale": 0.1 }
214
+ },
215
+ "outputs": {
216
+ "final_state": { "dtype": "float32", "shape": [68] },
217
+ "scan_output": { "dtype": "float32", "shape": [257, 68] }
218
+ },
219
+ "preset": "edge",
220
+ "bench": {
221
+ "metrics": [{ "type": "bandwidth", "value": "8 * numel(shapes.scan_input) + 8 * dim(shapes.initial_state, 0)" }]
222
+ }
223
+ },
224
+ {
225
+ "name": "vector_channel_boundary_t257_d69_reverse1",
226
+ "attrs": { "reverse": 1 },
227
+ "inputs": {
228
+ "initial_state": { "dtype": "float32", "shape": [69], "dist": "normal", "seed": 1301, "scale": 0.1 },
229
+ "scan_input": { "dtype": "float32", "shape": [257, 69], "dist": "normal", "seed": 1302, "scale": 0.1 }
230
+ },
231
+ "outputs": {
232
+ "final_state": { "dtype": "float32", "shape": [69] },
233
+ "scan_output": { "dtype": "float32", "shape": [257, 69] }
234
+ },
235
+ "preset": "edge",
236
+ "bench": {
237
+ "metrics": [{ "type": "bandwidth", "value": "8 * numel(shapes.scan_input) + 8 * dim(shapes.initial_state, 0)" }]
238
+ }
239
  }
240
  ]
241
  }
build/webgpu/manifest.json CHANGED
@@ -17,22 +17,36 @@
17
  "subgroupsWave32": "device.features.has(\"subgroups\") and wave32Adapter",
18
  "variableNarrowSubgroups": "device.features.has(\"subgroups\") and has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize < device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= 16",
19
  "parallelSubgroupScan": "subgroupsWave32 or variableNarrowSubgroups",
20
- "scanUseSubgroups": "parallelSubgroupScan"
 
 
 
21
  },
22
  "when": ["ranks.initial_state == 1", "ranks.scan_input == 2", "ranks.final_state == 1", "ranks.scan_output == 2", "dim(shapes.initial_state, 0) == dim(shapes.scan_input, 1)", "dim(shapes.final_state, 0) == dim(shapes.initial_state, 0)", "dim(shapes.scan_output, 0) == dim(shapes.scan_input, 0)", "dim(shapes.scan_output, 1) == dim(shapes.scan_input, 1)"],
23
  "bindings": {
24
- "scan_input": { "buffer": "read-only-storage", "elementType": "f32" },
25
- "scan_output": { "buffer": "storage", "elementType": "f32" },
26
  "params": {
27
- "buffer": "uniform",
28
  "struct": [
29
  { "name": "steps", "type": "u32", "value": "dim(shapes.scan_input, 0)" },
30
  { "name": "dim", "type": "u32", "value": "dim(shapes.scan_input, 1)" },
31
  { "name": "reverse", "type": "u32", "value": "attrs.reverse" }
32
  ]
33
  },
34
- "initial_state": { "buffer": "read-only-storage", "elementType": "f32" },
35
- "final_state": { "buffer": "storage", "elementType": "f32" }
 
 
 
 
 
 
 
 
 
 
 
 
36
  },
37
  "variants": [
38
  {
@@ -53,14 +67,8 @@
53
  "name": "Scan.MultichunkLocal",
54
  "shader": "scan-multichunk-local.wgsl.jinja",
55
  "derive": { "useSubgroups": "scanUseSubgroups" },
56
- "bindings": [
57
- "scan_input",
58
- "scan_output",
59
- { "name": "chunkTotals", "buffer": "storage", "elementType": "f32" },
60
- "params"
61
- ],
62
- "dispatch": { "x": "min(totalChunks, 65535)", "y": "ceilDiv(totalChunks, 65535)", "z": 1 },
63
- "subgroupCollectivesWidth": "portable"
64
  },
65
  {
66
  "id": "carries",
@@ -69,7 +77,7 @@
69
  "bindings": [
70
  "initial_state",
71
  { "name": "chunkTotals", "buffer": "read-only-storage", "elementType": "f32" },
72
- { "name": "chunkCarries", "buffer": "storage", "elementType": "f32" },
73
  "final_state",
74
  { "name": "params", "struct": [{ "name": "dim", "type": "u32", "value": "dim(shapes.scan_input, 1)" }] }
75
  ],
@@ -119,8 +127,7 @@
119
  "x": "min(dim(shapes.scan_input, 1), 65535)",
120
  "y": "ceilDiv(dim(shapes.scan_input, 1), 65535)",
121
  "z": 1
122
- },
123
- "subgroupCollectivesWidth": "portable"
124
  }
125
  ]
126
  },
@@ -139,6 +146,28 @@
139
  }
140
  }
141
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
  ]
144
  }
 
17
  "subgroupsWave32": "device.features.has(\"subgroups\") and wave32Adapter",
18
  "variableNarrowSubgroups": "device.features.has(\"subgroups\") and has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize < device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= 16",
19
  "parallelSubgroupScan": "subgroupsWave32 or variableNarrowSubgroups",
20
+ "scanUseSubgroups": "parallelSubgroupScan",
21
+ "vectorScanChannels": "dim(shapes.scan_input, 1) / 4",
22
+ "vectorScanWg": "pow2ceil(min(tunables.WORKGROUP_SIZE, pow2ceil(dim(shapes.scan_input, 0)), device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX, floor((device.limits.maxComputeWorkgroupStorageSize - 4) / 16)) + 1) / 2",
23
+ "vectorScanUseSubgroups": "scanUseSubgroups and vectorScanWg >= device.adapterInfo.subgroupMaxSize"
24
  },
25
  "when": ["ranks.initial_state == 1", "ranks.scan_input == 2", "ranks.final_state == 1", "ranks.scan_output == 2", "dim(shapes.initial_state, 0) == dim(shapes.scan_input, 1)", "dim(shapes.final_state, 0) == dim(shapes.initial_state, 0)", "dim(shapes.scan_output, 0) == dim(shapes.scan_input, 0)", "dim(shapes.scan_output, 1) == dim(shapes.scan_input, 1)"],
26
  "bindings": {
27
+ "scan_input": { "elementType": "f32" },
28
+ "scan_output": { "elementType": "f32" },
29
  "params": {
 
30
  "struct": [
31
  { "name": "steps", "type": "u32", "value": "dim(shapes.scan_input, 0)" },
32
  { "name": "dim", "type": "u32", "value": "dim(shapes.scan_input, 1)" },
33
  { "name": "reverse", "type": "u32", "value": "attrs.reverse" }
34
  ]
35
  },
36
+ "initial_state": { "elementType": "f32" },
37
+ "final_state": { "elementType": "f32" },
38
+ "vectorInitial": { "arg": "initial_state", "name": "initial_state", "elementType": "vec4<f32>" },
39
+ "vectorInput": { "arg": "scan_input", "name": "x", "elementType": "vec4<f32>" },
40
+ "vectorOutput": { "arg": "scan_output", "name": "y", "elementType": "vec4<f32>" },
41
+ "vectorFinal": { "arg": "final_state", "name": "final_state", "elementType": "vec4<f32>" },
42
+ "vectorParams": {
43
+ "name": "params",
44
+ "struct": [
45
+ { "name": "rows", "type": "u32", "value": "vectorScanChannels" },
46
+ { "name": "dim", "type": "u32", "value": "dim(shapes.scan_input, 0)" },
47
+ { "name": "inner", "type": "u32", "value": "vectorScanChannels" }
48
+ ]
49
+ }
50
  },
51
  "variants": [
52
  {
 
67
  "name": "Scan.MultichunkLocal",
68
  "shader": "scan-multichunk-local.wgsl.jinja",
69
  "derive": { "useSubgroups": "scanUseSubgroups" },
70
+ "bindings": ["scan_input", "scan_output", { "name": "chunkTotals", "elementType": "f32" }, "params"],
71
+ "dispatch": { "x": "min(totalChunks, 65535)", "y": "ceilDiv(totalChunks, 65535)", "z": 1 }
 
 
 
 
 
 
72
  },
73
  {
74
  "id": "carries",
 
77
  "bindings": [
78
  "initial_state",
79
  { "name": "chunkTotals", "buffer": "read-only-storage", "elementType": "f32" },
80
+ { "name": "chunkCarries", "elementType": "f32" },
81
  "final_state",
82
  { "name": "params", "struct": [{ "name": "dim", "type": "u32", "value": "dim(shapes.scan_input, 1)" }] }
83
  ],
 
127
  "x": "min(dim(shapes.scan_input, 1), 65535)",
128
  "y": "ceilDiv(dim(shapes.scan_input, 1), 65535)",
129
  "z": 1
130
+ }
 
131
  }
132
  ]
133
  },
 
146
  }
147
  }
148
  ]
149
+ },
150
+ {
151
+ "id": "vector_channel_prefix_sum",
152
+ "priority": 20,
153
+ "when": ["dim(shapes.scan_input, 1) >= 4", "dim(shapes.scan_input, 1) % 4 == 0", "dim(shapes.scan_input, 0) >= 64", "vectorScanWg >= 1", "ceilDiv(vectorScanChannels, DISPATCH_FOLD_WIDTH) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535)"],
154
+ "derive": {
155
+ "scalar": "\"vec4<f32>\"",
156
+ "accType": "\"vec4<f32>\"",
157
+ "workgroupSize": "vectorScanWg",
158
+ "reverse": "attrs.reverse != 0",
159
+ "useSubgroups": "vectorScanUseSubgroups"
160
+ },
161
+ "passes": [
162
+ {
163
+ "id": "main",
164
+ "name": "Scan.VectorChannelPrefix",
165
+ "shader": "scan-strided-row.wgsl.jinja",
166
+ "bindings": ["vectorInitial", "vectorInput", "vectorOutput", "vectorFinal", "vectorParams"],
167
+ "dispatch": { "x": "min(vectorScanChannels, 65535)", "y": "ceilDiv(vectorScanChannels, 65535)", "z": 1 }
168
+ }
169
+ ],
170
+ "demoteWhen": ["dim(shapes.scan_input, 1) < vectorScanWg"]
171
  }
172
  ]
173
  }
build/webgpu/metadata.json CHANGED
@@ -1,29 +1,31 @@
1
  {
2
  "name": "ai.onnx.Scan",
3
- "id": "_ai_onnx_scan_webgpu_0844f69",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "tElCZSCAVE3/XQ0WO3gJ4ysjWou60Gx57WXs/iIiV70=",
11
- "manifest.json": "3nSOTULQv+1MDyOHGRakr7ahH/iELdpqI4TMW0Y/4Ac=",
12
  "scan-coop-channel.wgsl.jinja": "IoZdnMesPo0LyCYHIQ1NahvE0+BUC9hGFOB8kbKAAE0=",
13
- "scan-multichunk-apply.wgsl.jinja": "0xcAy1mf28ZOc4VdaIx9ZBFRLyWwUk+ukwd9hkGoahw=",
14
- "scan-multichunk-carries.wgsl.jinja": "Cd9J4zzmy935VHhL6BOPGIVrK0daOx2gQgfkMDlLZQg=",
15
  "scan-multichunk-local.wgsl.jinja": "E7BNf1L/PxxwcNFCdEfoWf1Zd7BF9CDD0GnPuQ+VinY=",
16
- "scan-prefix-sum.wgsl.jinja": "Z5kIBUtpZ99FlX3aMLLkCWZ1bc2JsLjcfTnN30kXId0=",
17
- "test.json": "jL0GWqv/vJge/cj98AfdAK4Bh4AATOcIykdfuVZsbnk="
 
18
  }
19
  },
20
- "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
21
  "webgpu": {
22
- "manifestSpec": "2.0",
23
  "variants": {
24
  "multichunk_small_state": ["scan-multichunk-apply.wgsl.jinja", "scan-multichunk-carries.wgsl.jinja", "scan-multichunk-local.wgsl.jinja"],
25
  "coop_channel_prefix_sum": ["scan-coop-channel.wgsl.jinja"],
26
- "lowered_prefix_sum": ["scan-prefix-sum.wgsl.jinja"]
 
27
  }
28
  }
29
  }
 
1
  {
2
  "name": "ai.onnx.Scan",
3
+ "id": "_ai_onnx_scan_webgpu_9e49b5b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "JCxeUmlGL+dliqxE/n7GRey9/Yf0V0QkCzOarWmaurg=",
11
+ "manifest.json": "gmnmQ85vpC5piLIt5SAtrHXLOtCL7uN1py6wJ4LCeyI=",
12
  "scan-coop-channel.wgsl.jinja": "IoZdnMesPo0LyCYHIQ1NahvE0+BUC9hGFOB8kbKAAE0=",
13
+ "scan-multichunk-apply.wgsl.jinja": "yyZ1xgBfo8oVd2kBGbgmEeKtKXwmPRVuYdAGie6AAZs=",
14
+ "scan-multichunk-carries.wgsl.jinja": "1iwwgIXERfLk8H/ZbAx5G7MA7hfPCLDpAZLqzbgHmLU=",
15
  "scan-multichunk-local.wgsl.jinja": "E7BNf1L/PxxwcNFCdEfoWf1Zd7BF9CDD0GnPuQ+VinY=",
16
+ "scan-prefix-sum.wgsl.jinja": "OvW6G/HRG2pyeNrDHFfsvrzN3U7MAUavQnaGXOMU7vw=",
17
+ "scan-strided-row.wgsl.jinja": "ywymN5rjP420WNzHOIgBF4dDH6a2lxC0SEV9DpWtq3k=",
18
+ "test.json": "8vWkrtFOsRfBxwiXhIPyZAxC18aH1kt6lcXBCRPxmH8="
19
  }
20
  },
21
+ "provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
22
  "webgpu": {
23
+ "manifestSpec": "2.1",
24
  "variants": {
25
  "multichunk_small_state": ["scan-multichunk-apply.wgsl.jinja", "scan-multichunk-carries.wgsl.jinja", "scan-multichunk-local.wgsl.jinja"],
26
  "coop_channel_prefix_sum": ["scan-coop-channel.wgsl.jinja"],
27
+ "lowered_prefix_sum": ["scan-prefix-sum.wgsl.jinja"],
28
+ "vector_channel_prefix_sum": ["scan-strided-row.wgsl.jinja"]
29
  }
30
  }
31
  }
build/webgpu/scan-multichunk-apply.wgsl.jinja CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
 
3
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
@@ -5,8 +11,7 @@ const CHUNKS: u32 = {{ chunks }}u;
5
 
6
  @compute @workgroup_size(WG)
7
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
8
- let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
9
- if (i >= params.count) { return; }
10
  let t = i / params.dim;
11
  let channel = i % params.dim;
12
  let iter = select(t, params.steps - 1u - t, params.reverse != 0u);
 
1
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
2
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width.
5
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
6
+ if ({{ name }} >= {{ bound }}) { return; }{% endmacro %}
7
  {{ env.wgsl.resourceDeclarations }}
8
 
9
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
 
11
 
12
  @compute @workgroup_size(WG)
13
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
14
+ {{ flat_index_2d("WG", guardInline=true) }}
 
15
  let t = i / params.dim;
16
  let channel = i % params.dim;
17
  let iter = select(t, params.steps - 1u - t, params.reverse != 0u);
build/webgpu/scan-multichunk-carries.wgsl.jinja CHANGED
@@ -1,11 +1,16 @@
 
 
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
 
3
  const CHUNKS: u32 = {{ chunks }}u;
4
 
5
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
6
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
7
- let channel = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
8
- if (channel >= params.dim) { return; }
9
  var carry = initial_state[channel];
10
  let base = channel * CHUNKS;
11
  for (var chunk = 0u; chunk < CHUNKS; chunk += 1u) {
 
1
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
2
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width.
5
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
6
+ if ({{ name }} >= {{ bound }}) { return; }{% endmacro %}
7
  {{ env.wgsl.resourceDeclarations }}
8
 
9
  const CHUNKS: u32 = {{ chunks }}u;
10
 
11
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
12
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
13
+ {{ flat_index_2d(tunables.WORKGROUP_SIZE, "channel", "params.dim", guardInline=true) }}
 
14
  var carry = initial_state[channel];
15
  let base = channel * CHUNKS;
16
  for (var chunk = 0u; chunk < CHUNKS; chunk += 1u) {
build/webgpu/scan-prefix-sum.wgsl.jinja CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
 
3
  // One invocation owns each channel and runs its prefix sum serially. Channels
@@ -5,9 +11,7 @@
5
  // order remain sequential.
6
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
7
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
8
- // gid.y carries channel-index bits past the per-dimension dispatch limit.
9
- let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
10
- if (i >= params.dim) { return; }
11
  var state = initial_state[i];
12
  for (var iter = 0u; iter < params.steps; iter = iter + 1u) {
13
  let t = select(iter, params.steps - 1u - iter, params.reverse != 0u);
 
1
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
2
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width.
5
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
6
+ if ({{ name }} >= {{ bound }}) { return; }{% endmacro %}
7
  {{ env.wgsl.resourceDeclarations }}
8
 
9
  // One invocation owns each channel and runs its prefix sum serially. Channels
 
11
  // order remain sequential.
12
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
13
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
14
+ {{ flat_index_2d(tunables.WORKGROUP_SIZE, bound="params.dim", guardInline=true) }}
 
 
15
  var state = initial_state[i];
16
  for (var iter = 0u; iter < params.steps; iter = iter + 1u) {
17
  let t = select(iter, params.steps - 1u - iter, params.reverse != 0u);
build/webgpu/scan-strided-row.wgsl.jinja ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Workgroup-per-row chunked prefix scan along a non-innermost axis
2
+ // (inner > 1). One workgroup owns one (outer, inner) row and scans the dim-length
3
+ // axis cooperatively in WORKGROUP_SIZE chunks. Splitting each row across the
4
+ // workgroup exposes parallelism when the tensor has few rows and a long axis.
5
+ //
6
+ // The row's element d is `(outer_i * dim + d) * inner + inner_i`, so successive
7
+ // scan elements are `inner` apart. The vec4 mode loads four adjacent columns
8
+ // per invocation. Distinct workgroups do not share a SIMD memory transaction.
9
+ // Short axes use a scan-length-sized workgroup to avoid idle padded lanes.
10
+ //
11
+ // Floats accumulate in f32 even for f16 storage, and identity padding leaves
12
+ // partial chunks unchanged.
13
+ // Tree reduction reassociates floating-point sums relative to a serial loop;
14
+ // wrapping integer addition remains associative.
15
+ {% if useSubgroups %}
16
+ enable subgroups;
17
+ {% endif %}
18
+ {{ env.wgsl.resourceDeclarations }}
19
+
20
+ const WG: u32 = {{ workgroupSize }}u;
21
+ const IDENTITY: {{ accType }} = {{ accType }}(0.0);
22
+
23
+ fn combine(a: {{ accType }}, b: {{ accType }}) -> {{ accType }} {
24
+ return a + b;
25
+ }
26
+
27
+ {% if useSubgroups %}
28
+ var<workgroup> sgLaneClaims: atomic<u32>;
29
+
30
+ struct SubgroupLogicalLanes {
31
+ ltid: u32,
32
+ ord: u32,
33
+ rank: u32,
34
+ count: u32,
35
+ }
36
+
37
+ fn subgroup_logical_lanes(rank: u32, count: u32) -> SubgroupLogicalLanes {
38
+ // Every lane performs the atomic so no collective follows a lane guard: a
39
+ // subgroup op after a closed `if (rank == 0u)` block is a reconvergence
40
+ // hazard, since every subgroup operation must be reached in uniform control
41
+ // flow. Only the lane-0 lane adds its subgroup's claim, every other lane
42
+ // adds 0 and discards its snapshot. Lane 0 is the lowest lane, which is the
43
+ // lane `subgroupBroadcastFirst` reads.
44
+ let ticket = atomicAdd(&sgLaneClaims, select(0u, count | (1u << 16u), rank == 0u));
45
+ let claim = subgroupBroadcastFirst(ticket);
46
+ return SubgroupLogicalLanes((claim & 0xffffu) + rank, claim >> 16u, rank, count);
47
+ }
48
+
49
+ fn subgroup_logical_count() -> u32 {
50
+ return atomicLoad(&sgLaneClaims) >> 16u;
51
+ }
52
+ // One partial per subgroup, addressed by its logical ordinal. A workgroup of WG
53
+ // invocations holds at most WG subgroups at any adapter width, so WG is a sound
54
+ // bound with no width guard.
55
+ var<workgroup> sgTotals: array<{{ accType }}, WG>;
56
+
57
+ fn subgroupAddRows(v: vec4<f32>) -> vec4<f32> {
58
+ return vec4<f32>(subgroupAdd(v.x), subgroupAdd(v.y), subgroupAdd(v.z), subgroupAdd(v.w));
59
+ }
60
+ fn subgroupScanRows(v: vec4<f32>) -> vec4<f32> {
61
+ return vec4<f32>(subgroupInclusiveAdd(v.x), subgroupInclusiveAdd(v.y), subgroupInclusiveAdd(v.z), subgroupInclusiveAdd(v.w));
62
+ }
63
+
64
+ @compute @workgroup_size(WG, 1, 1)
65
+ fn main(@builtin(workgroup_id) wg: vec3<u32>,
66
+ @builtin(subgroup_invocation_id) sgLane: u32,
67
+ @builtin(subgroup_size) sgWidth: u32) {
68
+ // wg.y carries row-index bits past the per-dimension dispatch limit.
69
+ let row = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
70
+ if (row >= params.rows) {
71
+ return;
72
+ }
73
+ let outer_i = row / params.inner;
74
+ let inner_i = row % params.inner;
75
+ let base = outer_i * params.dim * params.inner + inner_i;
76
+ // `ltid` is the axis position this invocation owns and `ord` its subgroup's
77
+ // carry ordinal; both are dense and independent of the device's partition.
78
+ let L = subgroup_logical_lanes(sgLane, sgWidth);
79
+ let tid = L.ltid;
80
+ workgroupBarrier();
81
+ let numSub = subgroup_logical_count();
82
+ var carry: {{ accType }} = initial_state[inner_i];
83
+ let chunks = (params.dim + WG - 1u) / WG;
84
+ for (var c = 0u; c < chunks; c = c + 1u) {
85
+ let j = c * WG + tid;
86
+ let valid = j < params.dim;
87
+ {% if reverse %}
88
+ let d = params.dim - 1u - j;
89
+ {% else %}
90
+ let d = j;
91
+ {% endif %}
92
+ let src = base + d * params.inner;
93
+ var v: {{ accType }} = IDENTITY;
94
+ if (valid) {
95
+ v = {{ accType }}(x[src]);
96
+ }
97
+ let scannedSg = subgroupScanRows(v);
98
+ let totSg = subgroupAddRows(v);
99
+ if (L.rank == 0u) {
100
+ sgTotals[L.ord] = totSg;
101
+ }
102
+ workgroupBarrier();
103
+ var sgOffset: {{ accType }} = IDENTITY;
104
+ for (var s = 0u; s < L.ord; s = s + 1u) {
105
+ sgOffset = combine(sgOffset, sgTotals[s]);
106
+ }
107
+ var chunkTotal: {{ accType }} = IDENTITY;
108
+ for (var s = 0u; s < numSub; s = s + 1u) {
109
+ chunkTotal = combine(chunkTotal, sgTotals[s]);
110
+ }
111
+ let value = combine(carry, combine(sgOffset, scannedSg));
112
+ if (valid) {
113
+ y[src] = {{ scalar }}(value);
114
+ }
115
+ carry = combine(carry, chunkTotal);
116
+ workgroupBarrier();
117
+ }
118
+ if (tid == 0u) { final_state[inner_i] = carry; }
119
+ }
120
+ {% else %}
121
+ var<workgroup> wgScan: array<{{ accType }}, WG>;
122
+
123
+ @compute @workgroup_size(WG, 1, 1)
124
+ fn main(@builtin(workgroup_id) wg: vec3<u32>,
125
+ @builtin(local_invocation_id) lid: vec3<u32>) {
126
+ let row = wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u;
127
+ let tid = lid.x;
128
+ if (row >= params.rows) {
129
+ return;
130
+ }
131
+ let outer_i = row / params.inner;
132
+ let inner_i = row % params.inner;
133
+ let base = outer_i * params.dim * params.inner + inner_i;
134
+ var carry: {{ accType }} = initial_state[inner_i];
135
+ let chunks = (params.dim + WG - 1u) / WG;
136
+ for (var c = 0u; c < chunks; c = c + 1u) {
137
+ let j = c * WG + tid;
138
+ let valid = j < params.dim;
139
+ {% if reverse %}
140
+ let d = params.dim - 1u - j;
141
+ {% else %}
142
+ let d = j;
143
+ {% endif %}
144
+ let src = base + d * params.inner;
145
+ var v: {{ accType }} = IDENTITY;
146
+ if (valid) {
147
+ v = {{ accType }}(x[src]);
148
+ }
149
+ workgroupBarrier();
150
+ wgScan[tid] = v;
151
+ for (var step = 1u; step < WG; step = step << 1u) {
152
+ workgroupBarrier();
153
+ var prev: {{ accType }} = IDENTITY;
154
+ if (tid >= step) {
155
+ prev = wgScan[tid - step];
156
+ }
157
+ workgroupBarrier();
158
+ wgScan[tid] = combine(prev, wgScan[tid]);
159
+ }
160
+ workgroupBarrier();
161
+ let value = combine(carry, wgScan[tid]);
162
+ if (valid) {
163
+ y[src] = {{ scalar }}(value);
164
+ }
165
+ carry = combine(carry, wgScan[WG - 1u]);
166
+ }
167
+ if (tid == 0u) { final_state[inner_i] = carry; }
168
+ }
169
+ {% endif %}
build/webgpu/test.json CHANGED
@@ -200,9 +200,9 @@
200
  }
201
  },
202
  {
203
- "name": "coop_dim1025_above_old_fixed_gate",
204
  "provenance": {
205
- "notes": "A 256-step prefix sum across 1,025 state lanes exercises cooperative dispatch beyond 1,024 lanes when the adapter limits admit it."
206
  },
207
  "inputs": {
208
  "initial_state": { "dtype": "float32", "shape": [1025] },
@@ -340,7 +340,7 @@
340
  {
341
  "name": "multichunk_small_state_dim2_steps4096",
342
  "provenance": {
343
- "notes": "Compact correctness lock for a realistic long-stream recurrence with only two independent state lanes. The optimized path partitions the sequential axis into workgroup-sized chunks, scans chunk totals, then applies carries."
344
  },
345
  "inputs": {
346
  "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [1.5, -2.0] } },
@@ -354,6 +354,241 @@
354
  "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.001, "relTolerance": 0.0001 },
355
  "scan_output": { "dtype": "float32", "shape": [4096, 2], "tolerance": 0.001, "relTolerance": 0.0001 }
356
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  }
358
  ]
359
  }
 
200
  }
201
  },
202
  {
203
+ "name": "coop_dim1025_above_fixed_gate",
204
  "provenance": {
205
+ "notes": "A 1025-element state, one past 1024, runs a 256-step prefix sum; checks correctness when the per-step state exceeds a 1024-lane count on adapters whose limits allow it."
206
  },
207
  "inputs": {
208
  "initial_state": { "dtype": "float32", "shape": [1025] },
 
340
  {
341
  "name": "multichunk_small_state_dim2_steps4096",
342
  "provenance": {
343
+ "notes": "A long sequence with two independent state lanes checks recurrent state across chunk boundaries."
344
  },
345
  "inputs": {
346
  "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [1.5, -2.0] } },
 
354
  "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.001, "relTolerance": 0.0001 },
355
  "scan_output": { "dtype": "float32", "shape": [4096, 2], "tolerance": 0.001, "relTolerance": 0.0001 }
356
  }
357
+ },
358
+ {
359
+ "name": "vector_channel_full_lowered_prefix_sum_256x512",
360
+ "inputs": {
361
+ "initial_state": {
362
+ "dtype": "float32",
363
+ "shape": [512],
364
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
365
+ },
366
+ "scan_input": {
367
+ "dtype": "float32",
368
+ "shape": [256, 512],
369
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
370
+ }
371
+ },
372
+ "outputs": {
373
+ "final_state": { "dtype": "float32", "shape": [512], "tolerance": 0.0001 },
374
+ "scan_output": { "dtype": "float32", "shape": [256, 512], "tolerance": 0.0001 }
375
+ }
376
+ },
377
+ {
378
+ "name": "vector_channel_full_lowered_prefix_sum_2048x512",
379
+ "inputs": {
380
+ "initial_state": {
381
+ "dtype": "float32",
382
+ "shape": [512],
383
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
384
+ },
385
+ "scan_input": {
386
+ "dtype": "float32",
387
+ "shape": [2048, 512],
388
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
389
+ }
390
+ },
391
+ "outputs": {
392
+ "final_state": { "dtype": "float32", "shape": [512], "tolerance": 0.0001 },
393
+ "scan_output": { "dtype": "float32", "shape": [2048, 512], "tolerance": 0.0001 }
394
+ }
395
+ },
396
+ {
397
+ "name": "vector_channel_full_lowered_prefix_sum_256x128",
398
+ "inputs": {
399
+ "initial_state": {
400
+ "dtype": "float32",
401
+ "shape": [128],
402
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
403
+ },
404
+ "scan_input": {
405
+ "dtype": "float32",
406
+ "shape": [256, 128],
407
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
408
+ }
409
+ },
410
+ "outputs": {
411
+ "final_state": { "dtype": "float32", "shape": [128], "tolerance": 0.0001 },
412
+ "scan_output": { "dtype": "float32", "shape": [256, 128], "tolerance": 0.0001 }
413
+ }
414
+ },
415
+ {
416
+ "name": "vector_channel_full_coop_control_dim512_steps4096",
417
+ "inputs": {
418
+ "initial_state": {
419
+ "dtype": "float32",
420
+ "shape": [512],
421
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
422
+ },
423
+ "scan_input": {
424
+ "dtype": "float32",
425
+ "shape": [4096, 512],
426
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
427
+ }
428
+ },
429
+ "outputs": {
430
+ "final_state": { "dtype": "float32", "shape": [512], "tolerance": 0.0001 },
431
+ "scan_output": { "dtype": "float32", "shape": [4096, 512], "tolerance": 0.0001 }
432
+ }
433
+ },
434
+ {
435
+ "name": "vector_channel_full_lowered_largedim_fallback_dim4096_steps1024",
436
+ "inputs": {
437
+ "initial_state": {
438
+ "dtype": "float32",
439
+ "shape": [4096],
440
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
441
+ },
442
+ "scan_input": {
443
+ "dtype": "float32",
444
+ "shape": [1024, 4096],
445
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
446
+ }
447
+ },
448
+ "outputs": {
449
+ "final_state": { "dtype": "float32", "shape": [4096], "tolerance": 0.0001 },
450
+ "scan_output": { "dtype": "float32", "shape": [1024, 4096], "tolerance": 0.0001 }
451
+ }
452
+ },
453
+ {
454
+ "name": "vector_channel_full_coop_reverse_partialchunk_dim512_steps300",
455
+ "attrs": { "reverse": 1 },
456
+ "inputs": {
457
+ "initial_state": {
458
+ "dtype": "float32",
459
+ "shape": [512],
460
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
461
+ },
462
+ "scan_input": {
463
+ "dtype": "float32",
464
+ "shape": [300, 512],
465
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
466
+ }
467
+ },
468
+ "outputs": {
469
+ "final_state": { "dtype": "float32", "shape": [512], "tolerance": 0.0001 },
470
+ "scan_output": { "dtype": "float32", "shape": [300, 512], "tolerance": 0.0001 }
471
+ }
472
+ },
473
+ {
474
+ "name": "vector_channel_boundary_t63_d12_reverse0",
475
+ "attrs": { "reverse": 0 },
476
+ "inputs": {
477
+ "initial_state": {
478
+ "dtype": "float32",
479
+ "shape": [12],
480
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
481
+ },
482
+ "scan_input": {
483
+ "dtype": "float32",
484
+ "shape": [63, 12],
485
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
486
+ }
487
+ },
488
+ "outputs": {
489
+ "final_state": { "dtype": "float32", "shape": [12], "tolerance": 0.0001 },
490
+ "scan_output": { "dtype": "float32", "shape": [63, 12], "tolerance": 0.0001 }
491
+ }
492
+ },
493
+ {
494
+ "name": "vector_channel_boundary_t64_d12_reverse0",
495
+ "attrs": { "reverse": 0 },
496
+ "inputs": {
497
+ "initial_state": {
498
+ "dtype": "float32",
499
+ "shape": [12],
500
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
501
+ },
502
+ "scan_input": {
503
+ "dtype": "float32",
504
+ "shape": [64, 12],
505
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
506
+ }
507
+ },
508
+ "outputs": {
509
+ "final_state": { "dtype": "float32", "shape": [12], "tolerance": 0.0001 },
510
+ "scan_output": { "dtype": "float32", "shape": [64, 12], "tolerance": 0.0001 }
511
+ }
512
+ },
513
+ {
514
+ "name": "vector_channel_boundary_t65_d12_reverse1",
515
+ "attrs": { "reverse": 1 },
516
+ "inputs": {
517
+ "initial_state": {
518
+ "dtype": "float32",
519
+ "shape": [12],
520
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
521
+ },
522
+ "scan_input": {
523
+ "dtype": "float32",
524
+ "shape": [65, 12],
525
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
526
+ }
527
+ },
528
+ "outputs": {
529
+ "final_state": { "dtype": "float32", "shape": [12], "tolerance": 0.0001 },
530
+ "scan_output": { "dtype": "float32", "shape": [65, 12], "tolerance": 0.0001 }
531
+ }
532
+ },
533
+ {
534
+ "name": "vector_channel_boundary_t255_d4_reverse0",
535
+ "attrs": { "reverse": 0 },
536
+ "inputs": {
537
+ "initial_state": {
538
+ "dtype": "float32",
539
+ "shape": [4],
540
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
541
+ },
542
+ "scan_input": {
543
+ "dtype": "float32",
544
+ "shape": [255, 4],
545
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
546
+ }
547
+ },
548
+ "outputs": {
549
+ "final_state": { "dtype": "float32", "shape": [4], "tolerance": 0.0001 },
550
+ "scan_output": { "dtype": "float32", "shape": [255, 4], "tolerance": 0.0001 }
551
+ }
552
+ },
553
+ {
554
+ "name": "vector_channel_boundary_t257_d68_reverse1",
555
+ "attrs": { "reverse": 1 },
556
+ "inputs": {
557
+ "initial_state": {
558
+ "dtype": "float32",
559
+ "shape": [68],
560
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
561
+ },
562
+ "scan_input": {
563
+ "dtype": "float32",
564
+ "shape": [257, 68],
565
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
566
+ }
567
+ },
568
+ "outputs": {
569
+ "final_state": { "dtype": "float32", "shape": [68], "tolerance": 0.0001 },
570
+ "scan_output": { "dtype": "float32", "shape": [257, 68], "tolerance": 0.0001 }
571
+ }
572
+ },
573
+ {
574
+ "name": "vector_channel_boundary_t257_d69_reverse1",
575
+ "attrs": { "reverse": 1 },
576
+ "inputs": {
577
+ "initial_state": {
578
+ "dtype": "float32",
579
+ "shape": [69],
580
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.1 }
581
+ },
582
+ "scan_input": {
583
+ "dtype": "float32",
584
+ "shape": [257, 69],
585
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 }
586
+ }
587
+ },
588
+ "outputs": {
589
+ "final_state": { "dtype": "float32", "shape": [69], "tolerance": 0.0001 },
590
+ "scan_output": { "dtype": "float32", "shape": [257, 69], "tolerance": 0.0001 }
591
+ }
592
  }
593
  ]
594
  }