Xenova HF Staff commited on
Commit
2daadc3
·
verified ·
1 Parent(s): bbc2d6e

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,17 +18,17 @@ See the [ONNX `OneHot` spec](https://onnx.ai/onnx/operators/onnx__OneHot.html) f
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
- | --- | --- | --- | --- | --- | --- | --- |
23
- | `indices` | `indices` | `I` | — | — | Integer or float index tensor; values outside `[-depth, depth-1]` produce all-`off_value` output rows. | required |
24
- | `depth` | `depth` | `D` | — | — | Scalar (or length-1 rank-1) tensor specifying the number of classes and the size of the one-hot dimension. | required |
25
- | `values` | `values` | `T` | `1` | — | Rank-1 tensor of exactly two elements `[off_value, on_value]` giving the values written to inactive and active positions respectively. | required |
26
 
27
  ## Outputs
28
 
29
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
30
- | --- | --- | --- | --- | --- | --- | --- |
31
- | `output` | `output` | `T` | derived | — | One-hot tensor with rank equal to `rank(indices) + 1`, same element type as `values`. | required |
32
 
33
  ## Attributes
34
 
@@ -48,7 +48,7 @@ Default values (overridable per request):
48
 
49
  ## Files
50
 
51
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
52
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
53
  - [`test.json`](build/webgpu/test.json) — correctness cases
54
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -58,15 +58,18 @@ Default values (overridable per request):
58
 
59
  ## Use with `@huggingface/kernels`
60
 
61
- The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
 
 
62
 
63
- The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
64
 
65
- - `output`
66
 
67
- Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
68
 
69
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
70
 
71
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
72
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | `indices` | `I` | — | — | Integer or float index tensor; values outside `[-depth, depth-1]` produce all-`off_value` output rows. | required |
24
+ | `depth` | `D` | — | — | Scalar (or length-1 rank-1) tensor specifying the number of classes and the size of the one-hot dimension. | required |
25
+ | `values` | `T` | `1` | — | Rank-1 tensor of exactly two elements `[off_value, on_value]` giving the values written to inactive and active positions respectively. | required |
26
 
27
  ## Outputs
28
 
29
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
30
+ | --- | --- | --- | --- | --- | --- |
31
+ | `output` | `T` | derived | — | One-hot tensor with rank equal to `rank(indices) + 1`, same element type as `values`. | required |
32
 
33
  ## Attributes
34
 
 
48
 
49
  ## Files
50
 
51
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
52
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
53
  - [`test.json`](build/webgpu/test.json) — correctness cases
54
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
58
 
59
  ## Use with `@huggingface/kernels`
60
 
61
+ ```sh
62
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
63
+ ```
64
 
65
+ Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
66
 
67
+ This example supplies explicit metadata for:
68
 
69
+ - `output`
70
 
71
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
72
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
73
 
74
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
75
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.OneHot",
3
  "cases": [
4
  {
5
  "name": "tokens_vocab",
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "tokens_vocab",
build/webgpu/manifest.json CHANGED
@@ -2,65 +2,32 @@
2
  "domain": "ai.onnx",
3
  "name": "OneHot",
4
  "sinceVersion": 11,
5
- "description": "Produces a one-hot tensor from an `indices` input: positions matching each index are filled with `on_value` and all other positions with `off_value`, where both are taken from the two-element `values` tensor `[off_value, on_value]`. The output rank is one greater than `indices`, with the new dimension of size `depth` inserted at the position given by `axis`; indices outside `[-depth, depth-1]` yield all-`off_value` rows.",
6
- "inputs": [
7
- {
8
- "role": "indices",
9
- "dtype": "I",
10
- "description": "Integer or float index tensor; values outside `[-depth, depth-1]` produce all-`off_value` output rows."
11
- },
12
- {
13
- "role": "depth",
14
- "dtype": "D",
15
- "description": "Scalar (or length-1 rank-1) tensor specifying the number of classes and the size of the one-hot dimension."
16
- },
17
- {
18
- "role": "values",
19
- "dtype": "T",
20
- "description": "Rank-1 tensor of exactly two elements `[off_value, on_value]` giving the values written to inactive and active positions respectively.",
21
- "rank": 1
22
- }
23
- ],
24
- "outputs": [
25
- {
26
- "role": "output",
27
- "dtype": "T",
28
- "description": "One-hot tensor with rank equal to `rank(indices) + 1`, same element type as `values`.",
29
- "rank": "ranks.indices + 1"
30
- }
31
- ],
32
- "attributes": { "axis": -1 },
33
- "attributeDescriptions": {
34
- "axis": "Axis along which the one-hot dimension is inserted; default `-1` appends it as the last dimension. Negative values count from the back; accepted range is `[-r-1, r]` where `r = rank(indices)`."
35
- },
36
  "typeConstraints": {
37
  "I": ["float32", "float16", "int32", "int16", "int8", "uint32", "uint8"],
38
  "D": ["float32", "float16", "int32", "int16", "int8", "uint32", "uint8"],
39
  "T": ["float32", "float16", "int32", "int16", "int8", "uint32", "uint8", "bool"]
40
  },
41
- "args": {
42
- "indices": { "kind": "tensor", "semantic": "indices", "role": "indices" },
43
- "depth": { "kind": "tensor", "semantic": "depth", "role": "depth" },
44
- "values": { "kind": "tensor", "semantic": "values", "role": "values" },
45
- "output": { "kind": "tensor", "semantic": "output", "role": "output" }
46
- },
47
- "tunables": { "WORKGROUP_SIZE": 256 },
48
  "derive": {
49
  "oneHotContractOk": "(dtypes.I == \"u32\" or dtypes.I == \"i32\" or dtypes.I == \"f32\" or dtypes.I == \"f16\") and ranks.indices >= 1 and ranks.output == ranks.indices + 1 and (ranks.depth == 0 or (ranks.depth == 1 and dim(shapes.depth, 0) == 1)) and ranks.values == 1 and dim(shapes.values, 0) == 2 and numel(shapes.output) >= numel(shapes.indices) and f16Ok(dtypes.T) and f16Ok(dtypes.I)",
50
  "oneHotLastAxisCovered": "oneHotContractOk and (attrs.axis == ranks.output - 1 or attrs.axis == -1) and dim(shapes.output, -1) % 4 == 0",
51
  "oneHotLastAxisVecCount": "dim(shapes.output, -1) / 4",
52
  "oneHotRowWorkgroup": "min(tunables.WORKGROUP_SIZE, device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX, max(1, pow2ceil(oneHotLastAxisVecCount)))",
53
  "oneHotCooperativeMinVecs": "device.adapterInfo.subgroupMinSize if has(device.adapterInfo, \"subgroupMinSize\") else 32",
54
- "oneHotRowCooperative": "oneHotLastAxisCovered and oneHotLastAxisVecCount >= oneHotCooperativeMinVecs"
55
- },
56
- "constants": {
57
  "scalar": "dtypes.T",
58
  "indexScalar": "dtypes.I",
59
  "usesI32": "dtypes.I == \"i32\"",
60
  "floatIndices": "dtypes.I == \"f32\" or dtypes.I == \"f16\"",
61
- "usesF16": "dtypes.T == \"f16\" or dtypes.I == \"f16\"",
62
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\""
63
  },
 
 
 
 
64
  "variants": [
65
  {
66
  "id": "last_axis_row_vec4",
@@ -70,40 +37,15 @@
70
  {
71
  "id": "main",
72
  "name": "OneHot.last_axis_row_vec4",
73
- "source": {
74
- "shader": "one-hot-last-axis-vec4.wgsl.jinja",
75
- "inputs": {
76
- "depth": "dim(shapes.output, -1)",
77
- "rows": "numel(shapes.indices)",
78
- "wg": "oneHotRowWorkgroup",
79
- "cooperative": true
80
- }
81
  },
82
- "bindings": [
83
- {
84
- "name": "indices",
85
- "arg": "indices",
86
- "semantic": "indices",
87
- "buffer": { "type": "read-only-storage" },
88
- "elementType": "$indexScalar"
89
- },
90
- {
91
- "name": "values",
92
- "arg": "values",
93
- "semantic": "values",
94
- "buffer": { "type": "read-only-storage" },
95
- "elementType": "$scalar",
96
- "length": 2
97
- },
98
- {
99
- "name": "output",
100
- "arg": "output",
101
- "semantic": "output",
102
- "buffer": { "type": "storage" },
103
- "elementType": "$vectorScalar"
104
- }
105
- ],
106
- "dispatch": { "workgroups": "numel(shapes.indices)" }
107
  }
108
  ]
109
  },
@@ -115,44 +57,19 @@
115
  {
116
  "id": "main",
117
  "name": "OneHot.last_axis_vec4",
118
- "source": {
119
- "shader": "one-hot-last-axis-vec4.wgsl.jinja",
120
- "inputs": { "depth": "dim(shapes.output, -1)", "cooperative": false }
121
- },
122
  "bindings": [
123
- {
124
- "name": "indices",
125
- "arg": "indices",
126
- "semantic": "indices",
127
- "buffer": { "type": "read-only-storage" },
128
- "elementType": "$indexScalar"
129
- },
130
- {
131
- "name": "values",
132
- "arg": "values",
133
- "semantic": "values",
134
- "buffer": { "type": "read-only-storage" },
135
- "elementType": "$scalar",
136
- "length": 2
137
- },
138
- {
139
- "name": "output",
140
- "arg": "output",
141
- "semantic": "output",
142
- "buffer": { "type": "storage" },
143
- "elementType": "$vectorScalar"
144
- },
145
- {
146
- "name": "params",
147
- "semantic": "kernel.params",
148
- "buffer": { "type": "uniform" },
149
- "struct": {
150
- "name": "Params",
151
- "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }]
152
- }
153
- }
154
  ],
155
- "dispatch": { "threads": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
156
  }
157
  ]
158
  },
@@ -169,108 +86,57 @@
169
  {
170
  "id": "fill_vec4",
171
  "name": "OneHot.FillVec4",
172
- "source": { "shader": "one-hot-fill.wgsl.jinja", "inputs": { "vectorized": true } },
 
173
  "bindings": [
174
- {
175
- "name": "values",
176
- "arg": "values",
177
- "semantic": "values",
178
- "buffer": { "type": "read-only-storage" },
179
- "elementType": "$scalar",
180
- "length": 2
181
- },
182
- {
183
- "name": "output",
184
- "arg": "output",
185
- "semantic": "output",
186
- "buffer": { "type": "storage" },
187
- "elementType": "$vectorScalar"
188
- },
189
- {
190
- "name": "params",
191
- "semantic": "kernel.params",
192
- "buffer": { "type": "uniform" },
193
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "outputVecCount" }] }
194
- }
195
  ],
196
- "dispatch": { "threads": "outputVecCount", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
197
  },
198
  {
199
  "id": "fill_tail",
200
  "name": "OneHot.FillTail",
201
- "source": { "shader": "one-hot-fill.wgsl.jinja", "inputs": { "vectorized": false } },
 
202
  "bindings": [
203
- {
204
- "name": "values",
205
- "arg": "values",
206
- "semantic": "values",
207
- "buffer": { "type": "read-only-storage" },
208
- "elementType": "$scalar",
209
- "length": 2
210
- },
211
- {
212
- "name": "output",
213
- "arg": "output",
214
- "semantic": "output",
215
- "buffer": { "type": "storage" },
216
- "elementType": "$scalar"
217
- },
218
  {
219
  "name": "params",
220
- "semantic": "kernel.params",
221
- "buffer": { "type": "uniform" },
222
- "struct": {
223
- "name": "Params",
224
- "fields": [
225
- { "name": "count", "type": "u32", "value": "outputTailCount" },
226
- { "name": "offset", "type": "u32", "value": "outputVecCount * 4" }
227
- ]
228
- }
229
  }
230
  ],
231
- "dispatch": { "threads": "outputTailCount", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
232
  },
233
  {
234
  "id": "scatter",
235
  "name": "OneHot.Scatter",
236
- "source": {
237
- "shader": "one-hot-scatter.wgsl.jinja",
238
- "inputs": { "depth": "dim(shapes.output, depthAxis)", "inner": "depthInner" }
239
- },
240
  "bindings": [
241
- {
242
- "name": "indices",
243
- "arg": "indices",
244
- "semantic": "indices",
245
- "buffer": { "type": "read-only-storage" },
246
- "elementType": "$indexScalar"
247
- },
248
- {
249
- "name": "values",
250
- "arg": "values",
251
- "semantic": "values",
252
- "buffer": { "type": "read-only-storage" },
253
- "elementType": "$scalar",
254
- "length": 2
255
- },
256
- {
257
- "name": "output",
258
- "arg": "output",
259
- "semantic": "output",
260
- "buffer": { "type": "storage" },
261
- "elementType": "$scalar"
262
- },
263
- {
264
- "name": "params",
265
- "semantic": "kernel.params",
266
- "buffer": { "type": "uniform" },
267
- "struct": {
268
- "name": "Params",
269
- "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.indices)" }]
270
- }
271
- }
272
  ],
273
- "dispatch": { "threads": "numel(shapes.indices)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
274
  }
275
  ]
276
  }
 
2
  "domain": "ai.onnx",
3
  "name": "OneHot",
4
  "sinceVersion": 11,
5
+ "inputs": { "indices": { "dtype": "I" }, "depth": { "dtype": "D" }, "values": { "dtype": "T", "rank": 1 } },
6
+ "outputs": { "output": { "dtype": "T", "rank": "ranks.indices + 1" } },
7
+ "attributes": { "axis": { "default": -1 } },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  "typeConstraints": {
9
  "I": ["float32", "float16", "int32", "int16", "int8", "uint32", "uint8"],
10
  "D": ["float32", "float16", "int32", "int16", "int8", "uint32", "uint8"],
11
  "T": ["float32", "float16", "int32", "int16", "int8", "uint32", "uint8", "bool"]
12
  },
13
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
 
 
 
 
 
 
14
  "derive": {
15
  "oneHotContractOk": "(dtypes.I == \"u32\" or dtypes.I == \"i32\" or dtypes.I == \"f32\" or dtypes.I == \"f16\") and ranks.indices >= 1 and ranks.output == ranks.indices + 1 and (ranks.depth == 0 or (ranks.depth == 1 and dim(shapes.depth, 0) == 1)) and ranks.values == 1 and dim(shapes.values, 0) == 2 and numel(shapes.output) >= numel(shapes.indices) and f16Ok(dtypes.T) and f16Ok(dtypes.I)",
16
  "oneHotLastAxisCovered": "oneHotContractOk and (attrs.axis == ranks.output - 1 or attrs.axis == -1) and dim(shapes.output, -1) % 4 == 0",
17
  "oneHotLastAxisVecCount": "dim(shapes.output, -1) / 4",
18
  "oneHotRowWorkgroup": "min(tunables.WORKGROUP_SIZE, device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX, max(1, pow2ceil(oneHotLastAxisVecCount)))",
19
  "oneHotCooperativeMinVecs": "device.adapterInfo.subgroupMinSize if has(device.adapterInfo, \"subgroupMinSize\") else 32",
20
+ "oneHotRowCooperative": "oneHotLastAxisCovered and oneHotLastAxisVecCount >= oneHotCooperativeMinVecs",
 
 
21
  "scalar": "dtypes.T",
22
  "indexScalar": "dtypes.I",
23
  "usesI32": "dtypes.I == \"i32\"",
24
  "floatIndices": "dtypes.I == \"f32\" or dtypes.I == \"f16\"",
 
25
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\""
26
  },
27
+ "bindings": {
28
+ "values": { "buffer": "read-only-storage", "elementType": "$scalar", "length": 2 },
29
+ "output": { "buffer": "storage", "elementType": "$vectorScalar" }
30
+ },
31
  "variants": [
32
  {
33
  "id": "last_axis_row_vec4",
 
37
  {
38
  "id": "main",
39
  "name": "OneHot.last_axis_row_vec4",
40
+ "shader": "one-hot-last-axis-vec4.wgsl.jinja",
41
+ "derive": {
42
+ "depth": "dim(shapes.output, -1)",
43
+ "rowCount": "numel(shapes.indices)",
44
+ "wg": "oneHotRowWorkgroup",
45
+ "cooperative": true
 
 
46
  },
47
+ "bindings": ["indices", "values", "output"],
48
+ "dispatch": { "x": "min(numel(shapes.indices), 65535)", "y": "ceilDiv(numel(shapes.indices), 65535)", "z": 1 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
  ]
51
  },
 
57
  {
58
  "id": "main",
59
  "name": "OneHot.last_axis_vec4",
60
+ "shader": "one-hot-last-axis-vec4.wgsl.jinja",
61
+ "derive": { "depth": "dim(shapes.output, -1)", "cooperative": false },
 
 
62
  "bindings": [
63
+ "indices",
64
+ "values",
65
+ "output",
66
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  ],
68
+ "dispatch": {
69
+ "x": "min(ceilDiv((numel(shapes.output) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
70
+ "y": "ceilDiv(ceilDiv((numel(shapes.output) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
71
+ "z": 1
72
+ }
73
  }
74
  ]
75
  },
 
86
  {
87
  "id": "fill_vec4",
88
  "name": "OneHot.FillVec4",
89
+ "shader": "one-hot-fill.wgsl.jinja",
90
+ "derive": { "vectorizedSpec": true },
91
  "bindings": [
92
+ "values",
93
+ "output",
94
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "outputVecCount" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  ],
96
+ "dispatch": {
97
+ "x": "min(ceilDiv((outputVecCount), (tunables.WORKGROUP_SIZE)), 65535)",
98
+ "y": "ceilDiv(ceilDiv((outputVecCount), (tunables.WORKGROUP_SIZE)), 65535)",
99
+ "z": 1
100
+ }
101
  },
102
  {
103
  "id": "fill_tail",
104
  "name": "OneHot.FillTail",
105
+ "shader": "one-hot-fill.wgsl.jinja",
106
+ "derive": { "vectorizedSpec": false },
107
  "bindings": [
108
+ "values",
109
+ { "arg": "output" },
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  {
111
  "name": "params",
112
+ "struct": [
113
+ { "name": "count", "type": "u32", "value": "outputTailCount" },
114
+ { "name": "offset", "type": "u32", "value": "outputVecCount * 4" }
115
+ ]
 
 
 
 
 
116
  }
117
  ],
118
+ "dispatch": {
119
+ "x": "min(ceilDiv((outputTailCount), (tunables.WORKGROUP_SIZE)), 65535)",
120
+ "y": "ceilDiv(ceilDiv((outputTailCount), (tunables.WORKGROUP_SIZE)), 65535)",
121
+ "z": 1
122
+ }
123
  },
124
  {
125
  "id": "scatter",
126
  "name": "OneHot.Scatter",
127
+ "shader": "one-hot-scatter.wgsl.jinja",
128
+ "derive": { "depth": "dim(shapes.output, depthAxis)", "innerSize": "depthInner" },
 
 
129
  "bindings": [
130
+ "indices",
131
+ "values",
132
+ { "arg": "output" },
133
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.indices)" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  ],
135
+ "dispatch": {
136
+ "x": "min(ceilDiv((numel(shapes.indices)), (tunables.WORKGROUP_SIZE)), 65535)",
137
+ "y": "ceilDiv(ceilDiv((numel(shapes.indices)), (tunables.WORKGROUP_SIZE)), 65535)",
138
+ "z": 1
139
+ }
140
  }
141
  ]
142
  }
build/webgpu/metadata.json CHANGED
@@ -1,20 +1,27 @@
1
  {
2
  "name": "ai.onnx.OneHot",
3
- "id": "_ai_onnx_onehot_webgpu_439ff74",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "tIK5K4R1/SOMH56dBhsH6mCf7IQ0DE5tsv4L5Q2UuEk=",
11
- "manifest.json": "BfEMlX6Fvw/2pFVCJ1L9FoZ2lBpbHjLoAr2PbX2GBZc=",
12
- "one-hot-fill.wgsl.jinja": "+1+3T4yVF91Ta11a5kEZHAC8D3C/uGzVKZm/CSK4Htc=",
13
- "one-hot-last-axis-vec4.wgsl.jinja": "nQ+vRZPx/rWi3Q+vuIhccDJzW6BATwWLFiaDf+HNwXo=",
14
- "one-hot-scatter.wgsl.jinja": "xXJjRoPmpglSeIQvxCv+9mmygMaGMw1LTKy82ViMVJ8=",
15
- "test.json": "UrZtlg9pSruj4lfpgeNOargkGctAr+ZUIM0fkeBJNsg="
16
  }
17
  },
18
- "provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
19
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.OneHot" }
 
 
 
 
 
 
 
20
  }
 
1
  {
2
  "name": "ai.onnx.OneHot",
3
+ "id": "_ai_onnx_onehot_webgpu_0f0f777",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "+FUw5FwoY0/2q5f1AHqGV4NyTYHeaoGS4hGUicbgYY4=",
11
+ "manifest.json": "yE4QIi2N3IwQ46XwkCOTuxL7t8DEXG7TX5L/updAYPA=",
12
+ "one-hot-fill.wgsl.jinja": "r55cBIB0D4pWDM63laO3cZbhz8Wox3J1FHZxHRplsM8=",
13
+ "one-hot-last-axis-vec4.wgsl.jinja": "kE19Rf4cbMJg+vJYsg3nI+T6wRd0pmF3t3Ah3xvo2mY=",
14
+ "one-hot-scatter.wgsl.jinja": "NEx5QGZ06RdFLM3T+T6L9jS2C0bkujL5niaeV9+HS38=",
15
+ "test.json": "nzqqm3CURddTStvPg1PZTCjgK+l1CyGWyK9EQwrXccU="
16
  }
17
  },
18
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
19
+ "webgpu": {
20
+ "manifestSpec": "2.0",
21
+ "variants": {
22
+ "last_axis_row_vec4": ["one-hot-last-axis-vec4.wgsl.jinja"],
23
+ "last_axis_vec4": ["one-hot-last-axis-vec4.wgsl.jinja"],
24
+ "generic_axis": ["one-hot-fill.wgsl.jinja", "one-hot-scatter.wgsl.jinja"]
25
+ }
26
+ }
27
  }
build/webgpu/one-hot-fill.wgsl.jinja CHANGED
@@ -1,48 +1,45 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
34
- {% if usesF16 %}
35
- enable f16;
36
- {% endif %}
37
  {{ env.wgsl.resourceDeclarations }}
38
 
39
  // Dense off-value fill for arbitrary-axis OneHot. The vector form writes four
40
  // elements per invocation and the scalar form handles the tail. A sparse pass
41
  // writes the on-values afterward.
42
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
43
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
44
  {{ flat_index_2d(note="") }}
45
- {% if source.vectorized %}
46
  output[i] = {{ vectorScalar }}(values[0]);
47
  {% else %}
48
  output[params.offset + i] = values[0];
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
 
 
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  // Dense off-value fill for arbitrary-axis OneHot. The vector form writes four
37
  // elements per invocation and the scalar form handles the tail. A sparse pass
38
  // writes the on-values afterward.
39
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
40
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
41
  {{ flat_index_2d(note="") }}
42
+ {% if vectorizedSpec %}
43
  output[i] = {{ vectorScalar }}(values[0]);
44
  {% else %}
45
  output[params.offset + i] = values[0];
build/webgpu/one-hot-last-axis-vec4.wgsl.jinja CHANGED
@@ -1,45 +1,42 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
34
- {% if usesF16 %}
35
- enable f16;
36
- {% endif %}
37
  {{ env.wgsl.resourceDeclarations }}
38
 
39
  // Vec4 OneHot for a contiguous depth-last output. Wide rows are assigned one
40
  // cooperative workgroup so they share the index load and avoid flat division;
41
  // short rows use the dense flat launch to avoid underfilled workgroups.
42
- const DEPTH: u32 = {{ source.depth }}u;
43
  const VEC_DEPTH: u32 = DEPTH / 4u;
44
 
45
  struct NormalizedIndex {
@@ -62,20 +59,19 @@ fn normalize_index(raw: {{ indexScalar }}) -> NormalizedIndex {
62
  {% endif %}
63
  }
64
 
65
- {% if source.cooperative %}
66
- const ROWS: u32 = {{ source.rows }}u;
67
- const WG: u32 = {{ source.wg }}u;
68
 
69
  var<workgroup> row_index: {{ indexScalar }};
70
 
71
- @compute @workgroup_size({{ source.wg }})
72
  fn main(
73
  @builtin(local_invocation_id) lid3: vec3<u32>,
74
- @builtin(workgroup_id) wid: vec3<u32>,
75
- @builtin(num_workgroups) nwg: vec3<u32>
76
  ) {
77
  let lane = lid3.x;
78
- let row = wid.x + wid.y * nwg.x;
79
  if (row >= ROWS) {
80
  return;
81
  }
@@ -99,7 +95,7 @@ fn main(
99
  }
100
  {% else %}
101
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
102
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
103
  {{ flat_index_2d("i4", note="") }}
104
  let d4 = i4 % VEC_DEPTH;
105
  let d0 = d4 * 4u;
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
 
 
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  // Vec4 OneHot for a contiguous depth-last output. Wide rows are assigned one
37
  // cooperative workgroup so they share the index load and avoid flat division;
38
  // short rows use the dense flat launch to avoid underfilled workgroups.
39
+ const DEPTH: u32 = {{ depth }}u;
40
  const VEC_DEPTH: u32 = DEPTH / 4u;
41
 
42
  struct NormalizedIndex {
 
59
  {% endif %}
60
  }
61
 
62
+ {% if cooperative %}
63
+ const ROWS: u32 = {{ rowCount }}u;
64
+ const WG: u32 = {{ wg }}u;
65
 
66
  var<workgroup> row_index: {{ indexScalar }};
67
 
68
+ @compute @workgroup_size({{ wg }})
69
  fn main(
70
  @builtin(local_invocation_id) lid3: vec3<u32>,
71
+ @builtin(workgroup_id) wid: vec3<u32>
 
72
  ) {
73
  let lane = lid3.x;
74
+ let row = wid.x + wid.y * {{ DISPATCH_FOLD_WIDTH }}u;
75
  if (row >= ROWS) {
76
  return;
77
  }
 
95
  }
96
  {% else %}
97
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
98
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
99
  {{ flat_index_2d("i4", note="") }}
100
  let d4 = i4 % VEC_DEPTH;
101
  let d0 = d4 * 4u;
build/webgpu/one-hot-scatter.wgsl.jinja CHANGED
@@ -1,49 +1,46 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
34
- {% if usesF16 %}
35
- enable f16;
36
- {% endif %}
37
  {{ env.wgsl.resourceDeclarations }}
38
 
39
  // Sparse on-value scatter for an output laid out as [outer, depth, inner].
40
  // One thread owns one input index, so output locations are unique even when
41
  // several input elements select the same depth value.
42
- const DEPTH: u32 = {{ source.depth }}u;
43
- const INNER: u32 = {{ source.inner }}u;
44
 
45
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
46
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
47
  {{ flat_index_2d(note="") }}
48
  let raw = indices[i];
49
  {% if floatIndices %}
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
 
 
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  // Sparse on-value scatter for an output laid out as [outer, depth, inner].
37
  // One thread owns one input index, so output locations are unique even when
38
  // several input elements select the same depth value.
39
+ const DEPTH: u32 = {{ depth }}u;
40
+ const INNER: u32 = {{ innerSize }}u;
41
 
42
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
43
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
44
  {{ flat_index_2d(note="") }}
45
  let raw = indices[i];
46
  {% if floatIndices %}
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.OneHot",
3
  "cases": [
4
  {
5
  "name": "float16_indices_depth_and_values",
@@ -63,7 +62,7 @@
63
  {
64
  "name": "float32_indices_int16_depth_float32_values",
65
  "provenance": {
66
- "notes": "An integral float32 index route with independently typed int16 depth connects both standard type variables while the all-int16 sibling retains widened-storage coverage."
67
  },
68
  "attrs": { "axis": -1 },
69
  "inputs": {
@@ -76,7 +75,7 @@
76
  {
77
  "name": "float32_indices_int8_depth_float32_values",
78
  "provenance": {
79
- "notes": "An integral float32 index route with independently typed int8 depth connects both standard type variables while the all-int8 sibling retains widened-storage coverage."
80
  },
81
  "attrs": { "axis": 0 },
82
  "inputs": {
@@ -89,7 +88,7 @@
89
  {
90
  "name": "float32_indices_uint8_depth_float32_values",
91
  "provenance": {
92
- "notes": "An integral float32 index route with independently typed uint8 depth connects both standard type variables while the all-uint8 sibling retains widened-storage coverage."
93
  },
94
  "attrs": { "axis": 1 },
95
  "inputs": {
@@ -435,7 +434,7 @@
435
  "outputs": { "output": { "dtype": "float32", "shape": [3, 10] } },
436
  "provenance": {
437
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_negative_indices",
438
- "notes": "ONNX int64 metadata/index tensors use framework int32/uint32 slots where representable. Official backend depth is represented with framework uint32 metadata."
439
  }
440
  },
441
  {
@@ -449,7 +448,7 @@
449
  "outputs": { "output": { "dtype": "float32", "shape": [2, 10, 2] } },
450
  "provenance": {
451
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_with_axis",
452
- "notes": "Official backend fixture stores integer-looking indices/depth as floats; represented with framework integer metadata tensors. Official backend depth is represented with framework uint32 metadata."
453
  }
454
  },
455
  {
@@ -463,7 +462,7 @@
463
  "outputs": { "output": { "dtype": "float32", "shape": [2, 10, 2] } },
464
  "provenance": {
465
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_with_negative_axis",
466
- "notes": "Official backend fixture stores integer-looking indices/depth as floats; represented with framework integer metadata tensors. Official backend depth is represented with framework uint32 metadata."
467
  }
468
  },
469
  {
@@ -476,7 +475,7 @@
476
  "outputs": { "output": { "dtype": "int32", "shape": [3, 12] } },
477
  "provenance": {
478
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_without_axis",
479
- "notes": "ONNX int64 metadata/index tensors use framework int32/uint32 slots where representable. Official backend depth is represented with framework uint32 metadata."
480
  }
481
  },
482
  {
@@ -484,7 +483,7 @@
484
  "provenance": {
485
  "source": "onnxruntime/test/providers/cpu/tensor/onehot_op_test.cc",
486
  "test": "OneHotOpTest.DefaultAxis_int64_int32_float_NonZeroOffValue",
487
- "notes": "Uses framework int32 depth instead of ORT's float depth so WebGPU can treat depth as shape metadata."
488
  },
489
  "inputs": {
490
  "indices": { "dtype": "int32", "shape": [4], "data": { "kind": "values", "values": [0, 2, -1, 5] } },
@@ -666,7 +665,7 @@
666
  {
667
  "name": "f16_last_axis_vec4_depth8",
668
  "provenance": {
669
- "notes": "f16 OneHot with a depth divisible by 4 selects last_axis_vec4 with usesF16=true (a vec4<f16> 64-bit store). Every existing f16 case has depth 3 or 10 (not %4) and routes to the scalar generic_axis kernel, so the vec4<f16> store branch is entirely untested. A real f16 embedding/classification one-hot over a %4 vocab hits this path."
670
  },
671
  "attrs": { "axis": -1 },
672
  "inputs": {
@@ -702,7 +701,7 @@
702
  {
703
  "name": "float_indices_last_axis_vec4_depth8",
704
  "provenance": {
705
- "notes": "Float32 indices with a depth divisible by 4 select last_axis_vec4 with floatIndices=true (i32(trunc(raw)) + negative-wrap, then a vec4 store). Every existing float-index case has depth 10 (not %4) and routes to generic_axis, so the floatIndices branch of the vec4 kernel is untested. ONNX permits numeric indices (cast to int); a negative float index exercises the truncation+wrap."
706
  },
707
  "attrs": { "axis": -1 },
708
  "inputs": {
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "float16_indices_depth_and_values",
 
62
  {
63
  "name": "float32_indices_int16_depth_float32_values",
64
  "provenance": {
65
+ "notes": "Integral float32 indices with an independently typed int16 depth exercise both standard type variables and widened depth storage."
66
  },
67
  "attrs": { "axis": -1 },
68
  "inputs": {
 
75
  {
76
  "name": "float32_indices_int8_depth_float32_values",
77
  "provenance": {
78
+ "notes": "Integral float32 indices with an independently typed int8 depth exercise both standard type variables and widened depth storage."
79
  },
80
  "attrs": { "axis": 0 },
81
  "inputs": {
 
88
  {
89
  "name": "float32_indices_uint8_depth_float32_values",
90
  "provenance": {
91
+ "notes": "Integral float32 indices with an independently typed uint8 depth exercise both standard type variables and widened depth storage."
92
  },
93
  "attrs": { "axis": 1 },
94
  "inputs": {
 
434
  "outputs": { "output": { "dtype": "float32", "shape": [3, 10] } },
435
  "provenance": {
436
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_negative_indices",
437
+ "notes": "The integer metadata tensors exactly represent the source ONNX int64 values, with `depth` stored as uint32."
438
  }
439
  },
440
  {
 
448
  "outputs": { "output": { "dtype": "float32", "shape": [2, 10, 2] } },
449
  "provenance": {
450
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_with_axis",
451
+ "notes": "Integer-valued indices and depth are represented with integer metadata tensors, using uint32 for `depth`."
452
  }
453
  },
454
  {
 
462
  "outputs": { "output": { "dtype": "float32", "shape": [2, 10, 2] } },
463
  "provenance": {
464
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_with_negative_axis",
465
+ "notes": "Integer-valued indices and depth are represented with integer metadata tensors, using uint32 for `depth`."
466
  }
467
  },
468
  {
 
475
  "outputs": { "output": { "dtype": "int32", "shape": [3, 12] } },
476
  "provenance": {
477
  "source": "cmake/external/onnx/onnx/backend/test/data/node/test_onehot_without_axis",
478
+ "notes": "The integer metadata tensors exactly represent the source ONNX int64 values, with `depth` stored as uint32."
479
  }
480
  },
481
  {
 
483
  "provenance": {
484
  "source": "onnxruntime/test/providers/cpu/tensor/onehot_op_test.cc",
485
  "test": "OneHotOpTest.DefaultAxis_int64_int32_float_NonZeroOffValue",
486
+ "notes": "Represents the integral `depth` value as int32 shape metadata."
487
  },
488
  "inputs": {
489
  "indices": { "dtype": "int32", "shape": [4], "data": { "kind": "values", "values": [0, 2, -1, 5] } },
 
665
  {
666
  "name": "f16_last_axis_vec4_depth8",
667
  "provenance": {
668
+ "notes": "A float16 OneHot with depth 8 selects the vectorized last-axis path and stores four float16 values per output group. Indices target the first, interior, and final classes."
669
  },
670
  "attrs": { "axis": -1 },
671
  "inputs": {
 
701
  {
702
  "name": "float_indices_last_axis_vec4_depth8",
703
  "provenance": {
704
+ "notes": "Float32 indices with depth 8 select the vectorized last-axis path. Fractional indices are truncated before indexing, negative indices wrap from the end, and out-of-range indices leave an all-off row."
705
  },
706
  "attrs": { "axis": -1 },
707
  "inputs": {