Xenova HF Staff commited on
Commit
61893fc
·
verified ·
1 Parent(s): b1ec92f

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,17 +18,17 @@ See the [standard ONNX `If` spec](https://onnx.ai/onnx/operators/onnx__If.html)
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
- | --- | --- | --- | --- | --- | --- | --- |
23
- | `cond` | `cond` | `B` | — | — | Scalar boolean condition that selects which value tensor to output. | required |
24
- | `then_value` | `then_value` | `T` | — | — | Values to output when `cond` is true. | required |
25
- | `else_value` | `else_value` | `T` | — | — | Values to output when `cond` is false. | required |
26
 
27
  ## Outputs
28
 
29
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
30
- | --- | --- | --- | --- | --- | --- | --- |
31
- | `y` | `y` | `T` | — | — | Output tensor with the same number of elements as `then_value` and `else_value`. | required |
32
 
33
  ## Type constraints
34
 
@@ -39,7 +39,7 @@ See the [standard ONNX `If` spec](https://onnx.ai/onnx/operators/onnx__If.html)
39
 
40
  ## Files
41
 
42
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
43
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
44
  - [`test.json`](build/webgpu/test.json) — correctness cases
45
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -47,15 +47,18 @@ See the [standard ONNX `If` spec](https://onnx.ai/onnx/operators/onnx__If.html)
47
 
48
  ## Use with `@huggingface/kernels`
49
 
50
- The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
 
 
51
 
52
- The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
53
 
54
- - `y`
55
 
56
- Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
57
 
58
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
59
 
60
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
61
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | `cond` | `B` | — | — | Scalar boolean condition that selects which value tensor to output. | required |
24
+ | `then_value` | `T` | — | — | Values to output when `cond` is true. | required |
25
+ | `else_value` | `T` | — | — | Values to output when `cond` is false. | required |
26
 
27
  ## Outputs
28
 
29
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
30
+ | --- | --- | --- | --- | --- | --- |
31
+ | `y` | `T` | — | — | Output tensor with the same number of elements as `then_value` and `else_value`. | required |
32
 
33
  ## Type constraints
34
 
 
39
 
40
  ## Files
41
 
42
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
43
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
44
  - [`test.json`](build/webgpu/test.json) — correctness cases
45
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
47
 
48
  ## Use with `@huggingface/kernels`
49
 
50
+ ```sh
51
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
52
+ ```
53
 
54
+ Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
55
 
56
+ This example supplies explicit metadata for:
57
 
58
+ - `y`
59
 
60
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
61
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
62
 
63
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
64
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.If",
3
  "cases": [
4
  {
5
  "name": "lowered_select_1m",
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "lowered_select_1m",
build/webgpu/if-select.wgsl.jinja CHANGED
@@ -1,30 +1,30 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
@@ -34,7 +34,7 @@
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
37
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
38
  {{ flat_index_2d(guardInline=true) }}
39
  y[i] = select(else_value[i], then_value[i], cond[0] != 0u);
40
  }
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
37
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
38
  {{ flat_index_2d(guardInline=true) }}
39
  y[i] = select(else_value[i], then_value[i], cond[0] != 0u);
40
  }
build/webgpu/manifest.json CHANGED
@@ -3,31 +3,10 @@
3
  "name": "If",
4
  "conformance": "internal-lowering",
5
  "sinceVersion": 25,
6
- "description": "Support status: the standard ONNX `If` control-flow operator is not implemented because standalone kernel packages cannot carry or execute its `then_branch` and `else_branch` graph attributes. This internal lowering only selects elementwise between two pre-evaluated, equal-sized tensors from a scalar condition and must not be treated as ONNX `If`.",
7
- "inputs": [
8
- {
9
- "role": "cond",
10
- "dtype": "B",
11
- "description": "Scalar boolean condition that selects which value tensor to output."
12
- },
13
- { "role": "then_value", "dtype": "T", "description": "Values to output when `cond` is true." },
14
- { "role": "else_value", "dtype": "T", "description": "Values to output when `cond` is false." }
15
- ],
16
- "outputs": [
17
- {
18
- "role": "y",
19
- "dtype": "T",
20
- "description": "Output tensor with the same number of elements as `then_value` and `else_value`."
21
- }
22
- ],
23
  "typeConstraints": { "T": ["float32"], "B": ["uint32", "bool"] },
24
- "args": {
25
- "cond": { "kind": "tensor", "semantic": "cond", "role": "input" },
26
- "then_value": { "kind": "tensor", "semantic": "then_value", "role": "input" },
27
- "else_value": { "kind": "tensor", "semantic": "else_value", "role": "input" },
28
- "y": { "kind": "tensor", "semantic": "y", "role": "output" }
29
- },
30
- "tunables": { "WORKGROUP_SIZE": 256 },
31
  "variants": [
32
  {
33
  "id": "lowered_select",
@@ -38,37 +17,17 @@
38
  "name": "If",
39
  "shader": "if-select.wgsl.jinja",
40
  "bindings": [
41
- {
42
- "name": "cond",
43
- "arg": "cond",
44
- "semantic": "cond",
45
- "buffer": { "type": "read-only-storage" },
46
- "elementType": "u32",
47
- "length": 1
48
- },
49
- {
50
- "name": "then_value",
51
- "arg": "then_value",
52
- "semantic": "then_value",
53
- "buffer": { "type": "read-only-storage" },
54
- "elementType": "f32"
55
- },
56
- {
57
- "name": "else_value",
58
- "arg": "else_value",
59
- "semantic": "else_value",
60
- "buffer": { "type": "read-only-storage" },
61
- "elementType": "f32"
62
- },
63
- { "name": "y", "arg": "y", "semantic": "y", "buffer": { "type": "storage" }, "elementType": "f32" },
64
- {
65
- "name": "params",
66
- "semantic": "kernel.params",
67
- "buffer": { "type": "uniform" },
68
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
69
- }
70
  ],
71
- "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
72
  }
73
  ]
74
  }
 
3
  "name": "If",
4
  "conformance": "internal-lowering",
5
  "sinceVersion": 25,
6
+ "inputs": { "cond": { "dtype": "B" }, "then_value": { "dtype": "T" }, "else_value": { "dtype": "T" } },
7
+ "outputs": { "y": { "dtype": "T" } },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  "typeConstraints": { "T": ["float32"], "B": ["uint32", "bool"] },
9
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
 
 
 
 
 
 
10
  "variants": [
11
  {
12
  "id": "lowered_select",
 
17
  "name": "If",
18
  "shader": "if-select.wgsl.jinja",
19
  "bindings": [
20
+ { "arg": "cond", "elementType": "u32", "length": 1 },
21
+ { "arg": "then_value", "elementType": "f32" },
22
+ { "arg": "else_value", "elementType": "f32" },
23
+ { "arg": "y", "elementType": "f32" },
24
+ { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  ],
26
+ "dispatch": {
27
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
28
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
29
+ "z": 1
30
+ }
31
  }
32
  ]
33
  }
build/webgpu/metadata.json CHANGED
@@ -1,18 +1,18 @@
1
  {
2
  "name": "ai.onnx.If",
3
- "id": "_ai_onnx_if_webgpu_2b93a6b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "aVzy6O2BXjbtp7QD6jSgMGQnCFr9rv3ErzC9HuwA6gE=",
11
- "if-select.wgsl.jinja": "SOCO3HJ0GVfXYEOn7wNTEDBq94rDoQ5sGuijBo/4u6Y=",
12
- "manifest.json": "qnXbI/F6Tz6YY42S8aJWiA5scMh+fR/ZkPTGfwzYYcc=",
13
- "test.json": "wPMNj7icFakVJ6lHXUZxozdTSnpyTakK1is0VuwCvzE="
14
  }
15
  },
16
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
17
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.If" }
18
  }
 
1
  {
2
  "name": "ai.onnx.If",
3
+ "id": "_ai_onnx_if_webgpu_fc82927",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "pKYDQ5YSgA8pWav7C7GCSLohOpQ9qIZlwV+NNK8NSic=",
11
+ "if-select.wgsl.jinja": "ywcv63EICuDTOALNqhfHgflu7LBg2gMSAG9rVDWXZNE=",
12
+ "manifest.json": "O5az6AZ2qds85ksKnrtYq+QUiS3/b7AbAZG0hzJ83q4=",
13
+ "test.json": "S8aT5V28HOrUh8vxW+Is0BVgTE78h+8AUypNHxDJdUA="
14
  }
15
  },
16
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
17
+ "webgpu": { "manifestSpec": "2.0", "variants": { "lowered_select": ["if-select.wgsl.jinja"] } }
18
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.If",
3
  "cases": [
4
  {
5
  "name": "lowered_dispatch_cliff_select_then",
@@ -41,7 +40,7 @@
41
  "provenance": {
42
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
43
  "test": "If.ShapeInMainGraph_NoShapeInSubgraph_True",
44
- "notes": "Projection onto the framework's lowered select variant: ORT's then branch computes split_out_0 + if_graph_input_0 = 2."
45
  },
46
  "inputs": {
47
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
@@ -55,7 +54,7 @@
55
  "provenance": {
56
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
57
  "test": "If.ShapeInMainGraph_NoShapeInSubgraph_False",
58
- "notes": "Projection onto the framework's lowered select variant: ORT's else branch computes split_out_1 + if_graph_input_0 = 11."
59
  },
60
  "inputs": {
61
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
@@ -69,7 +68,7 @@
69
  "provenance": {
70
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
71
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ThenBranchExecution",
72
- "notes": "Projection onto the framework's lowered select variant: branch subgraphs are represented by precomputed branch tensors."
73
  },
74
  "inputs": {
75
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
@@ -83,7 +82,7 @@
83
  "provenance": {
84
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
85
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ElseBranchExecution",
86
- "notes": "Projection onto the framework's lowered select variant: branch subgraphs are represented by precomputed branch tensors."
87
  },
88
  "inputs": {
89
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
@@ -97,7 +96,7 @@
97
  "provenance": {
98
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
99
  "test": "If.Opset11ThenAndElseBranchesProduceDifferentOutputShapes",
100
- "notes": "Projection onto the framework's lowered select variant: branch tensors use different ranks with equal storage size, and the selected then branch fixes the output shape."
101
  },
102
  "inputs": {
103
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
@@ -119,7 +118,7 @@
119
  "provenance": {
120
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
121
  "test": "If.Opset11ThenAndElseBranchesProduceDifferentOutputShapes",
122
- "notes": "Projection onto the framework's lowered select variant: branch tensors use different ranks with equal storage size, and the selected else branch fixes the output shape."
123
  },
124
  "inputs": {
125
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
@@ -150,7 +149,7 @@
150
  "provenance": {
151
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
152
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ThenBranchExecution",
153
- "notes": "ONNX If condition is a scalar bool. The project-lowered select form should accept scalar logical uint32 conditions as well as [1] conditions."
154
  },
155
  "inputs": {
156
  "cond": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [1] } },
@@ -196,7 +195,7 @@
196
  "provenance": {
197
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
198
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ElseBranchExecution",
199
- "notes": "Length-1 logical bool condition companion for the lowered select form; false must select the else tensor exactly."
200
  },
201
  "inputs": {
202
  "cond": { "dtype": "bool", "shape": [1], "data": { "kind": "values", "values": [0] } },
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "lowered_dispatch_cliff_select_then",
 
40
  "provenance": {
41
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
42
  "test": "If.ShapeInMainGraph_NoShapeInSubgraph_True",
43
+ "notes": "The internal lowered-select variant receives precomputed branch values; the selected then value is 2."
44
  },
45
  "inputs": {
46
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
 
54
  "provenance": {
55
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
56
  "test": "If.ShapeInMainGraph_NoShapeInSubgraph_False",
57
+ "notes": "The internal lowered-select variant receives precomputed branch values; the selected else value is 11."
58
  },
59
  "inputs": {
60
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
 
68
  "provenance": {
69
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
70
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ThenBranchExecution",
71
+ "notes": "The internal lowered-select variant receives precomputed branch tensors and selects the then value."
72
  },
73
  "inputs": {
74
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
 
82
  "provenance": {
83
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
84
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ElseBranchExecution",
85
+ "notes": "The internal lowered-select variant receives precomputed branch tensors and selects the else value."
86
  },
87
  "inputs": {
88
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
 
96
  "provenance": {
97
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
98
  "test": "If.Opset11ThenAndElseBranchesProduceDifferentOutputShapes",
99
+ "notes": "Precomputed branch tensors have different ranks but equal storage size; selecting the then tensor determines the output shape."
100
  },
101
  "inputs": {
102
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
 
118
  "provenance": {
119
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
120
  "test": "If.Opset11ThenAndElseBranchesProduceDifferentOutputShapes",
121
+ "notes": "Precomputed branch tensors have different ranks but equal storage size; selecting the else tensor determines the output shape."
122
  },
123
  "inputs": {
124
  "cond": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
 
149
  "provenance": {
150
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
151
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ThenBranchExecution",
152
+ "notes": "The fixed-branch select lowering accepts a scalar logical uint32 condition and chooses the complete two-element then value."
153
  },
154
  "inputs": {
155
  "cond": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [1] } },
 
195
  "provenance": {
196
  "source": "onnxruntime/test/providers/cpu/controlflow/if_test.cc",
197
  "test": "If.ConditionalBranchesOnlyContainConstantNodes_ElseBranchExecution",
198
+ "notes": "A length-one logical bool condition set to false must select the else tensor exactly in the lowered-select form."
199
  },
200
  "inputs": {
201
  "cond": { "dtype": "bool", "shape": [1], "data": { "kind": "values", "values": [0] } },