Xenova HF Staff commited on
Commit
e152cdb
·
verified ·
1 Parent(s): 0fe2ba4

sync 2e7068faf55e

Browse files
README.md CHANGED
@@ -1,3 +1,62 @@
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.Clip
10
+
11
+ `ai.onnx` · standard ONNX operator · ONNX opset ≥ 13
12
+
13
+ ## Description
14
+
15
+ Limits each element of the input tensor to the interval `[min, max]`, equivalent to `Min(max, Max(input, min))`. Each bound is an optional scalar input tensor; an omitted bound defaults to the input dtype's lowest or highest numeric value. When `min` exceeds `max`, all elements are set to `max`.
16
+
17
+ See the [ONNX `Clip` spec](https://onnx.ai/onnx/operators/onnx__Clip.html) for the reference semantics.
18
+
19
+ ## Inputs
20
+
21
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- | --- |
23
+ | `input` | `input` | `T` | — | — | Input tensor whose elements are to be clipped. | required |
24
+ | `min` | `min` | `T` | `0` | `[]` | Scalar lower bound; elements below this value are replaced by it. | optional |
25
+ | `max` | `max` | `T` | `0` | `[]` | Scalar upper bound; elements above this value are replaced by it. | optional |
26
+
27
+ ## Outputs
28
+
29
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
30
+ | --- | --- | --- | --- | --- | --- | --- |
31
+ | `output` | `output` | `T` | same as `input` | same as `input` | Output tensor with each element clipped to the specified interval. | required |
32
+
33
+ ## Type constraints
34
+
35
+ | Variable | Allowed dtypes |
36
+ | --- | --- |
37
+ | `T` | `float32`, `float16`, `int32`, `uint32`, `int8`, `uint8` |
38
+
39
+ ## Files
40
+
41
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
42
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
43
+ - [`test.json`](build/webgpu/test.json) — correctness cases
44
+ - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
45
+ - [`unary-scalar.wgsl.jinja`](build/webgpu/unary-scalar.wgsl.jinja)
46
+ - [`unary-vec4.wgsl.jinja`](build/webgpu/unary-vec4.wgsl.jinja)
47
+
48
+ ## Use with `@huggingface/kernels`
49
+
50
+ The loader derives every required output's shape and logical dtype from the manifest contract and this call.
51
+ It then allocates the result tensors automatically.
52
+
53
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
54
+
55
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
56
+
57
+ ```js
58
+ import { getKernel } from "@huggingface/kernels";
59
+
60
+ const kernel = await getKernel("webgpu-kernels/ai.onnx.Clip", { version: 1 });
61
+ const { output } = await kernel({ input: { data: inputData, shape: [3] } });
62
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Clip",
3
+ "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
4
+ "cases": [
5
+ {
6
+ "name": "1m_f32",
7
+ "inputs": {
8
+ "input": { "dtype": "float32", "shape": [1048576] },
9
+ "min": { "dtype": "float32", "shape": [], "dist": "constant", "value": -1 },
10
+ "max": { "dtype": "float32", "shape": [], "dist": "constant", "value": 1 }
11
+ },
12
+ "outputs": { "output": { "dtype": "float32", "shape": [1048576] } }
13
+ },
14
+ {
15
+ "name": "clip-f32-8m",
16
+ "preset": "smoke",
17
+ "vars": { "dtype": "float32", "count": 8388608 },
18
+ "inputs": {
19
+ "input": { "shape": [8388608], "dtype": "float32", "seed": 7025, "dist": "normal", "scale": 2 },
20
+ "min": { "dtype": "float32", "shape": [], "dist": "constant", "value": -1 },
21
+ "max": { "dtype": "float32", "shape": [], "dist": "constant", "value": 1 }
22
+ },
23
+ "outputs": { "output": { "shape": [8388608], "dtype": "float32" } },
24
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
25
+ },
26
+ {
27
+ "name": "clip-f16-8m",
28
+ "preset": "smoke",
29
+ "vars": { "dtype": "float16", "count": 8388608 },
30
+ "inputs": {
31
+ "input": { "shape": [8388608], "dtype": "float16", "seed": 7026, "dist": "normal", "scale": 2 },
32
+ "min": { "dtype": "float16", "shape": [], "dist": "constant", "value": -1 },
33
+ "max": { "dtype": "float16", "shape": [], "dist": "constant", "value": 1 }
34
+ },
35
+ "outputs": { "output": { "shape": [8388608], "dtype": "float16" } },
36
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
37
+ },
38
+ {
39
+ "name": "clip-f32-input-bounds-large-scalar-path",
40
+ "provenance": {
41
+ "source": "authored for performance coverage",
42
+ "notes": "Named for the scalar route it was authored to exercise. Its element count is divisible by four, so it now takes the vectorized input-bounds route; the scalar route stays live for counts that are not, and its fixtures still cover it."
43
+ },
44
+ "preset": "stress",
45
+ "vars": { "dtype": "float32", "count": 8388608 },
46
+ "inputs": {
47
+ "input": { "shape": [8388608], "dtype": "float32", "dist": "normal", "seed": 7101, "scale": 2 },
48
+ "min": { "shape": [], "dtype": "float32", "dist": "constant", "value": -1 },
49
+ "max": { "shape": [], "dtype": "float32", "dist": "constant", "value": 1 }
50
+ },
51
+ "outputs": { "output": { "shape": [8388608], "dtype": "float32", "dist": "empty" } },
52
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
53
+ },
54
+ {
55
+ "name": "clip-f16-8m-default-bounds-vec4",
56
+ "provenance": {
57
+ "source": "authored for performance coverage",
58
+ "notes": "Clip with no min or max inputs, the only form that reaches the vec4 unary path. Nothing benchmarked that path, so the scalar input-bounds route had no vectorized sibling to be measured against."
59
+ },
60
+ "preset": "smoke",
61
+ "vars": { "dtype": "float16", "count": 8388608 },
62
+ "inputs": { "input": { "shape": [8388608], "dtype": "float16", "seed": 7026, "dist": "normal", "scale": 2 } },
63
+ "outputs": { "output": { "shape": [8388608], "dtype": "float16" } },
64
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
65
+ }
66
+ ]
67
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "ai.onnx",
3
+ "name": "Clip",
4
+ "sinceVersion": 13,
5
+ "description": "Limits each element of the input tensor to the interval `[min, max]`, equivalent to `Min(max, Max(input, min))`. Each bound is an optional scalar input tensor; an omitted bound defaults to the input dtype's lowest or highest numeric value. When `min` exceeds `max`, all elements are set to `max`.",
6
+ "inputs": [
7
+ { "role": "input", "dtype": "T", "description": "Input tensor whose elements are to be clipped." },
8
+ {
9
+ "role": "min",
10
+ "dtype": "T",
11
+ "rank": 0,
12
+ "optional": true,
13
+ "shape": [],
14
+ "description": "Scalar lower bound; elements below this value are replaced by it."
15
+ },
16
+ {
17
+ "role": "max",
18
+ "dtype": "T",
19
+ "rank": 0,
20
+ "optional": true,
21
+ "shape": [],
22
+ "description": "Scalar upper bound; elements above this value are replaced by it."
23
+ }
24
+ ],
25
+ "outputs": [
26
+ {
27
+ "role": "output",
28
+ "dtype": "T",
29
+ "rank": "ranks.input",
30
+ "shape": "shapes.input",
31
+ "description": "Output tensor with each element clipped to the specified interval."
32
+ }
33
+ ],
34
+ "typeConstraints": { "T": ["float32", "float16", "int32", "uint32", "int8", "uint8"] },
35
+ "args": {
36
+ "input": { "kind": "tensor", "semantic": "input", "role": "input" },
37
+ "output": { "kind": "tensor", "semantic": "output", "role": "output" },
38
+ "min": { "kind": "tensor", "semantic": "min", "role": "input1", "required": false },
39
+ "max": { "kind": "tensor", "semantic": "max", "role": "input2", "required": false }
40
+ },
41
+ "tunables": { "WORKGROUP_SIZE": 256 },
42
+ "derive": {
43
+ "shapeContract": "numel(shapes.input) == numel(shapes.output) and f16Ok(dtypes.T)",
44
+ "defaultBoundsContract": "shapeContract and not present.min and not present.max",
45
+ "minInputContract": "shapeContract and present.min and not present.max and ranks.min == 0 and numel(shapes.min) == 1",
46
+ "maxInputContract": "shapeContract and not present.min and present.max and ranks.max == 0 and numel(shapes.max) == 1",
47
+ "inputBoundsContract": "shapeContract and present.min and present.max and ranks.min == 0 and numel(shapes.min) == 1 and ranks.max == 0 and numel(shapes.max) == 1",
48
+ "defaultMin": "-65504 if tensorDtypes.input == \"float16\" else (-340282346638528859811704183484516925440 if tensorDtypes.input == \"float32\" else (-128 if tensorDtypes.input == \"int8\" else (-2147483648 if tensorDtypes.input == \"int32\" else 0)))",
49
+ "defaultMax": "65504 if tensorDtypes.input == \"float16\" else (340282346638528859811704183484516925440 if tensorDtypes.input == \"float32\" else (127 if tensorDtypes.input == \"int8\" else (2147483647 if tensorDtypes.input == \"int32\" else (255 if tensorDtypes.input == \"uint8\" else 4294967295))))",
50
+ "wideVec4StorageOk": "dtypes.T != \"f32\" or device.features.has(\"subgroups\") or not (has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 8 and device.adapterInfo.subgroupMaxSize <= 32)"
51
+ },
52
+ "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"" },
53
+ "bindingSets": {
54
+ "defaultVector": [
55
+ {
56
+ "name": "x",
57
+ "arg": "input",
58
+ "semantic": "input",
59
+ "buffer": { "type": "read-only-storage" },
60
+ "elementType": "$vectorScalar"
61
+ },
62
+ {
63
+ "name": "y",
64
+ "arg": "output",
65
+ "semantic": "output",
66
+ "buffer": { "type": "storage" },
67
+ "elementType": "$vectorScalar"
68
+ },
69
+ {
70
+ "name": "params",
71
+ "semantic": "kernel.params",
72
+ "buffer": { "type": "uniform" },
73
+ "struct": {
74
+ "name": "Params",
75
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }]
76
+ }
77
+ }
78
+ ],
79
+ "defaultScalar": [
80
+ {
81
+ "name": "input",
82
+ "arg": "input",
83
+ "semantic": "input",
84
+ "buffer": { "type": "read-only-storage" },
85
+ "elementType": "$scalar"
86
+ },
87
+ {
88
+ "name": "output",
89
+ "arg": "output",
90
+ "semantic": "output",
91
+ "buffer": { "type": "storage" },
92
+ "elementType": "$scalar"
93
+ },
94
+ {
95
+ "name": "params",
96
+ "semantic": "kernel.params",
97
+ "buffer": { "type": "uniform" },
98
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
99
+ }
100
+ ],
101
+ "inputBounds": [
102
+ {
103
+ "name": "input",
104
+ "arg": "input",
105
+ "semantic": "input",
106
+ "buffer": { "type": "read-only-storage" },
107
+ "elementType": "$scalar"
108
+ },
109
+ {
110
+ "name": "output",
111
+ "arg": "output",
112
+ "semantic": "output",
113
+ "buffer": { "type": "storage" },
114
+ "elementType": "$scalar"
115
+ },
116
+ {
117
+ "name": "clipMin",
118
+ "arg": "min",
119
+ "semantic": "min",
120
+ "buffer": { "type": "read-only-storage" },
121
+ "elementType": "$scalar"
122
+ },
123
+ {
124
+ "name": "clipMax",
125
+ "arg": "max",
126
+ "semantic": "max",
127
+ "buffer": { "type": "read-only-storage" },
128
+ "elementType": "$scalar"
129
+ },
130
+ {
131
+ "name": "params",
132
+ "semantic": "kernel.params",
133
+ "buffer": { "type": "uniform" },
134
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
135
+ }
136
+ ],
137
+ "minInput": [
138
+ {
139
+ "name": "input",
140
+ "arg": "input",
141
+ "semantic": "input",
142
+ "buffer": { "type": "read-only-storage" },
143
+ "elementType": "$scalar"
144
+ },
145
+ {
146
+ "name": "output",
147
+ "arg": "output",
148
+ "semantic": "output",
149
+ "buffer": { "type": "storage" },
150
+ "elementType": "$scalar"
151
+ },
152
+ {
153
+ "name": "clipMin",
154
+ "arg": "min",
155
+ "semantic": "min",
156
+ "buffer": { "type": "read-only-storage" },
157
+ "elementType": "$scalar"
158
+ },
159
+ {
160
+ "name": "params",
161
+ "semantic": "kernel.params",
162
+ "buffer": { "type": "uniform" },
163
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
164
+ }
165
+ ],
166
+ "maxInput": [
167
+ {
168
+ "name": "input",
169
+ "arg": "input",
170
+ "semantic": "input",
171
+ "buffer": { "type": "read-only-storage" },
172
+ "elementType": "$scalar"
173
+ },
174
+ {
175
+ "name": "output",
176
+ "arg": "output",
177
+ "semantic": "output",
178
+ "buffer": { "type": "storage" },
179
+ "elementType": "$scalar"
180
+ },
181
+ {
182
+ "name": "clipMax",
183
+ "arg": "max",
184
+ "semantic": "max",
185
+ "buffer": { "type": "read-only-storage" },
186
+ "elementType": "$scalar"
187
+ },
188
+ {
189
+ "name": "params",
190
+ "semantic": "kernel.params",
191
+ "buffer": { "type": "uniform" },
192
+ "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }] }
193
+ }
194
+ ],
195
+ "inputBoundsVector": [
196
+ {
197
+ "name": "x",
198
+ "arg": "input",
199
+ "semantic": "input",
200
+ "buffer": { "type": "read-only-storage" },
201
+ "elementType": "$vectorScalar"
202
+ },
203
+ {
204
+ "name": "y",
205
+ "arg": "output",
206
+ "semantic": "output",
207
+ "buffer": { "type": "storage" },
208
+ "elementType": "$vectorScalar"
209
+ },
210
+ {
211
+ "name": "clipMin",
212
+ "arg": "min",
213
+ "semantic": "min",
214
+ "buffer": { "type": "read-only-storage" },
215
+ "elementType": "$scalar"
216
+ },
217
+ {
218
+ "name": "clipMax",
219
+ "arg": "max",
220
+ "semantic": "max",
221
+ "buffer": { "type": "read-only-storage" },
222
+ "elementType": "$scalar"
223
+ },
224
+ {
225
+ "name": "params",
226
+ "semantic": "kernel.params",
227
+ "buffer": { "type": "uniform" },
228
+ "struct": {
229
+ "name": "Params",
230
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }]
231
+ }
232
+ }
233
+ ],
234
+ "minInputVector": [
235
+ {
236
+ "name": "x",
237
+ "arg": "input",
238
+ "semantic": "input",
239
+ "buffer": { "type": "read-only-storage" },
240
+ "elementType": "$vectorScalar"
241
+ },
242
+ {
243
+ "name": "y",
244
+ "arg": "output",
245
+ "semantic": "output",
246
+ "buffer": { "type": "storage" },
247
+ "elementType": "$vectorScalar"
248
+ },
249
+ {
250
+ "name": "clipMin",
251
+ "arg": "min",
252
+ "semantic": "min",
253
+ "buffer": { "type": "read-only-storage" },
254
+ "elementType": "$scalar"
255
+ },
256
+ {
257
+ "name": "params",
258
+ "semantic": "kernel.params",
259
+ "buffer": { "type": "uniform" },
260
+ "struct": {
261
+ "name": "Params",
262
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }]
263
+ }
264
+ }
265
+ ],
266
+ "maxInputVector": [
267
+ {
268
+ "name": "x",
269
+ "arg": "input",
270
+ "semantic": "input",
271
+ "buffer": { "type": "read-only-storage" },
272
+ "elementType": "$vectorScalar"
273
+ },
274
+ {
275
+ "name": "y",
276
+ "arg": "output",
277
+ "semantic": "output",
278
+ "buffer": { "type": "storage" },
279
+ "elementType": "$vectorScalar"
280
+ },
281
+ {
282
+ "name": "clipMax",
283
+ "arg": "max",
284
+ "semantic": "max",
285
+ "buffer": { "type": "read-only-storage" },
286
+ "elementType": "$scalar"
287
+ },
288
+ {
289
+ "name": "params",
290
+ "semantic": "kernel.params",
291
+ "buffer": { "type": "uniform" },
292
+ "struct": {
293
+ "name": "Params",
294
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }]
295
+ }
296
+ }
297
+ ]
298
+ },
299
+ "variants": [
300
+ {
301
+ "id": "default_bounds_vec4",
302
+ "priority": 20,
303
+ "when": ["defaultBoundsContract", "numel(shapes.input) > 0", "numel(shapes.input) % 4 == 0", "wideVec4StorageOk"],
304
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
305
+ "passes": [
306
+ {
307
+ "id": "main",
308
+ "name": "Clip.defaultBoundsVec4",
309
+ "source": {
310
+ "shader": "unary-vec4.wgsl.jinja",
311
+ "inputs": { "op": "\"clip\"", "boundsFromInput": false, "minValue": "defaultMin", "maxValue": "defaultMax" }
312
+ },
313
+ "bindings": "defaultVector",
314
+ "dispatch": { "threads": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
315
+ }
316
+ ]
317
+ },
318
+ {
319
+ "id": "default_bounds",
320
+ "when": ["defaultBoundsContract"],
321
+ "passes": [
322
+ {
323
+ "id": "main",
324
+ "name": "Clip.defaultBounds",
325
+ "source": {
326
+ "shader": "unary-scalar.wgsl.jinja",
327
+ "inputs": { "op": "\"clip\"", "boundsFromInput": false, "minValue": "defaultMin", "maxValue": "defaultMax" }
328
+ },
329
+ "bindings": "defaultScalar",
330
+ "dispatch": { "threads": "numel(shapes.output)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
331
+ }
332
+ ]
333
+ },
334
+ {
335
+ "id": "input_bounds_vec4",
336
+ "priority": 40,
337
+ "when": ["inputBoundsContract", "numel(shapes.input) > 0", "numel(shapes.input) % 4 == 0", "wideVec4StorageOk"],
338
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
339
+ "passes": [
340
+ {
341
+ "id": "main",
342
+ "name": "Clip.inputBoundsVec4",
343
+ "source": {
344
+ "shader": "unary-vec4.wgsl.jinja",
345
+ "inputs": { "op": "\"clip\"", "boundsFromInput": true, "clipMinFromInput": true, "clipMaxFromInput": true }
346
+ },
347
+ "bindings": "inputBoundsVector",
348
+ "dispatch": { "threads": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
349
+ }
350
+ ]
351
+ },
352
+ {
353
+ "id": "min_input_vec4",
354
+ "priority": 40,
355
+ "when": ["minInputContract", "numel(shapes.input) > 0", "numel(shapes.input) % 4 == 0", "wideVec4StorageOk"],
356
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
357
+ "passes": [
358
+ {
359
+ "id": "main",
360
+ "name": "Clip.minInputVec4",
361
+ "source": {
362
+ "shader": "unary-vec4.wgsl.jinja",
363
+ "inputs": { "op": "\"clip\"", "boundsFromInput": true, "clipMinFromInput": true, "clipMaxFromInput": false }
364
+ },
365
+ "bindings": "minInputVector",
366
+ "dispatch": { "threads": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
367
+ }
368
+ ]
369
+ },
370
+ {
371
+ "id": "max_input_vec4",
372
+ "priority": 40,
373
+ "when": ["maxInputContract", "numel(shapes.input) > 0", "numel(shapes.input) % 4 == 0", "wideVec4StorageOk"],
374
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
375
+ "passes": [
376
+ {
377
+ "id": "main",
378
+ "name": "Clip.maxInputVec4",
379
+ "source": {
380
+ "shader": "unary-vec4.wgsl.jinja",
381
+ "inputs": { "op": "\"clip\"", "boundsFromInput": true, "clipMinFromInput": false, "clipMaxFromInput": true }
382
+ },
383
+ "bindings": "maxInputVector",
384
+ "dispatch": { "threads": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
385
+ }
386
+ ]
387
+ },
388
+ {
389
+ "id": "input_bounds",
390
+ "priority": 30,
391
+ "when": ["inputBoundsContract"],
392
+ "passes": [
393
+ {
394
+ "id": "main",
395
+ "name": "Clip.inputBounds",
396
+ "source": {
397
+ "shader": "unary-scalar.wgsl.jinja",
398
+ "inputs": { "op": "\"clip\"", "boundsFromInput": true, "clipMinFromInput": true, "clipMaxFromInput": true }
399
+ },
400
+ "bindings": "inputBounds",
401
+ "dispatch": { "threads": "numel(shapes.output)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
402
+ }
403
+ ]
404
+ },
405
+ {
406
+ "id": "min_input",
407
+ "priority": 30,
408
+ "when": ["minInputContract"],
409
+ "passes": [
410
+ {
411
+ "id": "main",
412
+ "name": "Clip.minInput",
413
+ "source": {
414
+ "shader": "unary-scalar.wgsl.jinja",
415
+ "inputs": { "op": "\"clip\"", "boundsFromInput": true, "clipMinFromInput": true, "clipMaxFromInput": false }
416
+ },
417
+ "bindings": "minInput",
418
+ "dispatch": { "threads": "numel(shapes.output)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
419
+ }
420
+ ]
421
+ },
422
+ {
423
+ "id": "max_input",
424
+ "priority": 30,
425
+ "when": ["maxInputContract"],
426
+ "passes": [
427
+ {
428
+ "id": "main",
429
+ "name": "Clip.maxInput",
430
+ "source": {
431
+ "shader": "unary-scalar.wgsl.jinja",
432
+ "inputs": { "op": "\"clip\"", "boundsFromInput": true, "clipMinFromInput": false, "clipMaxFromInput": true }
433
+ },
434
+ "bindings": "maxInput",
435
+ "dispatch": { "threads": "numel(shapes.output)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
436
+ }
437
+ ]
438
+ }
439
+ ]
440
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ai.onnx.Clip",
3
+ "id": "_ai_onnx_clip_webgpu_ea7567b",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "SiGZqqpALf6ubJItLzIkYagu90oP+HzScN4MdEyZgc8=",
11
+ "manifest.json": "us9b3p/dfzQqD40BHqWMZiVp6xaoru4kvNbEXWhfevM=",
12
+ "test.json": "ymGZ3BiNHy1Nye9xpVQCWrfPh5sbCvMpsaE5E8lWq/c=",
13
+ "unary-scalar.wgsl.jinja": "NeWScwhOG4N4iqKUuLMB0vL0DbMQqgUCFSp+g+YoKCY=",
14
+ "unary-vec4.wgsl.jinja": "QKAUBbXPhcNyTR7St0KyqUqJjuS++OgbomsrxdMCrTw="
15
+ }
16
+ },
17
+ "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
18
+ "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Clip" }
19
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,985 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Clip",
3
+ "fixtureArrays": {
4
+ "onnx_backend_clip_input": [1.764052391052246, 0.40015721321105957, 0.978738009929657, 2.2408931255340576, 1.8675580024719238, -0.9772778749465942, 0.9500884413719177, -0.15135720372200012, -0.10321885347366333, 0.4105985164642334, 0.14404356479644775, 1.4542734622955322, 0.7610377073287964, 0.12167501449584961, 0.44386324286460876, 0.3336743414402008, 1.4940791130065918, -0.2051582634449005, 0.3130677044391632, -0.8540957570075989, -2.5529897212982178, 0.653618574142456, 0.8644362092018127, -0.7421650290489197, 2.269754648208618, -1.4543657302856445, 0.04575851559638977, -0.18718385696411133, 1.5327792167663574, 1.4693588018417358, 0.154947429895401, 0.37816253304481506, -0.8877857327461243, -1.980796456336975, -0.34791216254234314, 0.15634897351264954, 1.2302906513214111, 1.202379822731018, -0.38732680678367615, -0.302302747964859, -1.0485529899597168, -1.420017957687378, -1.7062702178955078, 1.950775384902954, -0.5096521973609924, -0.4380742907524109, -1.2527953386306763, 0.7774903774261475, -1.6138978004455566, -0.21274028718471527, -0.8954665660858154, 0.38690251111984253, -0.5108051300048828, -1.18063223361969, -0.02818222902715206, 0.4283318817615509, 0.06651721894741058, 0.30247190594673157, -0.6343221068382263, -0.3627411723136902]
5
+ },
6
+ "cases": [
7
+ {
8
+ "name": "f32_relu_style_positive_subnormal_gpu_gap",
9
+ "skipGpu": {
10
+ "category": "permanent",
11
+ "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: WebGPU/Metal flushes subnormals to zero (f32 and f16); the kernel cannot preserve denormal inputs/outputs bit-exactly."
12
+ },
13
+ "provenance": {
14
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
15
+ "test": "MathOpTest.Clip_Relu",
16
+ "notes": "Clip with min=0 is ReLU-like: positive subnormal inputs should pass through instead of flushing to zero."
17
+ },
18
+ "inputs": {
19
+ "input": {
20
+ "dtype": "float32",
21
+ "shape": [4],
22
+ "data": { "kind": "values", "values": [-1e-40, 0.0, 1e-40, 1e-39] }
23
+ },
24
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } },
25
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
26
+ },
27
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0 } }
28
+ },
29
+ {
30
+ "name": "f32_relu_style_positive_subnormal_scalar_gpu_gap",
31
+ "skipGpu": {
32
+ "category": "permanent",
33
+ "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: WebGPU/Metal flushes subnormals to zero (f32 and f16); the kernel cannot preserve denormal inputs/outputs bit-exactly."
34
+ },
35
+ "provenance": {
36
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
37
+ "test": "MathOpTest.Clip_Relu",
38
+ "notes": "Scalar-path companion: Clip with min=0 should pass through positive subnormal inputs instead of flushing them to zero."
39
+ },
40
+ "inputs": {
41
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-1e-40, 0.0, 1e-40] } },
42
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } },
43
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
44
+ },
45
+ "outputs": { "output": { "dtype": "float32", "shape": [3], "tolerance": 0 } }
46
+ },
47
+ {
48
+ "name": "input_bounds_f32_small",
49
+ "inputs": {
50
+ "input": {
51
+ "dtype": "float32",
52
+ "shape": [6],
53
+ "data": { "kind": "values", "values": [-3.0, -1.0, 0.5, 1.5, 3.0, 9.0] }
54
+ },
55
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.5] } },
56
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [2.0] } }
57
+ },
58
+ "outputs": { "output": { "dtype": "float32", "shape": [6] } }
59
+ },
60
+ {
61
+ "name": "input_bounds_f32_1024",
62
+ "inputs": {
63
+ "input": { "dtype": "float32", "shape": [1024], "data": { "kind": "linspace", "start": -4.0, "end": 4.0 } },
64
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.5] } },
65
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [2.0] } }
66
+ },
67
+ "outputs": { "output": { "dtype": "float32", "shape": [1024], "tolerance": 0.000001 } }
68
+ },
69
+ {
70
+ "name": "input_bounds_f32_nonfinite",
71
+ "inputs": {
72
+ "input": {
73
+ "dtype": "float32",
74
+ "shape": [4],
75
+ "data": { "kind": "values", "values": ["-Infinity", -1.0, 1.0, "Infinity"] }
76
+ },
77
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": ["-Infinity"] } },
78
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": ["Infinity"] } }
79
+ },
80
+ "outputs": {
81
+ "output": {
82
+ "dtype": "float32",
83
+ "shape": [4],
84
+ "tolerance": 0,
85
+ "data": { "kind": "values", "values": ["-Infinity", -1.0, 1.0, "Infinity"] }
86
+ }
87
+ }
88
+ },
89
+ {
90
+ "name": "f16_relu6_style",
91
+ "inputs": {
92
+ "input": { "dtype": "float16", "shape": [4], "data": { "kind": "values", "values": [-1.0, 0.5, 7.0, 3.0] } },
93
+ "min": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [0.0] } },
94
+ "max": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [6.0] } }
95
+ },
96
+ "outputs": { "output": { "dtype": "float16", "shape": [4] } },
97
+ "tolerance": 0.001
98
+ },
99
+ {
100
+ "name": "input_bounds_float32",
101
+ "provenance": {
102
+ "source": "onnx/onnx/backend/test/case/node/clip.py",
103
+ "notes": "Clip-13 expresses min/max as optional scalar inputs; this fixture exercises both tensor-bound inputs."
104
+ },
105
+ "inputs": {
106
+ "input": {
107
+ "dtype": "float32",
108
+ "shape": [5],
109
+ "data": { "kind": "values", "values": [-3.0, -1.0, 0.5, 2.0, 9.0] }
110
+ },
111
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
112
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [2.0] } }
113
+ },
114
+ "outputs": { "output": { "dtype": "float32", "shape": [5], "tolerance": 0 } }
115
+ },
116
+ {
117
+ "name": "input_bounds_float16",
118
+ "inputs": {
119
+ "input": {
120
+ "dtype": "float16",
121
+ "shape": [5],
122
+ "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 2.0, 8.0] }
123
+ },
124
+ "min": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
125
+ "max": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [2.0] } }
126
+ },
127
+ "outputs": {
128
+ "output": {
129
+ "dtype": "float16",
130
+ "shape": [5],
131
+ "tolerance": 0,
132
+ "data": { "kind": "values", "values": [-1.0, -1.0, 0.5, 2.0, 2.0] }
133
+ }
134
+ }
135
+ },
136
+ {
137
+ "name": "input_bounds_int8",
138
+ "inputs": {
139
+ "input": { "dtype": "int8", "shape": [6], "data": { "kind": "values", "values": [-128, -12, -3, 4, 17, 127] } },
140
+ "min": { "dtype": "int8", "shape": [], "data": { "kind": "values", "values": [-3] } },
141
+ "max": { "dtype": "int8", "shape": [], "data": { "kind": "values", "values": [17] } }
142
+ },
143
+ "outputs": {
144
+ "output": {
145
+ "dtype": "int8",
146
+ "shape": [6],
147
+ "tolerance": 0,
148
+ "data": { "kind": "values", "values": [-3, -3, -3, 4, 17, 17] }
149
+ }
150
+ }
151
+ },
152
+ {
153
+ "name": "input_bounds_uint8",
154
+ "inputs": {
155
+ "input": { "dtype": "uint8", "shape": [6], "data": { "kind": "values", "values": [0, 5, 17, 18, 199, 255] } },
156
+ "min": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [17] } },
157
+ "max": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [199] } }
158
+ },
159
+ "outputs": {
160
+ "output": {
161
+ "dtype": "uint8",
162
+ "shape": [6],
163
+ "tolerance": 0,
164
+ "data": { "kind": "values", "values": [17, 17, 17, 18, 199, 199] }
165
+ }
166
+ }
167
+ },
168
+ {
169
+ "name": "input_min_only_float16",
170
+ "inputs": {
171
+ "input": { "dtype": "float16", "shape": [4], "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 8.0] } },
172
+ "min": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [-1.0] } }
173
+ },
174
+ "outputs": {
175
+ "output": {
176
+ "dtype": "float16",
177
+ "shape": [4],
178
+ "tolerance": 0,
179
+ "data": { "kind": "values", "values": [-1.0, -1.0, 0.5, 8.0] }
180
+ }
181
+ }
182
+ },
183
+ {
184
+ "name": "input_max_only_float16",
185
+ "inputs": {
186
+ "input": { "dtype": "float16", "shape": [4], "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 8.0] } },
187
+ "max": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [2.0] } }
188
+ },
189
+ "outputs": {
190
+ "output": {
191
+ "dtype": "float16",
192
+ "shape": [4],
193
+ "tolerance": 0,
194
+ "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 2.0] }
195
+ }
196
+ }
197
+ },
198
+ {
199
+ "name": "input_min_only_int8",
200
+ "inputs": {
201
+ "input": { "dtype": "int8", "shape": [4], "data": { "kind": "values", "values": [-128, -3, 4, 127] } },
202
+ "min": { "dtype": "int8", "shape": [], "data": { "kind": "values", "values": [-3] } }
203
+ },
204
+ "outputs": {
205
+ "output": {
206
+ "dtype": "int8",
207
+ "shape": [4],
208
+ "tolerance": 0,
209
+ "data": { "kind": "values", "values": [-3, -3, 4, 127] }
210
+ }
211
+ }
212
+ },
213
+ {
214
+ "name": "input_max_only_int8",
215
+ "inputs": {
216
+ "input": { "dtype": "int8", "shape": [4], "data": { "kind": "values", "values": [-128, -3, 17, 127] } },
217
+ "max": { "dtype": "int8", "shape": [], "data": { "kind": "values", "values": [17] } }
218
+ },
219
+ "outputs": {
220
+ "output": {
221
+ "dtype": "int8",
222
+ "shape": [4],
223
+ "tolerance": 0,
224
+ "data": { "kind": "values", "values": [-128, -3, 17, 17] }
225
+ }
226
+ }
227
+ },
228
+ {
229
+ "name": "input_min_only_uint8",
230
+ "inputs": {
231
+ "input": { "dtype": "uint8", "shape": [4], "data": { "kind": "values", "values": [0, 17, 18, 255] } },
232
+ "min": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [17] } }
233
+ },
234
+ "outputs": {
235
+ "output": {
236
+ "dtype": "uint8",
237
+ "shape": [4],
238
+ "tolerance": 0,
239
+ "data": { "kind": "values", "values": [17, 17, 18, 255] }
240
+ }
241
+ }
242
+ },
243
+ {
244
+ "name": "input_max_only_uint8",
245
+ "inputs": {
246
+ "input": { "dtype": "uint8", "shape": [4], "data": { "kind": "values", "values": [0, 17, 199, 255] } },
247
+ "max": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [199] } }
248
+ },
249
+ "outputs": {
250
+ "output": {
251
+ "dtype": "uint8",
252
+ "shape": [4],
253
+ "tolerance": 0,
254
+ "data": { "kind": "values", "values": [0, 17, 199, 199] }
255
+ }
256
+ }
257
+ },
258
+ {
259
+ "name": "input_min_only_float32",
260
+ "inputs": {
261
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 8.0] } },
262
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } }
263
+ },
264
+ "outputs": {
265
+ "output": {
266
+ "dtype": "float32",
267
+ "shape": [4],
268
+ "tolerance": 0,
269
+ "data": { "kind": "values", "values": [-1.0, -1.0, 0.5, 8.0] }
270
+ }
271
+ }
272
+ },
273
+ {
274
+ "name": "input_max_only_float32",
275
+ "inputs": {
276
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 8.0] } },
277
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [2.0] } }
278
+ },
279
+ "outputs": {
280
+ "output": {
281
+ "dtype": "float32",
282
+ "shape": [4],
283
+ "tolerance": 0,
284
+ "data": { "kind": "values", "values": [-4.0, -1.0, 0.5, 2.0] }
285
+ }
286
+ }
287
+ },
288
+ {
289
+ "name": "input_min_only_int32",
290
+ "inputs": {
291
+ "input": {
292
+ "dtype": "int32",
293
+ "shape": [4],
294
+ "data": { "kind": "values", "values": [-2147483648, -16777217, 0, 2147483647] }
295
+ },
296
+ "min": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [-16777217] } }
297
+ },
298
+ "outputs": {
299
+ "output": {
300
+ "dtype": "int32",
301
+ "shape": [4],
302
+ "tolerance": 0,
303
+ "data": { "kind": "values", "values": [-16777217, -16777217, 0, 2147483647] }
304
+ }
305
+ }
306
+ },
307
+ {
308
+ "name": "input_max_only_int32",
309
+ "inputs": {
310
+ "input": {
311
+ "dtype": "int32",
312
+ "shape": [4],
313
+ "data": { "kind": "values", "values": [-2147483648, 0, 16777217, 2147483647] }
314
+ },
315
+ "max": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [16777217] } }
316
+ },
317
+ "outputs": {
318
+ "output": {
319
+ "dtype": "int32",
320
+ "shape": [4],
321
+ "tolerance": 0,
322
+ "data": { "kind": "values", "values": [-2147483648, 0, 16777217, 16777217] }
323
+ }
324
+ }
325
+ },
326
+ {
327
+ "name": "input_min_only_uint32",
328
+ "inputs": {
329
+ "input": {
330
+ "dtype": "uint32",
331
+ "shape": [4],
332
+ "data": { "kind": "values", "values": [0, 16777216, 16777217, 4000000001] }
333
+ },
334
+ "min": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [16777217] } }
335
+ },
336
+ "outputs": {
337
+ "output": {
338
+ "dtype": "uint32",
339
+ "shape": [4],
340
+ "tolerance": 0,
341
+ "data": { "kind": "values", "values": [16777217, 16777217, 16777217, 4000000001] }
342
+ }
343
+ }
344
+ },
345
+ {
346
+ "name": "input_max_only_uint32",
347
+ "inputs": {
348
+ "input": {
349
+ "dtype": "uint32",
350
+ "shape": [4],
351
+ "data": { "kind": "values", "values": [0, 16777217, 16777218, 4000000001] }
352
+ },
353
+ "max": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [16777217] } }
354
+ },
355
+ "outputs": {
356
+ "output": {
357
+ "dtype": "uint32",
358
+ "shape": [4],
359
+ "tolerance": 0,
360
+ "data": { "kind": "values", "values": [0, 16777217, 16777217, 16777217] }
361
+ }
362
+ }
363
+ },
364
+ {
365
+ "name": "nan_input_propagates",
366
+ "inputs": {
367
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": ["NaN", -2.0, 0.0, 3.0] } },
368
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
369
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
370
+ },
371
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0.000001, "allowNaN": true } }
372
+ },
373
+ {
374
+ "name": "backend_min_greater_than_max",
375
+ "inputs": {
376
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-2.0, 0.0, 6.0] } },
377
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [2.0] } },
378
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
379
+ },
380
+ "outputs": { "output": { "dtype": "float32", "shape": [3], "tolerance": 0 } },
381
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_min_greater_than_max" }
382
+ },
383
+ {
384
+ "name": "default_max_only",
385
+ "inputs": {
386
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [-3.0, -0.5, 0.0, 2.0] } },
387
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
388
+ },
389
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0 } }
390
+ },
391
+ {
392
+ "name": "default_min_only",
393
+ "inputs": {
394
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [-3.0, 0.0, 0.5, 2.0] } },
395
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
396
+ },
397
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0 } }
398
+ },
399
+ {
400
+ "name": "onnx_example_input_bounds",
401
+ "inputs": {
402
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-2.0, 0.0, 2.0] } },
403
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
404
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
405
+ },
406
+ "outputs": { "output": { "dtype": "float32", "shape": [3], "tolerance": 0 } },
407
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_example" }
408
+ },
409
+ {
410
+ "name": "default_no_bounds_uses_float_limits",
411
+ "inputs": {
412
+ "input": {
413
+ "dtype": "float32",
414
+ "shape": [4],
415
+ "data": { "kind": "values", "values": ["-Infinity", -3.0, 3.0, "Infinity"] }
416
+ }
417
+ },
418
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0 } }
419
+ },
420
+ {
421
+ "name": "default_int32_vec4_full_range",
422
+ "provenance": {
423
+ "source": "ONNX Clip-13 specification",
424
+ "test": "omitted integer bounds use the dtype range on the packed path"
425
+ },
426
+ "inputs": {
427
+ "input": {
428
+ "dtype": "int32",
429
+ "shape": [4],
430
+ "data": { "kind": "values", "values": [-2147483648, -1, 1, 2147483647] }
431
+ }
432
+ },
433
+ "outputs": { "output": { "dtype": "int32", "shape": [4], "tolerance": 0 } }
434
+ },
435
+ {
436
+ "name": "zero_size_noop",
437
+ "inputs": {
438
+ "input": { "dtype": "float32", "shape": [0, 3], "data": { "kind": "values", "values": [] } },
439
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
440
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
441
+ },
442
+ "outputs": { "output": { "dtype": "float32", "shape": [0, 3], "tolerance": 0 } }
443
+ },
444
+ {
445
+ "name": "ort_int32_input_bounds",
446
+ "provenance": {
447
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
448
+ "test": "MathOpTest.Clip_int32",
449
+ "notes": "ORT supplies min/max as scalar tensor inputs; the input_bounds route consumes both values directly."
450
+ },
451
+ "inputs": {
452
+ "input": {
453
+ "dtype": "int32",
454
+ "shape": [3, 3],
455
+ "data": { "kind": "values", "values": [-1, 0, 1, -16, 12, -6, -5, 2, 16] }
456
+ },
457
+ "min": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [-10] } },
458
+ "max": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [10] } }
459
+ },
460
+ "outputs": { "output": { "dtype": "int32", "shape": [3, 3], "tolerance": 0 } }
461
+ },
462
+ {
463
+ "name": "ort_uint32_input_bounds",
464
+ "provenance": {
465
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
466
+ "test": "MathOpTest.Clip_uint32",
467
+ "notes": "ORT supplies min/max as scalar tensor inputs; the input_bounds route consumes both values directly."
468
+ },
469
+ "inputs": {
470
+ "input": {
471
+ "dtype": "uint32",
472
+ "shape": [3, 3],
473
+ "data": { "kind": "values", "values": [0, 0, 1, 5, 12, 3, 2, 7, 16] }
474
+ },
475
+ "min": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
476
+ "max": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [10] } }
477
+ },
478
+ "outputs": { "output": { "dtype": "uint32", "shape": [3, 3], "tolerance": 0 } }
479
+ },
480
+ {
481
+ "name": "int32_exact_above_float24_no_float_cast",
482
+ "inputs": {
483
+ "input": {
484
+ "dtype": "int32",
485
+ "shape": [6],
486
+ "data": { "kind": "values", "values": [-16777218, -16777217, -16777216, 16777216, 16777217, 16777218] }
487
+ },
488
+ "min": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [-16777217] } },
489
+ "max": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [16777217] } }
490
+ },
491
+ "outputs": { "output": { "dtype": "int32", "shape": [6], "tolerance": 0 } }
492
+ },
493
+ {
494
+ "name": "uint32_exact_above_float24_no_float_cast",
495
+ "inputs": {
496
+ "input": {
497
+ "dtype": "uint32",
498
+ "shape": [5],
499
+ "data": { "kind": "values", "values": [16777215, 16777216, 16777217, 16777218, 20000000] }
500
+ },
501
+ "min": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [16777216] } },
502
+ "max": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [16777217] } }
503
+ },
504
+ "outputs": { "output": { "dtype": "uint32", "shape": [5], "tolerance": 0 } }
505
+ },
506
+ {
507
+ "name": "ort_input_bounds_f32_m10_p10",
508
+ "provenance": { "source": "onnxruntime/test/providers/cpu/math/clip_test.cc", "test": "MathOpTest.Clip_6" },
509
+ "inputs": {
510
+ "input": {
511
+ "dtype": "float32",
512
+ "shape": [3, 3],
513
+ "data": { "kind": "values", "values": [11.0, 4.4, 432.3, -1.3, 3.5, 64.0, -5.4, 9.3, 82.4] }
514
+ },
515
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-10.0] } },
516
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [10.0] } }
517
+ },
518
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 3], "tolerance": 0.00001 } }
519
+ },
520
+ {
521
+ "name": "ort_default_f32_3x3",
522
+ "provenance": { "source": "onnxruntime/test/providers/cpu/math/clip_test.cc", "test": "MathOpTest.Clip_Default" },
523
+ "inputs": {
524
+ "input": {
525
+ "dtype": "float32",
526
+ "shape": [3, 3],
527
+ "data": { "kind": "values", "values": [11.0, 4.4, 432.3, -1.3, 3.5, 64.0, -5.4, 9.3, 82.4] }
528
+ }
529
+ },
530
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 3], "tolerance": 0 } }
531
+ },
532
+ {
533
+ "name": "ort_default_int8_3x3",
534
+ "provenance": {
535
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
536
+ "test": "MathOpTest.Clip_Default_int8"
537
+ },
538
+ "inputs": {
539
+ "input": {
540
+ "dtype": "int8",
541
+ "shape": [3, 3],
542
+ "data": { "kind": "values", "values": [11, 4, 127, -1, 3, 64, -5, 9, 82] }
543
+ }
544
+ },
545
+ "outputs": { "output": { "dtype": "int8", "shape": [3, 3], "tolerance": 0 } }
546
+ },
547
+ {
548
+ "name": "ort_default_uint8_3x3",
549
+ "provenance": {
550
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
551
+ "test": "MathOpTest.Clip_Default_uint8"
552
+ },
553
+ "inputs": {
554
+ "input": {
555
+ "dtype": "uint8",
556
+ "shape": [3, 3],
557
+ "data": { "kind": "values", "values": [11, 4, 255, 1, 3, 64, 5, 9, 82] }
558
+ }
559
+ },
560
+ "outputs": { "output": { "dtype": "uint8", "shape": [3, 3], "tolerance": 0 } }
561
+ },
562
+ {
563
+ "name": "ort_f16_input_bounds",
564
+ "provenance": {
565
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
566
+ "test": "MathOpTest.Clip_MLFloat16"
567
+ },
568
+ "inputs": {
569
+ "input": {
570
+ "dtype": "float16",
571
+ "shape": [3, 3],
572
+ "data": { "kind": "values", "values": [-1.0, -2.0, -3.0, -4.0, 0.0, 2.0, 4.0, 6.0, 8.0] }
573
+ },
574
+ "min": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [0.0] } },
575
+ "max": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [6.0] } }
576
+ },
577
+ "outputs": { "output": { "dtype": "float16", "shape": [3, 3], "tolerance": 0.001 } }
578
+ },
579
+ {
580
+ "name": "ort_input_bounds_f32_3x3",
581
+ "provenance": {
582
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
583
+ "test": "MathOpTest.Clip",
584
+ "notes": "ORT supplies min/max as scalar inputs; this fixture supplies both bounds through Clip-13 scalar tensor inputs."
585
+ },
586
+ "inputs": {
587
+ "input": {
588
+ "dtype": "float32",
589
+ "shape": [3, 3],
590
+ "data": { "kind": "values", "values": [-1.0, 0.0, 1.0, -6.0, 0.0, 6.0, -5.4, 2.0, 6.0] }
591
+ },
592
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-5.0] } },
593
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [5.0] } }
594
+ },
595
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 3], "tolerance": 0 } }
596
+ },
597
+ {
598
+ "name": "ort_f16_default_bounds",
599
+ "provenance": {
600
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
601
+ "test": "MathOpTest.Clip_MLFloat16_NoMin_NoMax"
602
+ },
603
+ "inputs": {
604
+ "input": { "dtype": "float16", "shape": [3], "data": { "kind": "values", "values": [-1.0, -2.0, 3.0] } }
605
+ },
606
+ "outputs": { "output": { "dtype": "float16", "shape": [3], "tolerance": 0.001 } }
607
+ },
608
+ {
609
+ "name": "ort_f16_min_only",
610
+ "provenance": {
611
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
612
+ "test": "MathOpTest.Clip_MLFloat16_NoMax"
613
+ },
614
+ "inputs": {
615
+ "input": { "dtype": "float16", "shape": [3], "data": { "kind": "values", "values": [-1.0, -2.0, 3.0] } },
616
+ "min": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [0.0] } }
617
+ },
618
+ "outputs": { "output": { "dtype": "float16", "shape": [3], "tolerance": 0.001 } }
619
+ },
620
+ {
621
+ "name": "ort_f16_max_only",
622
+ "provenance": {
623
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
624
+ "test": "MathOpTest.Clip_MLFloat16_NoMin"
625
+ },
626
+ "inputs": {
627
+ "input": { "dtype": "float16", "shape": [3], "data": { "kind": "values", "values": [-1.0, -2.0, 3.0] } },
628
+ "max": { "dtype": "float16", "shape": [], "data": { "kind": "values", "values": [0.0] } }
629
+ },
630
+ "outputs": { "output": { "dtype": "float16", "shape": [3], "tolerance": 0.001 } }
631
+ },
632
+ {
633
+ "name": "ort_relu6_f32_3x3",
634
+ "provenance": { "source": "onnxruntime/test/providers/cpu/math/clip_test.cc", "test": "MathOpTest.Clip_Relu6" },
635
+ "inputs": {
636
+ "input": {
637
+ "dtype": "float32",
638
+ "shape": [3, 3],
639
+ "data": { "kind": "values", "values": [-1.0, 0.0, 1.0, -6.0, 3.5, 6.0, -5.4, 2.0, 8.0] }
640
+ },
641
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } },
642
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [6.0] } }
643
+ },
644
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 3], "tolerance": 0 } }
645
+ },
646
+ {
647
+ "name": "ort_relu_min_only_f32_3x3",
648
+ "provenance": {
649
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
650
+ "test": "MathOpTest.Clip_Relu",
651
+ "notes": "ORT supplies the lower bound as a scalar input; this fixture exercises the same Clip-13 input directly."
652
+ },
653
+ "inputs": {
654
+ "input": {
655
+ "dtype": "float32",
656
+ "shape": [3, 3],
657
+ "data": { "kind": "values", "values": [-1.0, 0.0, 1.0, -6.0, 3.5, 6.0, -5.4, 2.0, 8.0] }
658
+ },
659
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
660
+ },
661
+ "outputs": {
662
+ "output": {
663
+ "dtype": "float32",
664
+ "shape": [3, 3],
665
+ "tolerance": 0,
666
+ "data": { "kind": "values", "values": [0.0, 0.0, 1.0, 0.0, 3.5, 6.0, 0.0, 2.0, 8.0] }
667
+ }
668
+ }
669
+ },
670
+ {
671
+ "name": "ort_relu1_bounds_f32_3x3",
672
+ "provenance": {
673
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
674
+ "test": "MathOpTest.Clip_Relu1",
675
+ "notes": "ORT supplies both bounds as scalar inputs; this fixture exercises the same Clip-13 inputs directly."
676
+ },
677
+ "inputs": {
678
+ "input": {
679
+ "dtype": "float32",
680
+ "shape": [3, 3],
681
+ "data": { "kind": "values", "values": [-1.0, 0.0, 1.0, -6.0, 3.5, 6.0, -5.4, 2.0, 8.0] }
682
+ },
683
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
684
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
685
+ },
686
+ "outputs": {
687
+ "output": {
688
+ "dtype": "float32",
689
+ "shape": [3, 3],
690
+ "tolerance": 0,
691
+ "data": { "kind": "values", "values": [-1.0, 0.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0] }
692
+ }
693
+ }
694
+ },
695
+ {
696
+ "name": "ort_empty_dim_input_bounds",
697
+ "provenance": {
698
+ "source": "onnxruntime/test/providers/cpu/math/clip_test.cc",
699
+ "test": "MathOpTest.ClipDimWithZero"
700
+ },
701
+ "inputs": {
702
+ "input": { "dtype": "float32", "shape": [3, 0], "data": { "kind": "values", "values": [] } },
703
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-5.0] } },
704
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [5.0] } }
705
+ },
706
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 0], "tolerance": 0 } }
707
+ },
708
+ {
709
+ "name": "onnx_backend_clip",
710
+ "inputs": {
711
+ "input": {
712
+ "dtype": "float32",
713
+ "shape": [3, 4, 5],
714
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_clip_input" } }
715
+ },
716
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
717
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
718
+ },
719
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 4, 5] } },
720
+ "provenance": {
721
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip",
722
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
723
+ }
724
+ },
725
+ {
726
+ "name": "onnx_backend_clip_default_inbounds",
727
+ "inputs": {
728
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-1.0, 0.0, 1.0] } }
729
+ },
730
+ "outputs": { "output": { "dtype": "float32", "shape": [3] } },
731
+ "provenance": {
732
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_default_inbounds",
733
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
734
+ }
735
+ },
736
+ {
737
+ "name": "onnx_backend_clip_default_int8_inbounds",
738
+ "inputs": { "input": { "dtype": "int8", "shape": [3], "data": { "kind": "values", "values": [-1, 0, 1] } } },
739
+ "outputs": { "output": { "dtype": "int8", "shape": [3] } },
740
+ "provenance": {
741
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_default_int8_inbounds",
742
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
743
+ }
744
+ },
745
+ {
746
+ "name": "onnx_backend_clip_default_int8_max",
747
+ "inputs": {
748
+ "input": {
749
+ "dtype": "int8",
750
+ "shape": [3, 4, 5],
751
+ "data": {
752
+ "kind": "values",
753
+ "values": [0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 1, 1, 1, 0, -1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, -1, -1, 0, -1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
754
+ }
755
+ },
756
+ "max": { "dtype": "int8", "shape": [], "data": { "kind": "values", "values": [0] } }
757
+ },
758
+ "outputs": { "output": { "dtype": "int8", "shape": [3, 4, 5] } },
759
+ "provenance": {
760
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_default_int8_max",
761
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
762
+ }
763
+ },
764
+ {
765
+ "name": "onnx_backend_clip_default_int8_min",
766
+ "inputs": {
767
+ "input": {
768
+ "dtype": "int8",
769
+ "shape": [3, 4, 5],
770
+ "data": {
771
+ "kind": "values",
772
+ "values": [1, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -2, 0, 0, 0, 2, -1, 0, 0, 1, 1, 0, 0, 0, -1, 0, 0, 1, 1, 0, 0, -1, -1, -1, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0]
773
+ }
774
+ },
775
+ "min": { "dtype": "int8", "shape": [], "data": { "kind": "values", "values": [0] } }
776
+ },
777
+ "outputs": { "output": { "dtype": "int8", "shape": [3, 4, 5] } },
778
+ "provenance": {
779
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_default_int8_min",
780
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
781
+ }
782
+ },
783
+ {
784
+ "name": "onnx_backend_clip_default_max",
785
+ "inputs": {
786
+ "input": {
787
+ "dtype": "float32",
788
+ "shape": [3, 4, 5],
789
+ "data": {
790
+ "kind": "values",
791
+ "values": [-0.6724604368209839, -0.35955315828323364, -0.8131462931632996, -1.7262825965881348, 0.17742614448070526, -0.4017809331417084, -1.630198359489441, 0.46278226375579834, -0.9072983860969543, 0.05194539576768875, 0.7290905714035034, 0.12898291647434235, 1.1394007205963135, -1.234825849533081, 0.4023416340351105, -0.6848101019859314, -0.8707971572875977, -0.5788496732711792, -0.3115525245666504, 0.056165341287851334, -1.1651498079299927, 0.9008265137672424, 0.4656624495983124, -1.5362436771392822, 1.4882521629333496, 1.895889163017273, 1.1787796020507812, -0.1799248307943344, -1.0707526206970215, 1.0544517040252686, -0.4031769335269928, 1.222445011138916, 0.2082749754190445, 0.9766390323638916, 0.3563663959503174, 0.7065731883049011, 0.01050002034753561, 1.7858705520629883, 0.12691208720207214, 0.4019893705844879, 1.8831506967544556, -1.3477590084075928, -1.2704850435256958, 0.969396710395813, -1.1731233596801758, 1.9436211585998535, -0.4136189818382263, -0.747454822063446, 1.922942042350769, 1.4805147647857666, 1.8675589561462402, 0.9060446619987488, -0.8612256646156311, 1.910064935684204, -0.26800337433815, 0.8024563789367676, 0.9472519755363464, -0.15501008927822113, 0.6140793561935425, 0.922206699848175]
792
+ }
793
+ },
794
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
795
+ },
796
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 4, 5] } },
797
+ "provenance": {
798
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_default_max",
799
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
800
+ }
801
+ },
802
+ {
803
+ "name": "onnx_backend_clip_default_min",
804
+ "inputs": {
805
+ "input": {
806
+ "dtype": "float32",
807
+ "shape": [3, 4, 5],
808
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_clip_input" } }
809
+ },
810
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } }
811
+ },
812
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 4, 5] } },
813
+ "provenance": {
814
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_default_min",
815
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
816
+ }
817
+ },
818
+ {
819
+ "name": "onnx_backend_clip_example",
820
+ "inputs": {
821
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-2.0, 0.0, 2.0] } },
822
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
823
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
824
+ },
825
+ "outputs": { "output": { "dtype": "float32", "shape": [3] } },
826
+ "provenance": {
827
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_example",
828
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
829
+ }
830
+ },
831
+ {
832
+ "name": "onnx_backend_clip_inbounds",
833
+ "inputs": {
834
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-1.0, 0.0, 1.0] } },
835
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-5.0] } },
836
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [5.0] } }
837
+ },
838
+ "outputs": { "output": { "dtype": "float32", "shape": [3] } },
839
+ "provenance": {
840
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_inbounds",
841
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
842
+ }
843
+ },
844
+ {
845
+ "name": "onnx_backend_clip_min_greater_than_max",
846
+ "inputs": {
847
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-2.0, 0.0, 6.0] } },
848
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [2.0] } },
849
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
850
+ },
851
+ "outputs": { "output": { "dtype": "float32", "shape": [3] } },
852
+ "provenance": {
853
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_min_greater_than_max",
854
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
855
+ }
856
+ },
857
+ {
858
+ "name": "onnx_backend_clip_outbounds",
859
+ "inputs": {
860
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-6.0, 0.0, 6.0] } },
861
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-5.0] } },
862
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [5.0] } }
863
+ },
864
+ "outputs": { "output": { "dtype": "float32", "shape": [3] } },
865
+ "provenance": {
866
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_outbounds",
867
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
868
+ }
869
+ },
870
+ {
871
+ "name": "onnx_backend_clip_splitbounds",
872
+ "inputs": {
873
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [-1.0, 0.0, 6.0] } },
874
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-5.0] } },
875
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [5.0] } }
876
+ },
877
+ "outputs": { "output": { "dtype": "float32", "shape": [3] } },
878
+ "provenance": {
879
+ "source": "cmake/external/onnx/onnx/backend/test/data/node/test_clip_splitbounds",
880
+ "notes": "ONNX Clip min/max bounds are supplied as scalar tensor inputs."
881
+ }
882
+ },
883
+ {
884
+ "name": "vec4_u32_lanes",
885
+ "inputs": {
886
+ "input": {
887
+ "dtype": "uint32",
888
+ "shape": [8],
889
+ "data": { "kind": "values", "values": [0, 1, 5, 7, 100, 200, 201, 250] }
890
+ },
891
+ "min": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [5] } },
892
+ "max": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [200] } }
893
+ },
894
+ "outputs": { "output": { "dtype": "uint32", "shape": [8] } }
895
+ },
896
+ {
897
+ "name": "vec4_f32_nan_passthrough",
898
+ "inputs": {
899
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": ["NaN", 0.2, 5.0, -5.0] } },
900
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [0.0] } },
901
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
902
+ },
903
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0.000001, "allowNaN": true } }
904
+ },
905
+ {
906
+ "name": "vec4_i32_lanes",
907
+ "inputs": {
908
+ "input": {
909
+ "dtype": "int32",
910
+ "shape": [8],
911
+ "data": { "kind": "values", "values": [-2147483648, -100, -50, -1, 0, 49, 50, 2147483647] }
912
+ },
913
+ "min": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [-50] } },
914
+ "max": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [50] } }
915
+ },
916
+ "outputs": { "output": { "dtype": "int32", "shape": [8] } }
917
+ },
918
+ {
919
+ "name": "vec4_u8_lanes",
920
+ "inputs": {
921
+ "input": {
922
+ "dtype": "uint8",
923
+ "shape": [8],
924
+ "data": { "kind": "values", "values": [0, 1, 5, 7, 100, 200, 201, 255] }
925
+ },
926
+ "min": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [5] } },
927
+ "max": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [200] } }
928
+ },
929
+ "outputs": { "output": { "dtype": "uint8", "shape": [8] } }
930
+ },
931
+ {
932
+ "name": "input_bounds_i32_min_greater_than_max",
933
+ "inputs": {
934
+ "input": {
935
+ "dtype": "int32",
936
+ "shape": [6],
937
+ "data": { "kind": "values", "values": [-2147483648, -10, 0, 5, 10, 2147483647] }
938
+ },
939
+ "min": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [5] } },
940
+ "max": { "dtype": "int32", "shape": [], "data": { "kind": "values", "values": [-5] } }
941
+ },
942
+ "outputs": {
943
+ "output": {
944
+ "dtype": "int32",
945
+ "shape": [6],
946
+ "data": { "kind": "values", "values": [-5, -5, -5, -5, -5, -5] },
947
+ "tolerance": 0
948
+ }
949
+ }
950
+ },
951
+ {
952
+ "name": "f32_nan_passthrough_with_input_bounds",
953
+ "inputs": {
954
+ "input": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": ["NaN", -3.0, 0.5, 3.0] } },
955
+ "min": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-1.0] } },
956
+ "max": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [1.0] } }
957
+ },
958
+ "outputs": {
959
+ "output": {
960
+ "dtype": "float32",
961
+ "shape": [4],
962
+ "data": { "kind": "values", "values": ["NaN", -1.0, 0.5, 1.0] },
963
+ "tolerance": 0,
964
+ "allowNaN": true
965
+ }
966
+ }
967
+ },
968
+ {
969
+ "name": "uint8_input_bounds_min_greater_than_max",
970
+ "inputs": {
971
+ "input": { "dtype": "uint8", "shape": [5], "data": { "kind": "values", "values": [0, 50, 100, 150, 255] } },
972
+ "min": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [200] } },
973
+ "max": { "dtype": "uint8", "shape": [], "data": { "kind": "values", "values": [100] } }
974
+ },
975
+ "outputs": {
976
+ "output": {
977
+ "dtype": "uint8",
978
+ "shape": [5],
979
+ "data": { "kind": "values", "values": [100, 100, 100, 100, 100] },
980
+ "tolerance": 0
981
+ }
982
+ }
983
+ }
984
+ ]
985
+ }
build/webgpu/unary-scalar.wgsl.jinja ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% macro flat_tail_open() %}
2
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
3
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
4
+ // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
5
+ // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
6
+ let invocation = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
7
+ let i = invocation;
8
+ if (i >= params.count) {
9
+ return;
10
+ }{% endmacro %}
11
+ {% macro flat_tail_close() %}
12
+ {% endmacro %}
13
+
14
+ // Scalar unary fallback. Each branch retains the operation's numeric hardening,
15
+ // including Payne-Hanek trigonometric range reduction and NaN/overflow guards.
16
+ {% if usesF16 %}
17
+ enable f16;
18
+ {% endif %}
19
+ {{ env.wgsl.resourceDeclarations }}
20
+ {% macro emit_is_nan_f32() %}
21
+ fn is_nan_f32(value: f32) -> bool {
22
+ let bits = bitcast<u32>(value);
23
+ return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
24
+ }{% endmacro %}
25
+ {% if source.op == "clip" and scalar != "i32" and scalar != "u32" %}
26
+ {{ emit_is_nan_f32() }}
27
+ {% endif %}
28
+ {{ flat_tail_open() }}
29
+ {% set clipMinFromInput = source.clipMinFromInput | default(false) %}
30
+ {% set clipMaxFromInput = source.clipMaxFromInput | default(false) %}
31
+ {% set clipMinValue = source.minValue | default(0) %}
32
+ {% set clipMaxValue = source.maxValue | default(0) %}
33
+ {% set clipScalar = scalar | default("f32") %}
34
+ {% if scalar == "i32" or scalar == "u32" %}
35
+ let value = input[i];
36
+ {% if source.boundsFromInput %}
37
+ {% if clipMinFromInput and clipMaxFromInput %}
38
+ output[i] = min(max(value, clipMin[0]), clipMax[0]);
39
+ {% elif clipMinFromInput %}
40
+ output[i] = max(value, clipMin[0]);
41
+ {% else %}
42
+ output[i] = min(value, clipMax[0]);
43
+ {% endif %}
44
+ {% else %}
45
+ output[i] = min(max(value, {{ clipScalar }}({{ clipMinValue }})), {{ clipScalar }}({{ clipMaxValue }}));
46
+ {% endif %}
47
+ {% else %}
48
+ let value = f32(input[i]);
49
+ {% if source.boundsFromInput %}
50
+ {% if clipMinFromInput and clipMaxFromInput %}
51
+ var out = min(max(value, f32(clipMin[0])), f32(clipMax[0]));
52
+ {% elif clipMinFromInput %}
53
+ var out = max(value, f32(clipMin[0]));
54
+ {% else %}
55
+ var out = min(value, f32(clipMax[0]));
56
+ {% endif %}
57
+ {% else %}
58
+ var out = min(max(value, f32({{ clipMinValue }})), f32({{ clipMaxValue }}));
59
+ {% endif %}
60
+ if (is_nan_f32(value)) {
61
+ out = value;
62
+ }
63
+ output[i] = {{ clipScalar }}(out);
64
+ {% endif %}
65
+ {{ flat_tail_close() -}}
66
+ }
build/webgpu/unary-vec4.wgsl.jinja ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Loads and stores vec4<T> (128 bits) while retaining scalar per-component
2
+ // arithmetic, including per-component helper calls for guard-heavy operations.
3
+ {% if usesF16 %}
4
+ enable f16;
5
+ {% endif %}
6
+ {{ env.wgsl.resourceDeclarations }}
7
+
8
+ {% macro emit_is_nan_f32() %}
9
+ fn is_nan_f32(value: f32) -> bool {
10
+ let bits = bitcast<u32>(value);
11
+ return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
12
+ }{% endmacro %}
13
+ {% if (source.op == "clip" and scalar != "i32" and scalar != "u32") or (source.op == "relu" and scalar != "i32") or source.op == "sin" or source.op == "tan" %}
14
+ {{ emit_is_nan_f32() }}
15
+ {% endif %}
16
+ {% if not (source.boundsFromInput | default(false)) and scalar != "i32" and scalar != "u32" %}
17
+ {% set clipMinValue = source.minValue | default(0) %}
18
+ {% set clipMaxValue = source.maxValue | default(0) %}
19
+ fn clip_value(v: f32) -> f32 {
20
+ var out = min(max(v, f32({{ clipMinValue }})), f32({{ clipMaxValue }}));
21
+ if (is_nan_f32(v)) {
22
+ out = v;
23
+ }
24
+ return out;
25
+ }
26
+ {% endif %}
27
+
28
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
29
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
30
+ // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
31
+ // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
32
+ let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
33
+ if (i >= params.count) {
34
+ return;
35
+ }
36
+ let xv = x[i];
37
+ {% set clipMinValue = source.minValue | default(0) %}
38
+ {% set clipMaxValue = source.maxValue | default(0) %}
39
+ {% if source.boundsFromInput | default(false) %}
40
+ {% set clipMinFromInput = source.clipMinFromInput | default(false) %}
41
+ {% set clipMaxFromInput = source.clipMaxFromInput | default(false) %}
42
+ {% if scalar == "i32" or scalar == "u32" %}
43
+ {% if clipMinFromInput and clipMaxFromInput %}
44
+ y[i] = min(max(xv, vec4<{{ scalar }}>(clipMin[0])), vec4<{{ scalar }}>(clipMax[0]));
45
+ {% elif clipMinFromInput %}
46
+ y[i] = max(xv, vec4<{{ scalar }}>(clipMin[0]));
47
+ {% else %}
48
+ y[i] = min(xv, vec4<{{ scalar }}>(clipMax[0]));
49
+ {% endif %}
50
+ {% else %}
51
+ let fv = vec4<f32>(xv);
52
+ {% if clipMinFromInput and clipMaxFromInput %}
53
+ var outv = min(max(fv, vec4<f32>(f32(clipMin[0]))), vec4<f32>(f32(clipMax[0])));
54
+ {% elif clipMinFromInput %}
55
+ var outv = max(fv, vec4<f32>(f32(clipMin[0])));
56
+ {% else %}
57
+ var outv = min(fv, vec4<f32>(f32(clipMax[0])));
58
+ {% endif %}
59
+ if (is_nan_f32(fv.x)) { outv.x = fv.x; }
60
+ if (is_nan_f32(fv.y)) { outv.y = fv.y; }
61
+ if (is_nan_f32(fv.z)) { outv.z = fv.z; }
62
+ if (is_nan_f32(fv.w)) { outv.w = fv.w; }
63
+ y[i] = {{ vectorScalar }}(outv);
64
+ {% endif %}
65
+ {% elif scalar == "i32" or scalar == "u32" %}
66
+ let lower = {{ scalar }}({{ clipMinValue }});
67
+ let upper = {{ scalar }}({{ clipMaxValue }});
68
+ y[i] = min(max(xv, vec4<{{ scalar }}>(lower)), vec4<{{ scalar }}>(upper));
69
+ {% else %}
70
+ let fv = vec4<f32>(xv);
71
+ y[i] = {{ vectorScalar }}(vec4<f32>(clip_value(fv.x), clip_value(fv.y), clip_value(fv.z), clip_value(fv.w)));
72
+ {% endif %}
73
+ }