Xenova HF Staff commited on
Commit
67f32d3
·
verified ·
1 Parent(s): 554ff82

sync 2e7068faf55e

Browse files
README.md CHANGED
@@ -1,3 +1,60 @@
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.Sign
10
+
11
+ `ai.onnx` · standard ONNX operator · ONNX opset ≥ 13
12
+
13
+ ## Description
14
+
15
+ Computes the elementwise sign of the input tensor: outputs `1` for positive values, `-1` for negative values, and `0` where the input is zero. The output has the same shape and type as the input.
16
+
17
+ See the [ONNX `Sign` spec](https://onnx.ai/onnx/operators/onnx__Sign.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 of any numeric type. | required |
24
+
25
+ ## Outputs
26
+
27
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
28
+ | --- | --- | --- | --- | --- | --- | --- |
29
+ | `output` | `output` | `T` | same as `input` | same as `input` | Elementwise sign of the input; same shape and type as the input. | required |
30
+
31
+ ## Type constraints
32
+
33
+ | Variable | Allowed dtypes |
34
+ | --- | --- |
35
+ | `T` | `float32`, `float16`, `int32`, `uint32`, `int16`, `int8`, `uint8` |
36
+
37
+ ## Files
38
+
39
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
40
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
41
+ - [`test.json`](build/webgpu/test.json) — correctness cases
42
+ - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
43
+ - [`unary-scalar.wgsl.jinja`](build/webgpu/unary-scalar.wgsl.jinja)
44
+ - [`unary-vec4.wgsl.jinja`](build/webgpu/unary-vec4.wgsl.jinja)
45
+
46
+ ## Use with `@huggingface/kernels`
47
+
48
+ The loader derives every required output's shape and logical dtype from the manifest contract and this call.
49
+ It then allocates the result tensors automatically.
50
+
51
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
52
+
53
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
54
+
55
+ ```js
56
+ import { getKernel } from "@huggingface/kernels";
57
+
58
+ const kernel = await getKernel("webgpu-kernels/ai.onnx.Sign", { version: 1 });
59
+ const { output } = await kernel({ input: { data: inputData, shape: [] } });
60
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Sign",
3
+ "cases": [
4
+ {
5
+ "name": "1m_f32",
6
+ "inputs": { "input": { "dtype": "float32", "shape": [1048576] } },
7
+ "outputs": { "output": { "dtype": "float32", "shape": [1048576] } }
8
+ },
9
+ {
10
+ "name": "sign-f32-1m-vec4",
11
+ "preset": "smoke",
12
+ "vars": { "dtype": "float32", "count": 1048576 },
13
+ "inputs": { "input": { "shape": [1048576], "dtype": "float32", "dist": "normal", "seed": 7201, "scale": 1 } },
14
+ "outputs": { "output": { "shape": [1048576], "dtype": "float32" } },
15
+ "bench": {
16
+ "primary": true,
17
+ "metrics": [{ "type": "bandwidth", "value": "numel(shapes.input) * dtypeBytes(args.dtype) * 2" }]
18
+ }
19
+ },
20
+ {
21
+ "name": "sign-f32-1m-minus1-scalar-fallback",
22
+ "preset": "smoke",
23
+ "vars": { "dtype": "float32", "count": 1048575 },
24
+ "inputs": { "input": { "shape": [1048575], "dtype": "float32", "dist": "normal", "seed": 7202, "scale": 1 } },
25
+ "outputs": { "output": { "shape": [1048575], "dtype": "float32" } },
26
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "numel(shapes.input) * dtypeBytes(args.dtype) * 2" }] }
27
+ },
28
+ {
29
+ "name": "sign-f16-8m-vec4",
30
+ "preset": "smoke",
31
+ "vars": { "dtype": "float16", "count": 8388608 },
32
+ "inputs": { "input": { "shape": [8388608], "dtype": "float16", "dist": "normal", "seed": 7203, "scale": 1 } },
33
+ "outputs": { "output": { "shape": [8388608], "dtype": "float16" } },
34
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "numel(shapes.input) * dtypeBytes(args.dtype) * 2" }] }
35
+ },
36
+ {
37
+ "name": "sign-f16-8m-minus1-scalar-fallback",
38
+ "preset": "smoke",
39
+ "vars": { "dtype": "float16", "count": 8388607 },
40
+ "inputs": { "input": { "shape": [8388607], "dtype": "float16", "dist": "normal", "seed": 7204, "scale": 1 } },
41
+ "outputs": { "output": { "shape": [8388607], "dtype": "float16" } },
42
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "numel(shapes.input) * dtypeBytes(args.dtype) * 2" }] }
43
+ }
44
+ ]
45
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "ai.onnx",
3
+ "name": "Sign",
4
+ "sinceVersion": 13,
5
+ "description": "Computes the elementwise sign of the input tensor: outputs `1` for positive values, `-1` for negative values, and `0` where the input is zero. The output has the same shape and type as the input.",
6
+ "inputs": [{ "role": "input", "dtype": "T", "description": "Input tensor of any numeric type." }],
7
+ "outputs": [
8
+ {
9
+ "role": "output",
10
+ "dtype": "T",
11
+ "rank": "ranks.input",
12
+ "description": "Elementwise sign of the input; same shape and type as the input.",
13
+ "shape": "shapes.input"
14
+ }
15
+ ],
16
+ "typeConstraints": { "T": ["float32", "float16", "int32", "uint32", "int16", "int8", "uint8"] },
17
+ "args": {
18
+ "input": { "kind": "tensor", "semantic": "input", "role": "input" },
19
+ "output": { "kind": "tensor", "semantic": "output", "role": "output" }
20
+ },
21
+ "tunables": { "WORKGROUP_SIZE": 256 },
22
+ "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"" },
23
+ "variants": [
24
+ {
25
+ "id": "same_layout_vec4",
26
+ "when": ["numel(shapes.input) > 0", "numel(shapes.input) % 4 == 0", "numel(shapes.input) == numel(shapes.output)", "f16Ok(dtypes.T)"],
27
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
28
+ "passes": [
29
+ {
30
+ "id": "main",
31
+ "name": "Sign.vec4",
32
+ "source": { "shader": "unary-vec4.wgsl.jinja", "inputs": { "op": "\"sign\"" } },
33
+ "bindings": [
34
+ {
35
+ "name": "x",
36
+ "arg": "input",
37
+ "semantic": "input",
38
+ "buffer": { "type": "read-only-storage" },
39
+ "elementType": "$vectorScalar"
40
+ },
41
+ {
42
+ "name": "y",
43
+ "arg": "output",
44
+ "semantic": "output",
45
+ "buffer": { "type": "storage" },
46
+ "elementType": "$vectorScalar"
47
+ },
48
+ {
49
+ "name": "params",
50
+ "semantic": "kernel.params",
51
+ "buffer": { "type": "uniform" },
52
+ "struct": {
53
+ "name": "Params",
54
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output) / 4" }]
55
+ }
56
+ }
57
+ ],
58
+ "dispatch": { "threads": "numel(shapes.output) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
59
+ }
60
+ ],
61
+ "priority": 20
62
+ },
63
+ {
64
+ "id": "elementwise",
65
+ "when": "numel(shapes.input) == numel(shapes.output) and (f16Ok(dtypes.T))",
66
+ "passes": [
67
+ {
68
+ "id": "main",
69
+ "name": "Sign",
70
+ "source": { "shader": "unary-scalar.wgsl.jinja", "inputs": { "op": "\"sign\"", "itemsPerInvocation": 4 } },
71
+ "bindings": [
72
+ {
73
+ "name": "input",
74
+ "arg": "input",
75
+ "semantic": "input",
76
+ "buffer": { "type": "read-only-storage" },
77
+ "elementType": "$scalar"
78
+ },
79
+ {
80
+ "name": "output",
81
+ "arg": "output",
82
+ "semantic": "output",
83
+ "buffer": { "type": "storage" },
84
+ "elementType": "$scalar"
85
+ },
86
+ {
87
+ "name": "params",
88
+ "semantic": "kernel.params",
89
+ "buffer": { "type": "uniform" },
90
+ "struct": {
91
+ "name": "Params",
92
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.output)" }]
93
+ }
94
+ }
95
+ ],
96
+ "dispatch": { "threads": "ceilDiv(numel(shapes.output), 4)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
97
+ }
98
+ ]
99
+ }
100
+ ]
101
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ai.onnx.Sign",
3
+ "id": "_ai_onnx_sign_webgpu_6d0a2b1",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "4q8mZQrcY+ViFixjxj3zYSAeXdqwAnfqK6JL0eXfAeU=",
11
+ "manifest.json": "4+AgHdYHHwfbRxAG9bwppknCrSxXYIjvelehzFR1osw=",
12
+ "test.json": "xB83KjUJDVMn8MAFK2+ZMNngy6SCZSwPM0ALPFALosA=",
13
+ "unary-scalar.wgsl.jinja": "dCTx9hbmVW/fySnWmzBzZT9z4Ij9/iG6RFliizgwwxs=",
14
+ "unary-vec4.wgsl.jinja": "h0Y4ksIDPy57eIshgKVikk0FJSpUp/mXgHTtmxmVRNw="
15
+ }
16
+ },
17
+ "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
18
+ "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Sign" }
19
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Sign",
3
+ "cases": [
4
+ {
5
+ "name": "int16_scalar_boundaries",
6
+ "inputs": {
7
+ "input": { "dtype": "int16", "shape": [5], "data": { "kind": "values", "values": [-32768, -1, 0, 1, 32767] } }
8
+ },
9
+ "outputs": {
10
+ "output": {
11
+ "dtype": "int16",
12
+ "shape": [5],
13
+ "tolerance": 0,
14
+ "data": { "kind": "values", "values": [-1, -1, 0, 1, 1] }
15
+ }
16
+ }
17
+ },
18
+ {
19
+ "name": "f32_signed_subnormal_sign_gpu_gap",
20
+ "skipGpu": {
21
+ "category": "permanent",
22
+ "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."
23
+ },
24
+ "provenance": {
25
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
26
+ "test": "MathOpTest.Sign_float",
27
+ "notes": "Signed subnormal values have nonzero signs; flushing them to zero incorrectly returns 0."
28
+ },
29
+ "inputs": {
30
+ "input": {
31
+ "dtype": "float32",
32
+ "shape": [5],
33
+ "data": { "kind": "values", "values": [-1e-39, -1e-40, 0.0, 1e-40, 1e-39] }
34
+ }
35
+ },
36
+ "outputs": { "output": { "dtype": "float32", "shape": [5], "tolerance": 0 } }
37
+ },
38
+ {
39
+ "name": "f32_signed_subnormal_sign_vec4_gpu_gap",
40
+ "skipGpu": {
41
+ "category": "permanent",
42
+ "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."
43
+ },
44
+ "provenance": {
45
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
46
+ "test": "MathOpTest.Sign_float",
47
+ "notes": "Vec4 companion for signed subnormal Sign behavior; optimized unary paths must preserve nonzero signs."
48
+ },
49
+ "inputs": {
50
+ "input": {
51
+ "dtype": "float32",
52
+ "shape": [4],
53
+ "data": { "kind": "values", "values": [-1e-40, 0.0, 1e-40, 1e-39] }
54
+ }
55
+ },
56
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0 } }
57
+ },
58
+ {
59
+ "name": "f32_values",
60
+ "provenance": { "source": "onnxruntime/test/providers/cpu/math/sign_test.cc", "test": "MathOpTest.Sign_float" },
61
+ "inputs": {
62
+ "input": {
63
+ "dtype": "float32",
64
+ "shape": [5],
65
+ "data": { "kind": "values", "values": [-2.0, -0.1, 0.0, 0.1, 2.0] }
66
+ }
67
+ },
68
+ "outputs": { "output": { "dtype": "float32", "shape": [5] } }
69
+ },
70
+ {
71
+ "name": "f32_zero_and_infinities",
72
+ "inputs": {
73
+ "input": {
74
+ "dtype": "float32",
75
+ "shape": [6],
76
+ "data": { "kind": "values", "values": ["-Infinity", 0.0, 0.0, 1e-7, -1e-7, "Infinity"] }
77
+ }
78
+ },
79
+ "outputs": { "output": { "dtype": "float32", "shape": [6], "tolerance": 0 } }
80
+ },
81
+ {
82
+ "name": "f16_values",
83
+ "provenance": {
84
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
85
+ "test": "MathOpTest.Sign_MLFloat16"
86
+ },
87
+ "inputs": {
88
+ "input": {
89
+ "dtype": "float16",
90
+ "shape": [6],
91
+ "data": { "kind": "values", "values": [-4.0, -0.5, 0.0, 0.0, 0.5, 4.0] }
92
+ }
93
+ },
94
+ "outputs": { "output": { "dtype": "float16", "shape": [6], "tolerance": 0 } }
95
+ },
96
+ {
97
+ "name": "ort_int32_sequence",
98
+ "provenance": { "source": "onnxruntime/test/providers/cpu/math/sign_test.cc", "test": "MathOpTest.Sign_int64" },
99
+ "inputs": {
100
+ "input": { "dtype": "int32", "shape": [7], "data": { "kind": "values", "values": [-5, -4, -3, -2, -1, 0, 1] } }
101
+ },
102
+ "outputs": { "output": { "dtype": "int32", "shape": [7], "tolerance": 0 } }
103
+ },
104
+ {
105
+ "name": "ort_uint32_sequence",
106
+ "provenance": { "source": "onnxruntime/test/providers/cpu/math/sign_test.cc", "test": "MathOpTest.Sign_uint64" },
107
+ "inputs": {
108
+ "input": { "dtype": "uint32", "shape": [7], "data": { "kind": "values", "values": [0, 1, 2, 3, 4, 5, 6] } }
109
+ },
110
+ "outputs": { "output": { "dtype": "uint32", "shape": [7], "tolerance": 0 } }
111
+ },
112
+ {
113
+ "name": "ort_int8_sequence",
114
+ "provenance": {
115
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
116
+ "test": "MathOpTest.Sign_int64",
117
+ "notes": "Uses the same signed integer sequence semantics as MathOpTest.Sign_int64 with logical int8 storage."
118
+ },
119
+ "inputs": {
120
+ "input": { "dtype": "int8", "shape": [7], "data": { "kind": "values", "values": [-5, -4, -3, -2, -1, 0, 1] } }
121
+ },
122
+ "outputs": { "output": { "dtype": "int8", "shape": [7], "tolerance": 0 } }
123
+ },
124
+ {
125
+ "name": "ort_uint8_sequence",
126
+ "provenance": {
127
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
128
+ "test": "MathOpTest.Sign_uint64",
129
+ "notes": "Uses the same unsigned integer sequence semantics as MathOpTest.Sign_uint64 with logical uint8 storage."
130
+ },
131
+ "inputs": {
132
+ "input": { "dtype": "uint8", "shape": [7], "data": { "kind": "values", "values": [0, 1, 2, 3, 4, 5, 6] } }
133
+ },
134
+ "outputs": { "output": { "dtype": "uint8", "shape": [7], "tolerance": 0 } }
135
+ },
136
+ {
137
+ "name": "onnx_backend_float32_range",
138
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_sign" },
139
+ "inputs": {
140
+ "input": {
141
+ "dtype": "float32",
142
+ "shape": [11],
143
+ "data": { "kind": "values", "values": [-5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0] }
144
+ }
145
+ },
146
+ "outputs": { "output": { "dtype": "float32", "shape": [11], "tolerance": 0 } }
147
+ },
148
+ {
149
+ "name": "onnx_backend_sign",
150
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_sign" },
151
+ "inputs": {
152
+ "input": {
153
+ "dtype": "float32",
154
+ "shape": [11],
155
+ "data": { "kind": "values", "values": [-5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0] }
156
+ }
157
+ },
158
+ "outputs": { "output": { "dtype": "float32", "shape": [11], "tolerance": 0.00001 } }
159
+ },
160
+ {
161
+ "name": "vec4_f32_lanes",
162
+ "inputs": {
163
+ "input": {
164
+ "dtype": "float32",
165
+ "shape": [8],
166
+ "data": { "kind": "values", "values": [-5.0, -0.1, 0.0, 0.1, 5.0, "-Infinity", "Infinity", 2.0] }
167
+ }
168
+ },
169
+ "outputs": { "output": { "dtype": "float32", "shape": [8], "tolerance": 0 } }
170
+ },
171
+ {
172
+ "name": "vec4_f16_lanes",
173
+ "inputs": {
174
+ "input": {
175
+ "dtype": "float16",
176
+ "shape": [8],
177
+ "data": { "kind": "values", "values": [-4.0, -0.5, 0.0, 0.0, 0.5, 4.0, -2.0, 2.0] }
178
+ }
179
+ },
180
+ "outputs": { "output": { "dtype": "float16", "shape": [8], "tolerance": 0 } }
181
+ },
182
+ {
183
+ "name": "vec4_i32_lanes",
184
+ "inputs": {
185
+ "input": { "dtype": "int32", "shape": [8], "data": { "kind": "values", "values": [-5, -1, 0, 1, 5, -2, 2, 0] } }
186
+ },
187
+ "outputs": { "output": { "dtype": "int32", "shape": [8] } }
188
+ },
189
+ {
190
+ "name": "vec4_u32_lanes",
191
+ "inputs": {
192
+ "input": { "dtype": "uint32", "shape": [8], "data": { "kind": "values", "values": [0, 1, 2, 3, 4, 5, 0, 7] } }
193
+ },
194
+ "outputs": { "output": { "dtype": "uint32", "shape": [8] } }
195
+ },
196
+ {
197
+ "name": "vec4_i8_lanes",
198
+ "inputs": {
199
+ "input": {
200
+ "dtype": "int8",
201
+ "shape": [8],
202
+ "data": { "kind": "values", "values": [-128, -5, -1, 0, 1, 5, 100, 127] }
203
+ }
204
+ },
205
+ "outputs": { "output": { "dtype": "int8", "shape": [8] } }
206
+ },
207
+ {
208
+ "name": "vec4_u8_lanes",
209
+ "inputs": {
210
+ "input": {
211
+ "dtype": "uint8",
212
+ "shape": [8],
213
+ "data": { "kind": "values", "values": [0, 1, 2, 3, 128, 200, 254, 255] }
214
+ }
215
+ },
216
+ "outputs": { "output": { "dtype": "uint8", "shape": [8] } }
217
+ },
218
+ {
219
+ "name": "ort_float_nan_preserved",
220
+ "provenance": {
221
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
222
+ "test": "MathOpTest.Sign_float",
223
+ "notes": "Extends the ORT float Sign coverage with ORT CPU-verified NaN preservation."
224
+ },
225
+ "inputs": {
226
+ "input": {
227
+ "dtype": "float32",
228
+ "shape": [4],
229
+ "data": { "kind": "values", "values": ["NaN", "-Infinity", 0.0, "Infinity"] }
230
+ }
231
+ },
232
+ "outputs": { "output": { "dtype": "float32", "shape": [4], "tolerance": 0, "allowNaN": true } }
233
+ },
234
+ {
235
+ "name": "ort_float_nan_preserved_scalar",
236
+ "provenance": {
237
+ "source": "onnxruntime/test/providers/cpu/math/sign_test.cc",
238
+ "test": "MathOpTest.Sign_float",
239
+ "notes": "Scalar-path companion for ORT CPU-verified NaN preservation."
240
+ },
241
+ "inputs": {
242
+ "input": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": ["NaN", -2.0, 2.0] } }
243
+ },
244
+ "outputs": { "output": { "dtype": "float32", "shape": [3], "tolerance": 0, "allowNaN": true } }
245
+ },
246
+ {
247
+ "name": "empty_input_zero_dim",
248
+ "inputs": { "input": { "dtype": "float32", "shape": [0], "data": { "kind": "values", "values": [] } } },
249
+ "outputs": { "output": { "dtype": "float32", "shape": [0], "tolerance": 0 } }
250
+ },
251
+ {
252
+ "name": "f32_rank0_scalar",
253
+ "inputs": { "input": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-3.5] } } },
254
+ "outputs": { "output": { "dtype": "float32", "shape": [], "tolerance": 0 } }
255
+ },
256
+ {
257
+ "name": "f32_rank2_3x5",
258
+ "inputs": {
259
+ "input": {
260
+ "dtype": "float32",
261
+ "shape": [3, 5],
262
+ "data": {
263
+ "kind": "values",
264
+ "values": [-7.0, -0.5, 0.0, 0.5, 7.0, -1e-7, 1e-7, -100.0, 100.0, 0.0, -2.0, -1.0, 1.0, 2.0, 3.0]
265
+ }
266
+ }
267
+ },
268
+ "outputs": { "output": { "dtype": "float32", "shape": [3, 5], "tolerance": 0 } }
269
+ },
270
+ {
271
+ "name": "f16_rank2_realistic",
272
+ "inputs": {
273
+ "input": {
274
+ "dtype": "float16",
275
+ "shape": [4, 16],
276
+ "data": {
277
+ "kind": "values",
278
+ "values": [-8.0, -4.0, -2.0, -1.0, -0.5, -0.25, -0.125, -0.0625, 0.0, 0.0625, 0.125, 0.25, 0.5, 1.0, 2.0, 4.0, -7.5, -3.5, -1.5, -0.75, -0.375, -0.1875, -0.09375, -0.046875, 0.046875, 0.09375, 0.1875, 0.375, 0.75, 1.5, 3.5, 7.5, 0.0, 0.0, -6.0, 6.0, -5.0, 5.0, -3.0, 3.0, -2.5, 2.5, -1.25, 1.25, -0.625, 0.625, -0.3125, 0.3125, 8.0, -8.0, 4.0, -4.0, 2.0, -2.0, 1.0, -1.0, 0.5, -0.5, 0.25, -0.25, 0.125, -0.125, 0.0625, -0.0625]
279
+ }
280
+ }
281
+ },
282
+ "outputs": { "output": { "dtype": "float16", "shape": [4, 16], "tolerance": 0 } }
283
+ },
284
+ {
285
+ "name": "f16_nan_preserved_scalar_path",
286
+ "inputs": {
287
+ "input": {
288
+ "dtype": "float16",
289
+ "shape": [5],
290
+ "data": { "kind": "values", "values": ["NaN", -2.0, 0.0, 2.0, "NaN"] }
291
+ }
292
+ },
293
+ "outputs": { "output": { "dtype": "float16", "shape": [5], "tolerance": 0, "allowNaN": true } }
294
+ },
295
+ {
296
+ "name": "int8_min_value_sign_scalar_path",
297
+ "inputs": {
298
+ "input": { "dtype": "int8", "shape": [5], "data": { "kind": "values", "values": [-128, -1, 0, 1, 127] } }
299
+ },
300
+ "outputs": { "output": { "dtype": "int8", "shape": [5], "tolerance": 0 } }
301
+ },
302
+ {
303
+ "name": "f32_scalar_tail_4095",
304
+ "provenance": {
305
+ "notes": "Compact sibling for the large f32 Sign scalar-fallback benchmark; odd numel forces the elementwise path while preserving exact sign semantics."
306
+ },
307
+ "inputs": {
308
+ "input": {
309
+ "dtype": "float32",
310
+ "shape": [4095],
311
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.027, "scale": 3.0 }
312
+ }
313
+ },
314
+ "outputs": { "output": { "dtype": "float32", "shape": [4095], "tolerance": 0 } }
315
+ }
316
+ ]
317
+ }
build/webgpu/unary-scalar.wgsl.jinja ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ // Tail-safe scalar x4 keeps vector-like dispatch density without requiring
8
+ // the logical tensor length (or its storage binding) to be vec4 aligned.
9
+ let begin = invocation * {{ source.itemsPerInvocation }}u;
10
+ let end = min(begin + {{ source.itemsPerInvocation }}u, params.count);
11
+ for (var i = begin; i < end; i = i + 1u) {
12
+ {%- endmacro %}
13
+ {% macro flat_tail_close() %}
14
+ }
15
+ {% endmacro %}
16
+
17
+ // Scalar unary fallback. Each branch retains the operation's numeric hardening,
18
+ // including Payne-Hanek trigonometric range reduction and NaN/overflow guards.
19
+ {% if usesF16 %}
20
+ enable f16;
21
+ {% endif %}
22
+ {{ env.wgsl.resourceDeclarations }}
23
+ {{ flat_tail_open() }}
24
+ {% if scalar == "i32" %}
25
+ let v = input[i];
26
+ output[i] = select(select(0i, 1i, v > 0i), -1i, v < 0i);
27
+ {% elif scalar == "u32" %}
28
+ let v = input[i];
29
+ output[i] = select(0u, 1u, v > 0u);
30
+ {% else %}
31
+ let v = f32(input[i]);
32
+ // ONNX Sign preserves NaN. A bitcast test cannot be folded away by no-NaN
33
+ // assumptions; it isolates NaN, whose ordered comparisons would collapse to 0.
34
+ let v_bits = bitcast<u32>(v);
35
+ let v_is_nan = (v_bits & 0x7f800000u) == 0x7f800000u && (v_bits & 0x007fffffu) != 0u;
36
+ var s = select(select(0.0, 1.0, v > 0.0), -1.0, v < 0.0);
37
+ if (v_is_nan) { s = v; }
38
+ output[i] = {{ scalar }}(s);
39
+ {% endif %}
40
+ {{ flat_tail_close() -}}
41
+ }
build/webgpu/unary-vec4.wgsl.jinja ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
9
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
10
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
11
+ // 2D-folded flat index: gid.y carries the high bits when the element count exceeds the
12
+ // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills the rest into y).
13
+ let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
14
+ if (i >= params.count) {
15
+ return;
16
+ }
17
+ let xv = x[i];
18
+ {% if scalar == "i32" %}
19
+ y[i] = select(select(vec4<i32>(0i), vec4<i32>(1i), xv > vec4<i32>(0i)), vec4<i32>(-1i), xv < vec4<i32>(0i));
20
+ {% elif scalar == "u32" %}
21
+ y[i] = select(vec4<u32>(0u), vec4<u32>(1u), xv > vec4<u32>(0u));
22
+ {% else %}
23
+ let fv = vec4<f32>(xv);
24
+ // ONNX Sign preserves NaN. A bitcast test cannot be folded away by no-NaN
25
+ // assumptions; it isolates lanes whose ordered comparisons would collapse to 0.
26
+ let fv_bits = bitcast<vec4<u32>>(fv);
27
+ let fv_is_nan = ((fv_bits & vec4<u32>(0x7f800000u)) == vec4<u32>(0x7f800000u)) & ((fv_bits & vec4<u32>(0x007fffffu)) != vec4<u32>(0u));
28
+ let signed = select(select(vec4<f32>(0.0), vec4<f32>(1.0), fv > vec4<f32>(0.0)), vec4<f32>(-1.0), fv < vec4<f32>(0.0));
29
+ y[i] = {{ vectorScalar }}(select(signed, fv, fv_is_nan));
30
+ {% endif %}
31
+ }