Xenova HF Staff commited on
Commit
d70fea3
·
verified ·
1 Parent(s): 76bc6f6

sync 2e7068faf55e

Browse files
README.md CHANGED
@@ -1,3 +1,64 @@
1
  ---
 
2
  license: apache-2.0
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ library_name: kernels
3
  license: apache-2.0
4
+ tags:
5
+ - kernel
6
+ - webgpu
7
+ - wgsl
8
  ---
9
+ # ai.onnx.Sum
10
+
11
+ `ai.onnx` · standard ONNX operator · ONNX opset ≥ 13
12
+
13
+ ## Description
14
+
15
+ Computes the elementwise sum of one or more input tensors with multidirectional (NumPy-style) broadcasting. All inputs and the output must share the same data type.
16
+
17
+ See the [ONNX `Sum` spec](https://onnx.ai/onnx/operators/onnx__Sum.html) for the reference semantics.
18
+
19
+ ## Inputs
20
+
21
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- | --- |
23
+ | `A` | `a` | `T` | — | — | First input tensor. | required |
24
+ | `B` | `b` | `T` | — | — | Second input tensor (optional). | optional |
25
+ | `C` | `c` | `T` | — | — | Third input tensor (optional). | optional |
26
+ | `D` | `d` | `T` | — | — | Fourth input tensor (optional). | optional |
27
+
28
+ ## Outputs
29
+
30
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
31
+ | --- | --- | --- | --- | --- | --- | --- |
32
+ | `sum` | `y` | `T` | derived | derived; see description | Elementwise sum of all provided input tensors. | required |
33
+
34
+ ## Type constraints
35
+
36
+ | Variable | Allowed dtypes |
37
+ | --- | --- |
38
+ | `T` | `float32`, `float16` |
39
+
40
+ ## Files
41
+
42
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
43
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
44
+ - [`test.json`](build/webgpu/test.json) — correctness cases
45
+ - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
46
+ - [`datamove-elementwise-copy.wgsl.jinja`](build/webgpu/datamove-elementwise-copy.wgsl.jinja)
47
+ - [`summean-broadcast.wgsl.jinja`](build/webgpu/summean-broadcast.wgsl.jinja)
48
+ - [`summean-vec4.wgsl.jinja`](build/webgpu/summean-vec4.wgsl.jinja)
49
+
50
+ ## Use with `@huggingface/kernels`
51
+
52
+ The loader derives every required output's shape and logical dtype from the manifest contract and this call.
53
+ It then allocates the result tensors automatically.
54
+
55
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
56
+
57
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
58
+
59
+ ```js
60
+ import { getKernel } from "@huggingface/kernels";
61
+
62
+ const kernel = await getKernel("webgpu-kernels/ai.onnx.Sum", { version: 1 });
63
+ const { y } = await kernel({ a: { data: aData, shape: [3] } });
64
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Sum",
3
+ "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
4
+ "cases": [
5
+ {
6
+ "name": "sum-f32-4m",
7
+ "preset": "smoke",
8
+ "vars": { "dtype": "float32", "count": 4194304 },
9
+ "inputs": {
10
+ "a": { "dtype": "float32", "shape": [4194304], "dist": "normal", "seed": 702, "scale": 2 },
11
+ "b": { "dtype": "float32", "shape": [4194304], "dist": "normal", "seed": 703, "scale": 2 }
12
+ },
13
+ "outputs": { "y": { "dtype": "float32", "shape": [4194304] } },
14
+ "bench": {
15
+ "primary": true,
16
+ "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 3" }]
17
+ }
18
+ },
19
+ {
20
+ "name": "sum-f32-broadcast-scalar-4m-unaligned",
21
+ "preset": "stress",
22
+ "vars": { "dtype": "float32", "count": 4194302 },
23
+ "inputs": {
24
+ "a": { "dtype": "float32", "shape": [4194302], "dist": "normal", "seed": 711, "scale": 2 },
25
+ "b": { "dtype": "float32", "shape": [4194302], "dist": "normal", "seed": 712, "scale": 2 }
26
+ },
27
+ "outputs": { "y": { "dtype": "float32", "shape": [4194302] } },
28
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 3" }] }
29
+ },
30
+ {
31
+ "name": "sum-f32-four-input-rank4-mixed-broadcast-pathology",
32
+ "preset": "stress",
33
+ "vars": { "dtype": "float32", "count": 2097152 },
34
+ "inputs": {
35
+ "a": { "dtype": "float32", "shape": [8, 64, 64, 64], "dist": "normal", "seed": 713, "scale": 2 },
36
+ "b": { "dtype": "float32", "shape": [1, 64, 1, 1], "dist": "normal", "seed": 714, "scale": 0.25 },
37
+ "c": { "dtype": "float32", "shape": [8, 1, 64, 64], "dist": "normal", "seed": 715, "scale": 0.5 },
38
+ "d": { "dtype": "float32", "shape": [1], "data": { "kind": "constant", "value": 0.125 } }
39
+ },
40
+ "outputs": { "y": { "dtype": "float32", "shape": [8, 64, 64, 64], "dist": "empty" } },
41
+ "bench": {
42
+ "metrics": [
43
+ {
44
+ "type": "bandwidth",
45
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.a) + numel(shapes.b) + numel(shapes.c) + numel(shapes.d) + numel(shapes.y))"
46
+ }
47
+ ]
48
+ }
49
+ },
50
+ {
51
+ "name": "sum-f32-three-input-rank4-mixed-broadcast-pathology",
52
+ "preset": "stress",
53
+ "vars": { "dtype": "float32", "count": 2097152 },
54
+ "inputs": {
55
+ "a": { "dtype": "float32", "shape": [8, 64, 64, 64], "dist": "normal", "seed": 716, "scale": 2 },
56
+ "b": { "dtype": "float32", "shape": [1, 64, 1, 1], "dist": "normal", "seed": 717, "scale": 0.25 },
57
+ "c": { "dtype": "float32", "shape": [8, 1, 64, 64], "dist": "normal", "seed": 718, "scale": 0.5 }
58
+ },
59
+ "outputs": { "y": { "dtype": "float32", "shape": [8, 64, 64, 64], "dist": "empty" } },
60
+ "bench": {
61
+ "primary": true,
62
+ "metrics": [
63
+ {
64
+ "type": "bandwidth",
65
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.a) + numel(shapes.b) + numel(shapes.c) + numel(shapes.y))"
66
+ }
67
+ ]
68
+ }
69
+ }
70
+ ]
71
+ }
build/webgpu/datamove-elementwise-copy.wgsl.jinja ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if usesF16 %}
2
+ enable f16;
3
+ {% endif %}
4
+ {{ env.wgsl.resourceDeclarations }}
5
+
6
+ const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
7
+
8
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
9
+ fn main(
10
+ @builtin(global_invocation_id) gid: vec3<u32>,
11
+ @builtin(num_workgroups) nwg: vec3<u32>
12
+ ) {
13
+ // Grid-stride loop: the dispatch is clamped to the maxComputeWorkgroupsPerDimension
14
+ // workgroups-per-dimension limit, so a thread may copy more than one
15
+ // element for very large tensors.
16
+ let stride = nwg.x * WG;
17
+ for (var i = gid.x; i < params.count; i += stride) {
18
+ y[i] = x[i];
19
+ }
20
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "ai.onnx",
3
+ "name": "Sum",
4
+ "sinceVersion": 13,
5
+ "description": "Computes the elementwise sum of one or more input tensors with multidirectional (NumPy-style) broadcasting. All inputs and the output must share the same data type.",
6
+ "inputs": [
7
+ { "role": "A", "dtype": "T", "description": "First input tensor." },
8
+ { "role": "B", "dtype": "T", "optional": true, "description": "Second input tensor (optional)." },
9
+ { "role": "C", "dtype": "T", "optional": true, "description": "Third input tensor (optional)." },
10
+ { "role": "D", "dtype": "T", "optional": true, "description": "Fourth input tensor (optional)." }
11
+ ],
12
+ "outputs": [
13
+ {
14
+ "role": "sum",
15
+ "dtype": "T",
16
+ "rank": "max(ranks.A, ranks.B if present.b else 0, ranks.C if present.b and present.c else 0, ranks.D if present.b and present.c and present.d else 0)",
17
+ "shape": "variadicShape",
18
+ "description": "Elementwise sum of all provided input tensors."
19
+ }
20
+ ],
21
+ "typeConstraints": { "T": ["float32", "float16"] },
22
+ "args": {
23
+ "a": { "kind": "tensor", "semantic": "A", "role": "input" },
24
+ "b": { "kind": "tensor", "semantic": "B", "role": "input", "required": false },
25
+ "c": { "kind": "tensor", "semantic": "C", "role": "input2", "required": false },
26
+ "y": { "kind": "tensor", "semantic": "sum", "role": "output" },
27
+ "d": { "kind": "tensor", "semantic": "D", "role": "input3", "required": false }
28
+ },
29
+ "tunables": { "WORKGROUP_SIZE": 256 },
30
+ "derive": {
31
+ "variadicShape": "broadcastShape(broadcastShape(broadcastShape(shapes.A, shapes.B), shapes.C), shapes.D) if present.b and present.c and present.d else (broadcastShape(broadcastShape(shapes.A, shapes.B), shapes.C) if present.b and present.c else (broadcastShape(shapes.A, shapes.B) if present.b else shapes.A))",
32
+ "flatVec4OutputOk": "numel(shapes.y) > 0 and numel(shapes.y) % 4 == 0 and f16Ok(dtypes.T)",
33
+ "broadcastOutputOk": "f16Ok(dtypes.T)",
34
+ "broadcastVec4OutputOk": "ranks.y >= 1 and dim(shapes.y, ranks.y - 1) > 0 and dim(shapes.y, ranks.y - 1) % 4 == 0 and f16Ok(dtypes.T)",
35
+ "aBroadcastVec4Ok": "ranks.A <= ranks.y and (ranks.A == 0 or dim(shapes.A, ranks.A - 1) == 1 or dim(shapes.A, ranks.A - 1) == dim(shapes.y, ranks.y - 1))",
36
+ "bBroadcastVec4Ok": "not present.b or (ranks.B <= ranks.y and (ranks.B == 0 or dim(shapes.B, ranks.B - 1) == 1 or dim(shapes.B, ranks.B - 1) == dim(shapes.y, ranks.y - 1)))",
37
+ "cBroadcastVec4Ok": "not present.c or (ranks.C <= ranks.y and (ranks.C == 0 or dim(shapes.C, ranks.C - 1) == 1 or dim(shapes.C, ranks.C - 1) == dim(shapes.y, ranks.y - 1)))",
38
+ "dBroadcastVec4Ok": "not present.d or (ranks.D <= ranks.y and (ranks.D == 0 or dim(shapes.D, ranks.D - 1) == 1 or dim(shapes.D, ranks.D - 1) == dim(shapes.y, ranks.y - 1)))"
39
+ },
40
+ "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"" },
41
+ "bindingSets": {
42
+ "identity": [
43
+ { "name": "x", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
44
+ { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
45
+ {
46
+ "name": "params",
47
+ "semantic": "kernel.params",
48
+ "buffer": { "type": "uniform" },
49
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
50
+ }
51
+ ],
52
+ "vec4Two": [
53
+ {
54
+ "name": "a",
55
+ "arg": "a",
56
+ "semantic": "A",
57
+ "buffer": { "type": "read-only-storage" },
58
+ "elementType": "$vectorScalar"
59
+ },
60
+ {
61
+ "name": "b",
62
+ "arg": "b",
63
+ "semantic": "B",
64
+ "buffer": { "type": "read-only-storage" },
65
+ "elementType": "$vectorScalar"
66
+ },
67
+ { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
68
+ {
69
+ "name": "params",
70
+ "semantic": "kernel.params",
71
+ "buffer": { "type": "uniform" },
72
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
73
+ }
74
+ ],
75
+ "vec4Three": [
76
+ {
77
+ "name": "a",
78
+ "arg": "a",
79
+ "semantic": "A",
80
+ "buffer": { "type": "read-only-storage" },
81
+ "elementType": "$aElement"
82
+ },
83
+ {
84
+ "name": "b",
85
+ "arg": "b",
86
+ "semantic": "B",
87
+ "buffer": { "type": "read-only-storage" },
88
+ "elementType": "$bElement"
89
+ },
90
+ {
91
+ "name": "c",
92
+ "arg": "c",
93
+ "semantic": "C",
94
+ "buffer": { "type": "read-only-storage" },
95
+ "elementType": "$cElement"
96
+ },
97
+ { "name": "y", "arg": "y", "semantic": "sum", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
98
+ {
99
+ "name": "params",
100
+ "semantic": "kernel.params",
101
+ "buffer": { "type": "uniform" },
102
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
103
+ }
104
+ ],
105
+ "vec4Four": [
106
+ {
107
+ "name": "a",
108
+ "arg": "a",
109
+ "semantic": "A",
110
+ "buffer": { "type": "read-only-storage" },
111
+ "elementType": "$aElement"
112
+ },
113
+ {
114
+ "name": "b",
115
+ "arg": "b",
116
+ "semantic": "B",
117
+ "buffer": { "type": "read-only-storage" },
118
+ "elementType": "$bElement"
119
+ },
120
+ {
121
+ "name": "c",
122
+ "arg": "c",
123
+ "semantic": "C",
124
+ "buffer": { "type": "read-only-storage" },
125
+ "elementType": "$cElement"
126
+ },
127
+ {
128
+ "name": "d",
129
+ "arg": "d",
130
+ "semantic": "D",
131
+ "buffer": { "type": "read-only-storage" },
132
+ "elementType": "$dElement"
133
+ },
134
+ { "name": "y", "arg": "y", "semantic": "sum", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
135
+ {
136
+ "name": "params",
137
+ "semantic": "kernel.params",
138
+ "buffer": { "type": "uniform" },
139
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
140
+ }
141
+ ],
142
+ "scalarTwo": [
143
+ { "name": "a", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
144
+ { "name": "b", "arg": "b", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
145
+ { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
146
+ {
147
+ "name": "params",
148
+ "semantic": "kernel.params",
149
+ "buffer": { "type": "uniform" },
150
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
151
+ }
152
+ ],
153
+ "scalarThree": [
154
+ { "name": "a", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
155
+ { "name": "b", "arg": "b", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
156
+ { "name": "c", "arg": "c", "semantic": "C", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
157
+ { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
158
+ {
159
+ "name": "params",
160
+ "semantic": "kernel.params",
161
+ "buffer": { "type": "uniform" },
162
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
163
+ }
164
+ ],
165
+ "scalarFour": [
166
+ { "name": "a", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
167
+ { "name": "b", "arg": "b", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
168
+ { "name": "c", "arg": "c", "semantic": "C", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
169
+ { "name": "d", "arg": "d", "semantic": "D", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
170
+ { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
171
+ {
172
+ "name": "params",
173
+ "semantic": "kernel.params",
174
+ "buffer": { "type": "uniform" },
175
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
176
+ }
177
+ ]
178
+ },
179
+ "variants": [
180
+ {
181
+ "id": "single_input_identity",
182
+ "priority": 30,
183
+ "when": ["not present.b", "not present.c", "ranks.A == ranks.y", "numel(shapes.A) == numel(shapes.y)", "f16Ok(dtypes.T)"],
184
+ "passes": [
185
+ {
186
+ "id": "main",
187
+ "name": "Sum.Identity",
188
+ "shader": "datamove-elementwise-copy.wgsl.jinja",
189
+ "bindings": "identity",
190
+ "dispatch": { "gridStride": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
191
+ }
192
+ ]
193
+ },
194
+ {
195
+ "id": "same_shape_vec4_two_input",
196
+ "priority": 20,
197
+ "when": ["present.b", "not present.c", "sameShape(shapes.A, shapes.y)", "sameShape(shapes.B, shapes.y)", "flatVec4OutputOk"],
198
+ "constants": {
199
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
200
+ "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
201
+ "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
202
+ "cElement": "\"vec4<\" ~ dtypes.T ~ \">\""
203
+ },
204
+ "passes": [
205
+ {
206
+ "id": "main",
207
+ "name": "Sum.vec4",
208
+ "source": { "shader": "summean-vec4.wgsl.jinja", "inputs": { "op": "\"sum\"", "hasC": "false" } },
209
+ "bindings": "vec4Two",
210
+ "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
211
+ }
212
+ ]
213
+ },
214
+ {
215
+ "id": "same_shape_vec4_three_input",
216
+ "priority": 25,
217
+ "when": ["present.b", "present.c", "sameShape(shapes.A, shapes.y)", "sameShape(shapes.B, shapes.y)", "sameShape(shapes.C, shapes.y)", "flatVec4OutputOk", "not present.d"],
218
+ "constants": {
219
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
220
+ "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
221
+ "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
222
+ "cElement": "\"vec4<\" ~ dtypes.T ~ \">\""
223
+ },
224
+ "passes": [
225
+ {
226
+ "id": "main",
227
+ "name": "Sum.vec4_3",
228
+ "source": { "shader": "summean-vec4.wgsl.jinja", "inputs": { "op": "\"sum\"", "hasC": "true" } },
229
+ "bindings": "vec4Three",
230
+ "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
231
+ }
232
+ ]
233
+ },
234
+ {
235
+ "id": "same_shape_vec4_four_input",
236
+ "priority": 27,
237
+ "when": ["present.b", "present.c", "present.d", "sameShape(shapes.A, shapes.y)", "sameShape(shapes.B, shapes.y)", "sameShape(shapes.C, shapes.y)", "sameShape(shapes.D, shapes.y)", "flatVec4OutputOk"],
238
+ "constants": {
239
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
240
+ "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
241
+ "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
242
+ "cElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
243
+ "dElement": "\"vec4<\" ~ dtypes.T ~ \">\""
244
+ },
245
+ "passes": [
246
+ {
247
+ "id": "main",
248
+ "name": "Sum.vec4_3",
249
+ "source": {
250
+ "shader": "summean-vec4.wgsl.jinja",
251
+ "inputs": { "op": "\"sum\"", "hasC": "true", "hasD": "true" }
252
+ },
253
+ "bindings": "vec4Four",
254
+ "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
255
+ }
256
+ ]
257
+ },
258
+ {
259
+ "id": "broadcast_two_input",
260
+ "when": ["present.b", "not present.c", "ranks.A <= ranks.y", "ranks.B <= ranks.y", "broadcastOutputOk"],
261
+ "passes": [
262
+ {
263
+ "id": "main",
264
+ "name": "Sum",
265
+ "source": {
266
+ "shader": "summean-broadcast.wgsl.jinja",
267
+ "inputs": {
268
+ "aShape": "shapes.A",
269
+ "bShape": "shapes.B",
270
+ "yShape": "shapes.y",
271
+ "aRank": "ranks.A",
272
+ "bRank": "ranks.B",
273
+ "yRank": "ranks.y",
274
+ "hasC": "false",
275
+ "op": "\"sum\""
276
+ }
277
+ },
278
+ "bindings": "scalarTwo",
279
+ "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
280
+ }
281
+ ]
282
+ },
283
+ {
284
+ "id": "broadcast_three_input_vec4",
285
+ "priority": 24,
286
+ "when": ["present.b", "present.c", "not present.d", "broadcastVec4OutputOk", "aBroadcastVec4Ok", "bBroadcastVec4Ok", "cBroadcastVec4Ok"],
287
+ "constants": {
288
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
289
+ "aElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1 else dtypes.T",
290
+ "bElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1 else dtypes.T",
291
+ "cElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1 else dtypes.T"
292
+ },
293
+ "passes": [
294
+ {
295
+ "id": "main",
296
+ "name": "Sum.Broadcast3Vec4",
297
+ "source": {
298
+ "shader": "summean-broadcast.wgsl.jinja",
299
+ "inputs": {
300
+ "aShape": "shapes.A",
301
+ "bShape": "shapes.B",
302
+ "cShape": "shapes.C",
303
+ "yShape": "shapes.y",
304
+ "aRank": "ranks.A",
305
+ "bRank": "ranks.B",
306
+ "cRank": "ranks.C",
307
+ "yRank": "ranks.y",
308
+ "aVector": "ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1",
309
+ "bVector": "ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1",
310
+ "cVector": "ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1",
311
+ "hasC": true,
312
+ "vectorized": true,
313
+ "op": "\"sum\""
314
+ }
315
+ },
316
+ "bindings": "vec4Three",
317
+ "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
318
+ }
319
+ ]
320
+ },
321
+ {
322
+ "id": "broadcast_three_input",
323
+ "priority": 20,
324
+ "when": ["present.b", "present.c", "ranks.A <= ranks.y", "ranks.B <= ranks.y", "ranks.C <= ranks.y", "broadcastOutputOk", "not present.d"],
325
+ "passes": [
326
+ {
327
+ "id": "main",
328
+ "name": "Sum",
329
+ "source": {
330
+ "shader": "summean-broadcast.wgsl.jinja",
331
+ "inputs": {
332
+ "aShape": "shapes.A",
333
+ "bShape": "shapes.B",
334
+ "cShape": "shapes.C",
335
+ "yShape": "shapes.y",
336
+ "aRank": "ranks.A",
337
+ "bRank": "ranks.B",
338
+ "cRank": "ranks.C",
339
+ "yRank": "ranks.y",
340
+ "hasC": "true",
341
+ "op": "\"sum\""
342
+ }
343
+ },
344
+ "bindings": "scalarThree",
345
+ "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
346
+ }
347
+ ]
348
+ },
349
+ {
350
+ "id": "broadcast_four_input",
351
+ "priority": 22,
352
+ "when": ["present.b", "present.c", "present.d", "ranks.A <= ranks.y", "ranks.B <= ranks.y", "ranks.C <= ranks.y", "ranks.D <= ranks.y", "broadcastOutputOk"],
353
+ "passes": [
354
+ {
355
+ "id": "main",
356
+ "name": "Sum",
357
+ "source": {
358
+ "shader": "summean-broadcast.wgsl.jinja",
359
+ "inputs": {
360
+ "aShape": "shapes.A",
361
+ "bShape": "shapes.B",
362
+ "cShape": "shapes.C",
363
+ "yShape": "shapes.y",
364
+ "aRank": "ranks.A",
365
+ "bRank": "ranks.B",
366
+ "cRank": "ranks.C",
367
+ "yRank": "ranks.y",
368
+ "hasC": "true",
369
+ "op": "\"sum\"",
370
+ "dShape": "shapes.D",
371
+ "dRank": "ranks.D",
372
+ "hasD": "true"
373
+ }
374
+ },
375
+ "bindings": "scalarFour",
376
+ "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
377
+ }
378
+ ]
379
+ },
380
+ {
381
+ "id": "broadcast_four_input_vec4",
382
+ "priority": 26,
383
+ "when": ["present.b", "present.c", "present.d", "broadcastVec4OutputOk", "aBroadcastVec4Ok", "bBroadcastVec4Ok", "cBroadcastVec4Ok", "dBroadcastVec4Ok"],
384
+ "constants": {
385
+ "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
386
+ "aElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1 else dtypes.T",
387
+ "bElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1 else dtypes.T",
388
+ "cElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1 else dtypes.T",
389
+ "dElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.D > 0 and dim(shapes.D, ranks.D - 1) != 1 else dtypes.T"
390
+ },
391
+ "passes": [
392
+ {
393
+ "id": "main",
394
+ "name": "Sum.Broadcast4Vec4",
395
+ "source": {
396
+ "shader": "summean-broadcast.wgsl.jinja",
397
+ "inputs": {
398
+ "aShape": "shapes.A",
399
+ "bShape": "shapes.B",
400
+ "cShape": "shapes.C",
401
+ "dShape": "shapes.D",
402
+ "yShape": "shapes.y",
403
+ "aRank": "ranks.A",
404
+ "bRank": "ranks.B",
405
+ "cRank": "ranks.C",
406
+ "dRank": "ranks.D",
407
+ "yRank": "ranks.y",
408
+ "aVector": "ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1",
409
+ "bVector": "ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1",
410
+ "cVector": "ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1",
411
+ "dVector": "ranks.D > 0 and dim(shapes.D, ranks.D - 1) != 1",
412
+ "hasC": true,
413
+ "hasD": true,
414
+ "vectorized": true,
415
+ "op": "\"sum\""
416
+ }
417
+ },
418
+ "bindings": "vec4Four",
419
+ "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
420
+ }
421
+ ]
422
+ }
423
+ ]
424
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ai.onnx.Sum",
3
+ "id": "_ai_onnx_sum_webgpu_2c2501b",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "BZH67s4WnBU60TILXvJnrpu86w673p/81oS0ITOTOjw=",
11
+ "datamove-elementwise-copy.wgsl.jinja": "J5yC2bAddPiP+odXLgVGS3TJ9jeNsfRTvedKrj/fhZg=",
12
+ "manifest.json": "n3EnmVEajNhmmHf1ueSFLaPzePrvHY41jyuk9AfSxWM=",
13
+ "summean-broadcast.wgsl.jinja": "6BaP0d/1jnidmzD5A+BolzRw9fhm4PaQa4nAjjGCVmI=",
14
+ "summean-vec4.wgsl.jinja": "kS8/lN2swjqu28ALxVcF4rkhpEfMfqUGRxyMf770veU=",
15
+ "test.json": "DUAhzo/YUbzJAmNXu5b92KKxvQCxcDDuuNeen2eTyfI="
16
+ }
17
+ },
18
+ "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
19
+ "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Sum" }
20
+ }
build/webgpu/summean-broadcast.wgsl.jinja ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% set allInputs = ["a", "b"] + (source.extraInputs if source.extraInputs is defined else (["c"] if source.hasC else []) + (["d"] if source.hasD else [])) %}
2
+ {% set extraInputs = source.extraInputs if source.extraInputs is defined else (["c"] if source.hasC else []) + (["d"] if source.hasD else []) %}
3
+ {% if usesF16 %}
4
+ enable f16;
5
+ {% endif %}
6
+ {{ env.wgsl.resourceDeclarations }}
7
+ {% macro offset_fn(fn_name, opShape, opRank, op_same, op_numel, outShape, outRank, out_numel) %}
8
+ fn {{ fn_name }}({% if out_numel != 0 and op_numel != 1 %}out_index: u32{% endif %}) -> u32 {
9
+ {% if out_numel == 0 %}
10
+ return 0u;
11
+ {% elif op_numel == 1 %}
12
+ return 0u;
13
+ {% elif op_same %}
14
+ return out_index;
15
+ {% else %}
16
+ var offset = 0u;
17
+ {% for axis in range(outRank) %}
18
+ {% set op_axis = axis - (outRank - opRank) %}
19
+ {% if op_axis >= 0 and opShape[op_axis] != 1 %}
20
+ {% set c_stride = namespace(value=1) %}
21
+ {% for j in range(axis + 1, outRank) %}
22
+ {% set c_stride.value = c_stride.value * outShape[j] %}
23
+ {% endfor %}
24
+ {% set op_stride = namespace(value=1) %}
25
+ {% for j in range(op_axis + 1, opRank) %}
26
+ {% set op_stride.value = op_stride.value * opShape[j] %}
27
+ {% endfor %}
28
+ {% if c_stride.value == 1 %}
29
+ let coord{{ axis }} = out_index % {{ outShape[axis] }}u;
30
+ {% else %}
31
+ let coord{{ axis }} = (out_index / {{ c_stride.value }}u) % {{ outShape[axis] }}u;
32
+ {% endif %}
33
+ {% if op_stride.value == 1 %}
34
+ offset = offset + coord{{ axis }};
35
+ {% else %}
36
+ offset = offset + coord{{ axis }} * {{ op_stride.value }}u;
37
+ {% endif %}
38
+ {% endif %}
39
+ {% endfor %}
40
+ return offset;
41
+ {% endif %}
42
+ }
43
+ {%- endmacro %}{% macro broadcast_offset_call(fn_name, opShape, outShape, out_index) %}
44
+ {% set op_numel = namespace(value=1) %}
45
+ {% for d in opShape %}{% set op_numel.value = op_numel.value * d %}{% endfor %}
46
+ {% set out_numel = namespace(value=1) %}
47
+ {% for d in outShape %}{% set out_numel.value = out_numel.value * d %}{% endfor %}
48
+ {{ fn_name }}({% if out_numel.value != 0 and op_numel.value != 1 %}{{ out_index }}{% endif %})
49
+ {%- endmacro %}{% macro broadcast_offset_fn(fn_name, opShape, opRank, outShape, outRank) %}
50
+ {% set op_numel = namespace(value=1) %}
51
+ {% for d in opShape %}
52
+ {% set op_numel.value = op_numel.value * d %}
53
+ {% endfor %}
54
+ {% set out_numel = namespace(value=1) %}
55
+ {% for d in outShape %}
56
+ {% set out_numel.value = out_numel.value * d %}
57
+ {% endfor %}
58
+ {% set op_same = namespace(value=(opRank == outRank)) %}
59
+ {% if op_same.value %}
60
+ {% for axis in range(outRank) %}
61
+ {% if opShape[axis] != outShape[axis] %}
62
+ {% set op_same.value = false %}
63
+ {% endif %}
64
+ {% endfor %}
65
+ {% endif %}
66
+ {{ offset_fn(fn_name, opShape, opRank, op_same.value, op_numel.value, outShape, outRank, out_numel.value) }}
67
+ {%- endmacro %}
68
+
69
+
70
+
71
+ {{ broadcast_offset_fn("a_offset", source.aShape, source.aRank, source.yShape, source.yRank) }}
72
+
73
+ {{ broadcast_offset_fn("b_offset", source.bShape, source.bRank, source.yShape, source.yRank) }}
74
+
75
+ {% for n in extraInputs %}
76
+ {{ broadcast_offset_fn(n ~ "_offset", source[n ~ "Shape"], source[n ~ "Rank"], source.yShape, source.yRank) }}
77
+
78
+ {% endfor %}
79
+
80
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
81
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
82
+ // 2D-folded flat index: gid.y carries the high bits past the
83
+ // maxComputeWorkgroupsPerDimension limit.
84
+ let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
85
+ if (i >= params.count) {
86
+ return;
87
+ }
88
+ {% if source.vectorized %}
89
+ // The output's innermost dimension is four-aligned. Each input either keeps
90
+ // that dimension (one aligned vec4 load) or broadcasts it (one scalar splat).
91
+ // Offset folding is therefore paid once per four outputs without changing
92
+ // multidirectional broadcast semantics on any outer dimension.
93
+ let base = i * 4u;
94
+ {% for n in allInputs %}
95
+ {% if source[n ~ "Vector"] %}
96
+ let {{ n }}v = vec4<f32>({{ n }}[{{ broadcast_offset_call(n ~ "_offset", source[n ~ "Shape"], source.yShape, "base") }} / 4u]);
97
+ {% else %}
98
+ let {{ n }}v = vec4<f32>(f32({{ n }}[{{ broadcast_offset_call(n ~ "_offset", source[n ~ "Shape"], source.yShape, "base") }}]));
99
+ {% endif %}
100
+ {% endfor %}
101
+ let total = {% for n in allInputs %}{{ n }}v{% if not loop.last %} + {% endif %}{% endfor %};
102
+ y[i] = {{ vectorScalar }}(total);
103
+ {% else %}
104
+ let total = {% for n in allInputs %}f32({{ n }}[{{ broadcast_offset_call(n ~ "_offset", source[n ~ "Shape"], source.yShape, "i") }}]){% if not loop.last %} + {% endif %}{% endfor %};
105
+ y[i] = {{ scalar }}(total);
106
+ {% endif %}
107
+ }
build/webgpu/summean-vec4.wgsl.jinja ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% set extraInputs = source.extraInputs if source.extraInputs is defined else (["c"] if source.hasC else []) + (["d"] if source.hasD else []) %}
2
+ {% if usesF16 %}
3
+ enable f16;
4
+ {% endif %}
5
+ {{ env.wgsl.resourceDeclarations }}
6
+
7
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
8
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
9
+ // 2D-folded flat index: gid.y carries the high bits past the
10
+ // maxComputeWorkgroupsPerDimension limit.
11
+ let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
12
+ if (i >= params.count) {
13
+ return;
14
+ }
15
+ let av = a[i];
16
+ let bv = b[i];
17
+ {% if scalar == "f16" %}
18
+ var total = vec4<f32>(av) + vec4<f32>(bv);
19
+ {% for n in extraInputs %}
20
+ total = total + vec4<f32>({{ n }}[i]);
21
+ {% endfor %}
22
+ y[i] = vec4<f16>(total);
23
+ {% else %}
24
+ var total = av + bv;
25
+ {% for n in extraInputs %}
26
+ total = total + {{ n }}[i];
27
+ {% endfor %}
28
+ y[i] = total;
29
+ {% endif %}
30
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,578 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Sum",
3
+ "cases": [
4
+ {
5
+ "name": "max_arity_float16_positions",
6
+ "provenance": {
7
+ "notes": "Synthetic four-input float16 Sum contract fixture; element-varying operands make every bounded input position contribute to a non-constant exact sum."
8
+ },
9
+ "inputs": {
10
+ "a": { "dtype": "float16", "shape": [5], "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0] } },
11
+ "b": { "dtype": "float16", "shape": [5], "data": { "kind": "values", "values": [2.0, 4.0, 6.0, 8.0, 10.0] } },
12
+ "c": { "dtype": "float16", "shape": [5], "data": { "kind": "values", "values": [4.0, 1.0, 3.0, 2.0, 5.0] } },
13
+ "d": { "dtype": "float16", "shape": [5], "data": { "kind": "values", "values": [8.0, 2.0, 1.0, 4.0, 3.0] } }
14
+ },
15
+ "outputs": {
16
+ "y": {
17
+ "dtype": "float16",
18
+ "shape": [5],
19
+ "tolerance": 0,
20
+ "data": { "kind": "values", "values": [15.0, 9.0, 13.0, 18.0, 23.0] }
21
+ }
22
+ }
23
+ },
24
+ {
25
+ "name": "same_shape",
26
+ "inputs": {
27
+ "a": {
28
+ "dtype": "float32",
29
+ "shape": [2, 3],
30
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29 }
31
+ },
32
+ "b": {
33
+ "dtype": "float32",
34
+ "shape": [2, 3],
35
+ "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.07 }
36
+ }
37
+ },
38
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3], "tolerance": 0.000001 } }
39
+ },
40
+ {
41
+ "name": "f32_subnormal_three_input_sum_gpu_gap",
42
+ "skipGpu": {
43
+ "category": "permanent",
44
+ "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes float32 subnormals to zero; the subnormal addends/sum cannot be reproduced on GPU."
45
+ },
46
+ "provenance": {
47
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
48
+ "test": "MathOpTest.Sum_8_Test1",
49
+ "notes": "Variadic Sum must preserve finite subnormal totals; a flush-to-zero path erases residual-like lanes before downstream scaling can amplify them."
50
+ },
51
+ "inputs": {
52
+ "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1e-40, -1e-40, 0.0] } },
53
+ "b": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1e-40, -1e-40, 1e-40] } },
54
+ "c": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1e-40, -1e-40, -1e-40] } }
55
+ },
56
+ "outputs": {
57
+ "y": {
58
+ "dtype": "float32",
59
+ "shape": [3],
60
+ "tolerance": 2e-45,
61
+ "data": { "kind": "values", "values": [3e-40, -3e-40, 0.0] }
62
+ }
63
+ }
64
+ },
65
+ {
66
+ "name": "f32_subnormal_three_input_sum_vec4_gpu_gap",
67
+ "skipGpu": {
68
+ "category": "permanent",
69
+ "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes float32 subnormals to zero; the subnormal addends/sum cannot be reproduced on GPU."
70
+ },
71
+ "provenance": {
72
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
73
+ "test": "MathOpTest.Sum_8_Test1",
74
+ "notes": "Vec4 companion: variadic Sum must preserve finite subnormal totals in the vectorized path."
75
+ },
76
+ "inputs": {
77
+ "a": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [1e-40, -1e-40, 0.0, 1e-39] } },
78
+ "b": {
79
+ "dtype": "float32",
80
+ "shape": [4],
81
+ "data": { "kind": "values", "values": [1e-40, -1e-40, 1e-40, -1e-39] }
82
+ },
83
+ "c": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [1e-40, -1e-40, -1e-40, 0.0] } }
84
+ },
85
+ "outputs": {
86
+ "y": {
87
+ "dtype": "float32",
88
+ "shape": [4],
89
+ "tolerance": 2e-45,
90
+ "data": { "kind": "values", "values": [3e-40, -3e-40, 0.0, 0.0] }
91
+ }
92
+ }
93
+ },
94
+ {
95
+ "name": "float16_vec4_same_shape",
96
+ "inputs": {
97
+ "a": {
98
+ "dtype": "float16",
99
+ "shape": [8],
100
+ "data": { "kind": "values", "values": [1.0, -2.0, 3.5, -4.0, 0.25, 10.0, -100.0, 0.001] }
101
+ },
102
+ "b": {
103
+ "dtype": "float16",
104
+ "shape": [8],
105
+ "data": { "kind": "values", "values": [0.5, 2.0, -1.5, 4.0, 0.75, -5.0, 100.0, -0.001] }
106
+ }
107
+ },
108
+ "outputs": {
109
+ "y": {
110
+ "dtype": "float16",
111
+ "shape": [8],
112
+ "tolerance": 0.001,
113
+ "data": { "kind": "values", "values": [1.5, 0.0, 2.0, 0.0, 1.0, 5.0, 0.0, 0.0] }
114
+ }
115
+ }
116
+ },
117
+ {
118
+ "name": "same_shape_vec4_three_input",
119
+ "inputs": {
120
+ "a": {
121
+ "dtype": "float32",
122
+ "shape": [8],
123
+ "data": { "kind": "values", "values": [1.0, 5.0, -2.0, 4.0, 0.0, 6.0, 10.0, -10.0] }
124
+ },
125
+ "b": {
126
+ "dtype": "float32",
127
+ "shape": [8],
128
+ "data": { "kind": "values", "values": [3.0, 2.0, -4.0, 8.0, 1.0, 1.0, 9.0, -9.0] }
129
+ },
130
+ "c": {
131
+ "dtype": "float32",
132
+ "shape": [8],
133
+ "data": { "kind": "values", "values": [0.0, 7.0, -3.0, 2.0, -1.0, 8.0, 11.0, -11.0] }
134
+ }
135
+ },
136
+ "outputs": {
137
+ "y": {
138
+ "dtype": "float32",
139
+ "shape": [8],
140
+ "tolerance": 0.000001,
141
+ "data": { "kind": "values", "values": [4.0, 14.0, -9.0, 14.0, 0.0, 15.0, 30.0, -30.0] }
142
+ }
143
+ }
144
+ },
145
+ {
146
+ "name": "float16_vec4_three_input",
147
+ "provenance": {
148
+ "notes": "The float16 three-input vec4 path widens each operand to f32 before the extra-operand fold. Every operand and partial sum is an exactly representable dyadic float16 value, so dropping or double-counting C changes an exact zero-tolerance result."
149
+ },
150
+ "inputs": {
151
+ "a": {
152
+ "dtype": "float16",
153
+ "shape": [8],
154
+ "data": { "kind": "values", "values": [1.0, -2.0, 0.5, 16.0, -0.25, 6.0, -32.0, 0.125] }
155
+ },
156
+ "b": {
157
+ "dtype": "float16",
158
+ "shape": [8],
159
+ "data": { "kind": "values", "values": [2.0, -1.5, -1.0, 8.0, 0.125, 3.0, -8.0, 0.375] }
160
+ },
161
+ "c": {
162
+ "dtype": "float16",
163
+ "shape": [8],
164
+ "data": { "kind": "values", "values": [0.5, -4.0, 3.0, -4.0, -0.5, 12.0, 24.0, 0.5] }
165
+ }
166
+ },
167
+ "outputs": {
168
+ "y": {
169
+ "dtype": "float16",
170
+ "shape": [8],
171
+ "tolerance": 0,
172
+ "data": { "kind": "values", "values": [3.5, -7.5, 2.5, 20.0, -0.625, 21.0, -16.0, 1.0] }
173
+ }
174
+ }
175
+ },
176
+ {
177
+ "name": "broadcast_rank4",
178
+ "inputs": {
179
+ "a": {
180
+ "dtype": "float32",
181
+ "shape": [2, 3, 4, 5],
182
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29 }
183
+ },
184
+ "b": {
185
+ "dtype": "float32",
186
+ "shape": [1, 3, 1, 5],
187
+ "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.07 }
188
+ }
189
+ },
190
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 4, 5], "tolerance": 0.000001 } }
191
+ },
192
+ {
193
+ "name": "rank0_rhs_scalar_broadcast",
194
+ "inputs": {
195
+ "a": {
196
+ "dtype": "float32",
197
+ "shape": [2, 3],
198
+ "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
199
+ },
200
+ "b": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.5] } }
201
+ },
202
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3], "tolerance": 0.000001 } }
203
+ },
204
+ {
205
+ "name": "ort_two_inputs_no_broadcasting_3d",
206
+ "provenance": {
207
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
208
+ "test": "MathOpTest.SumMultipleInputsNoBroadcasting(num_inputs=2)"
209
+ },
210
+ "inputs": {
211
+ "a": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
212
+ "b": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } }
213
+ },
214
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 3, 3], "tolerance": 0.000001 } }
215
+ },
216
+ {
217
+ "name": "ort_four_inputs_no_broadcasting_3d_variadic",
218
+ "provenance": {
219
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
220
+ "test": "MathOpTest.SumMultipleInputsNoBroadcasting(num_inputs=4)",
221
+ "notes": "ONNX Sum is variadic; this catches implementations capped at three inputs."
222
+ },
223
+ "inputs": {
224
+ "a": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
225
+ "b": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
226
+ "c": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
227
+ "d": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } }
228
+ },
229
+ "outputs": {
230
+ "y": { "dtype": "float32", "shape": [3, 3, 3], "tolerance": 0, "data": { "kind": "constant", "value": 4.0 } }
231
+ }
232
+ },
233
+ {
234
+ "name": "ort_four_inputs_nan_infinity_variadic",
235
+ "provenance": {
236
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
237
+ "test": "MathOpTest.SumMultipleInputsNoBroadcasting(num_inputs=4)",
238
+ "notes": "Extends ORT's four-input Sum coverage with NaN propagation and inf-minus-inf cancellation."
239
+ },
240
+ "inputs": {
241
+ "a": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [1.0, "Infinity", 1.0, 8.0] } },
242
+ "b": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [3.0, "-Infinity", 5.0, 4.0] } },
243
+ "c": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [5.0, 2.0, "NaN", -4.0] } },
244
+ "d": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [7.0, 6.0, 9.0, "NaN"] } }
245
+ },
246
+ "outputs": {
247
+ "y": {
248
+ "dtype": "float32",
249
+ "shape": [4],
250
+ "tolerance": 0,
251
+ "allowNaN": true,
252
+ "data": { "kind": "values", "values": [16.0, "NaN", "NaN", "NaN"] }
253
+ }
254
+ }
255
+ },
256
+ {
257
+ "name": "ort_fifteen_inputs_no_broadcasting_3d_variadic_gpu_gap",
258
+ "skipGpu": {
259
+ "category": "todo",
260
+ "reason": "A single-pass implementation needs 15 input bindings plus one output binding, which exceeds WebGPU's guaranteed storage-buffer limit. A multi-pass in-place accumulator can implement this portable high-arity route but is not yet available."
261
+ },
262
+ "provenance": {
263
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
264
+ "test": "MathOpTest.SumMultipleInputsNoBroadcasting(num_inputs=15)",
265
+ "notes": "ORT explicitly covers high-arity no-broadcast Sum paths; this catches implementations capped at a small fixed arity."
266
+ },
267
+ "inputs": {
268
+ "a": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
269
+ "b": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
270
+ "c": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
271
+ "d": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
272
+ "e": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
273
+ "f": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
274
+ "g": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
275
+ "h": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
276
+ "i": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
277
+ "j": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
278
+ "k": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
279
+ "l": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
280
+ "m": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
281
+ "n": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } },
282
+ "o": { "dtype": "float32", "shape": [3, 3, 3], "data": { "kind": "constant", "value": 1.0 } }
283
+ },
284
+ "outputs": {
285
+ "y": { "dtype": "float32", "shape": [3, 3, 3], "tolerance": 0, "data": { "kind": "constant", "value": 15.0 } }
286
+ }
287
+ },
288
+ {
289
+ "name": "ort_opset6_two_input_same_shape_projection",
290
+ "provenance": {
291
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
292
+ "test": "MathOpTest.Sum_6",
293
+ "notes": "Two-input projection of ORT's same-shape three-input case."
294
+ },
295
+ "inputs": {
296
+ "a": {
297
+ "dtype": "float32",
298
+ "shape": [3, 3],
299
+ "data": { "kind": "values", "values": [1.0, 0.0, 1.0, -1.0, 1.1, -100.0, -5.4, 0.01, -10000.0] }
300
+ },
301
+ "b": {
302
+ "dtype": "float32",
303
+ "shape": [3, 3],
304
+ "data": { "kind": "values", "values": [1.0, 0.0, 2.0, -2.0, 2.2, 64.0, -1.0, 0.02, 0.25] }
305
+ }
306
+ },
307
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 3], "tolerance": 0.000001 } }
308
+ },
309
+ {
310
+ "name": "ort_opset6_three_input_same_shape_exact",
311
+ "provenance": {
312
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
313
+ "test": "MathOpTest.Sum_6"
314
+ },
315
+ "inputs": {
316
+ "a": {
317
+ "dtype": "float32",
318
+ "shape": [3, 3],
319
+ "data": { "kind": "values", "values": [1.0, 0.0, 1.0, -1.0, 1.1, -100.0, -5.4, 0.01, -10000.0] }
320
+ },
321
+ "b": {
322
+ "dtype": "float32",
323
+ "shape": [3, 3],
324
+ "data": { "kind": "values", "values": [1.0, 0.0, 2.0, -2.0, 2.2, 64.0, -1.0, 0.02, 0.25] }
325
+ },
326
+ "c": {
327
+ "dtype": "float32",
328
+ "shape": [3, 3],
329
+ "data": { "kind": "values", "values": [1.0, 0.0, 3.0, -3.0, 3.3, 64.0, 5.4, 0.03, 10000.0] }
330
+ }
331
+ },
332
+ "outputs": {
333
+ "y": {
334
+ "dtype": "float32",
335
+ "shape": [3, 3],
336
+ "tolerance": 0.000001,
337
+ "data": { "kind": "values", "values": [3.0, 0.0, 6.0, -6.0, 6.6, 28.0, -1.0, 0.06, 0.25] }
338
+ }
339
+ }
340
+ },
341
+ {
342
+ "name": "ort_opset8_vector_column_broadcast_projection",
343
+ "provenance": {
344
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
345
+ "test": "MathOpTest.Sum_8_Test1",
346
+ "notes": "Two-input projection exercising vector plus column multidirectional broadcasting."
347
+ },
348
+ "inputs": {
349
+ "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } },
350
+ "b": { "dtype": "float32", "shape": [3, 1], "data": { "kind": "values", "values": [10.0, 20.0, 30.0] } }
351
+ },
352
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 3], "tolerance": 0.000001 } }
353
+ },
354
+ {
355
+ "name": "ort_opset8_three_input_vector_column_depth_broadcast_exact",
356
+ "provenance": {
357
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
358
+ "test": "MathOpTest.Sum_8_Test1"
359
+ },
360
+ "inputs": {
361
+ "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } },
362
+ "b": { "dtype": "float32", "shape": [3, 1], "data": { "kind": "values", "values": [10.0, 20.0, 30.0] } },
363
+ "c": { "dtype": "float32", "shape": [3, 1, 1], "data": { "kind": "values", "values": [100.0, 200.0, 300.0] } }
364
+ },
365
+ "outputs": {
366
+ "y": {
367
+ "dtype": "float32",
368
+ "shape": [3, 3, 3],
369
+ "tolerance": 0,
370
+ "data": {
371
+ "kind": "values",
372
+ "values": [111.0, 112.0, 113.0, 121.0, 122.0, 123.0, 131.0, 132.0, 133.0, 211.0, 212.0, 213.0, 221.0, 222.0, 223.0, 231.0, 232.0, 233.0, 311.0, 312.0, 313.0, 321.0, 322.0, 323.0, 331.0, 332.0, 333.0]
373
+ }
374
+ }
375
+ }
376
+ },
377
+ {
378
+ "name": "ort_opset8_four_input_vector_column_depth_broadcast",
379
+ "provenance": {
380
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
381
+ "test": "MathOpTest.Sum_8_Test1",
382
+ "notes": "Extends ORT's multidirectional broadcast case to a valid four-input ONNX variadic Sum node."
383
+ },
384
+ "inputs": {
385
+ "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } },
386
+ "b": { "dtype": "float32", "shape": [3, 1], "data": { "kind": "values", "values": [10.0, 20.0, 30.0] } },
387
+ "c": { "dtype": "float32", "shape": [3, 1, 1], "data": { "kind": "values", "values": [100.0, 200.0, 300.0] } },
388
+ "d": {
389
+ "dtype": "float32",
390
+ "shape": [1, 1, 3],
391
+ "data": { "kind": "values", "values": [1000.0, 2000.0, 3000.0] }
392
+ }
393
+ },
394
+ "outputs": {
395
+ "y": {
396
+ "dtype": "float32",
397
+ "shape": [3, 3, 3],
398
+ "tolerance": 0,
399
+ "data": {
400
+ "kind": "values",
401
+ "values": [1111.0, 2112.0, 3113.0, 1121.0, 2122.0, 3123.0, 1131.0, 2132.0, 3133.0, 1211.0, 2212.0, 3213.0, 1221.0, 2222.0, 3223.0, 1231.0, 2232.0, 3233.0, 1311.0, 2312.0, 3313.0, 1321.0, 2322.0, 3323.0, 1331.0, 2332.0, 3333.0]
402
+ }
403
+ }
404
+ }
405
+ },
406
+ {
407
+ "name": "ort_opset8_matrix_vector_broadcast_projection",
408
+ "provenance": {
409
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
410
+ "test": "MathOpTest.Sum_8_Test2",
411
+ "notes": "Two-input projection of ORT's matrix plus trailing-vector broadcast case."
412
+ },
413
+ "inputs": {
414
+ "a": {
415
+ "dtype": "float32",
416
+ "shape": [3, 3],
417
+ "data": { "kind": "values", "values": [1.0, 0.0, 1.0, -1.0, 1.1, -100.0, -5.4, 0.01, -74.0] }
418
+ },
419
+ "b": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 0.0, 2.0] } }
420
+ },
421
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 3], "tolerance": 0.000001 } }
422
+ },
423
+ {
424
+ "name": "onnx_backend_sum_two_inputs",
425
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_sum_two_inputs" },
426
+ "inputs": {
427
+ "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [3.0, 0.0, 2.0] } },
428
+ "b": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 3.0, 4.0] } }
429
+ },
430
+ "outputs": { "y": { "dtype": "float32", "shape": [3], "tolerance": 0 } }
431
+ },
432
+ {
433
+ "name": "onnx_backend_sum_one_input_identity",
434
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_sum_one_input" },
435
+ "inputs": { "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [3.0, 0.0, 2.0] } } },
436
+ "outputs": { "y": { "dtype": "float32", "shape": [3], "tolerance": 0 } }
437
+ },
438
+ {
439
+ "name": "onnx_backend_sum_example_three_inputs",
440
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_sum_example" },
441
+ "inputs": {
442
+ "a": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [3.0, 0.0, 2.0] } },
443
+ "b": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 3.0, 4.0] } },
444
+ "c": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [2.0, 6.0, 6.0] } }
445
+ },
446
+ "outputs": { "y": { "dtype": "float32", "shape": [3], "tolerance": 0 } }
447
+ },
448
+ {
449
+ "name": "ort_dim_zero_equal_rank",
450
+ "provenance": {
451
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
452
+ "test": "MathOpTest.DimWithZeroHandling",
453
+ "notes": "Projected from ORT's binary elementwise zero-dimension Add coverage to generic ONNX multidirectional broadcasting."
454
+ },
455
+ "inputs": {
456
+ "a": { "dtype": "float32", "shape": [3, 1], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } },
457
+ "b": { "dtype": "float32", "shape": [3, 0], "data": { "kind": "values", "values": [] } }
458
+ },
459
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 0], "tolerance": 0 } }
460
+ },
461
+ {
462
+ "name": "ort_dim_zero_scalar_broadcast",
463
+ "provenance": {
464
+ "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
465
+ "test": "MathOpTest.DimWithZeroHandling",
466
+ "notes": "Projected from ORT's binary elementwise zero-dimension Add coverage to generic ONNX multidirectional broadcasting."
467
+ },
468
+ "inputs": {
469
+ "a": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } },
470
+ "b": { "dtype": "float32", "shape": [0], "data": { "kind": "values", "values": [] } }
471
+ },
472
+ "outputs": { "y": { "dtype": "float32", "shape": [0], "tolerance": 0 } }
473
+ },
474
+ {
475
+ "name": "single_input_empty_shape",
476
+ "inputs": { "a": { "dtype": "float32", "shape": [0], "data": { "kind": "values", "values": [] } } },
477
+ "outputs": { "y": { "dtype": "float32", "shape": [0], "tolerance": 0 } }
478
+ },
479
+ {
480
+ "name": "f16_broadcast_differing_shapes",
481
+ "inputs": {
482
+ "a": { "dtype": "float16", "shape": [1, 4], "data": { "kind": "values", "values": [1.0, 2.0, 4.0, 8.0] } },
483
+ "b": {
484
+ "dtype": "float16",
485
+ "shape": [4, 4],
486
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.41 }
487
+ }
488
+ },
489
+ "outputs": { "y": { "dtype": "float16", "shape": [4, 4], "tolerance": 0.001 } }
490
+ },
491
+ {
492
+ "name": "rank7_broadcast_two_input",
493
+ "inputs": {
494
+ "a": {
495
+ "dtype": "float32",
496
+ "shape": [1, 2, 1, 2, 1, 2, 3],
497
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
498
+ },
499
+ "b": {
500
+ "dtype": "float32",
501
+ "shape": [2, 1, 2, 1, 2, 1, 3],
502
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.19 }
503
+ }
504
+ },
505
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2, 2, 2, 2, 3], "tolerance": 0.000001 } }
506
+ },
507
+ {
508
+ "name": "three_input_empty_dim_broadcast",
509
+ "inputs": {
510
+ "a": { "dtype": "float32", "shape": [2, 1], "data": { "kind": "values", "values": [1.0, 2.0] } },
511
+ "b": { "dtype": "float32", "shape": [2, 0], "data": { "kind": "values", "values": [] } },
512
+ "c": { "dtype": "float32", "shape": [1, 0], "data": { "kind": "values", "values": [] } }
513
+ },
514
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 0], "tolerance": 0 } }
515
+ },
516
+ {
517
+ "name": "four_input_mixed_broadcast_vec4",
518
+ "provenance": {
519
+ "notes": "Route lock for mixed four-input vec4 broadcasting: A and C retain the eight-wide innermost dimension, while B broadcasts channel scalars and D broadcasts one scalar across every output lane."
520
+ },
521
+ "inputs": {
522
+ "a": {
523
+ "dtype": "float32",
524
+ "shape": [2, 3, 2, 8],
525
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.25 }
526
+ },
527
+ "b": { "dtype": "float32", "shape": [1, 3, 1, 1], "data": { "kind": "values", "values": [1.0, 2.0, 4.0] } },
528
+ "c": {
529
+ "dtype": "float32",
530
+ "shape": [2, 1, 2, 8],
531
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.29, "scale": 0.125 }
532
+ },
533
+ "d": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [0.5] } }
534
+ },
535
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 2, 8], "tolerance": 0.000001, "relTolerance": 0.000001 } }
536
+ },
537
+ {
538
+ "name": "three_input_mixed_broadcast_vec4_candidate",
539
+ "provenance": {
540
+ "notes": "Three-input mixed-broadcast lock: A is dense, B varies over channel and the penultimate axis but broadcasts its innermost scalar, and C retains vector lanes while broadcasting both middle axes."
541
+ },
542
+ "inputs": {
543
+ "a": {
544
+ "dtype": "float32",
545
+ "shape": [2, 3, 2, 8],
546
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.25 }
547
+ },
548
+ "b": {
549
+ "dtype": "float32",
550
+ "shape": [1, 3, 2, 1],
551
+ "data": { "kind": "values", "values": [1.0, 2.0, 4.0, 8.0, 16.0, 32.0] }
552
+ },
553
+ "c": {
554
+ "dtype": "float32",
555
+ "shape": [2, 1, 1, 8],
556
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.29, "scale": 0.125 }
557
+ }
558
+ },
559
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 2, 8], "tolerance": 0.000001, "relTolerance": 0.000001 } }
560
+ },
561
+ {
562
+ "name": "rank8_broadcast_two_input",
563
+ "inputs": {
564
+ "a": {
565
+ "dtype": "float32",
566
+ "shape": [1, 2, 1, 2, 1, 2, 2, 3],
567
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
568
+ },
569
+ "b": {
570
+ "dtype": "float32",
571
+ "shape": [2, 1, 2, 1, 2, 1, 2, 3],
572
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.19 }
573
+ }
574
+ },
575
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2, 2, 2, 2, 2, 3], "tolerance": 0.000001 } }
576
+ }
577
+ ]
578
+ }