Xenova HF Staff commited on
Commit
5e1310a
·
verified ·
1 Parent(s): cc98ec1

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,19 +18,19 @@ See the [standard ONNX `Loop` spec](https://onnx.ai/onnx/operators/onnx__Loop.ht
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `M` | `m` | `I` | — | — | Required uint32 trip-count limit encoded as either a scalar tensor or a one-element rank-1 tensor. The lowering executes at most `min(M, scan_output.shape[0])` iterations. | required |
24
- | `cond` | `cond` | `B` | — | — | Required initial condition encoded as either a scalar tensor or a one-element rank-1 tensor. A false value executes zero iterations; a true value permits all iterations selected by `M`. This fixed lowering does not update the condition inside the loop. | required |
25
- | `v_initial` | `v_initial` | `T` | `1` | — | Initial rank-1 loop-carried state of shape `[dim]`. | required |
26
- | `step` | `step` | `T` | `1` | — | Implementation-specific rank-1 increment of shape `[dim]`, added elementwise to the state on every executed iteration. This is not a standard ONNX `Loop` input. | required |
27
 
28
  ## Outputs
29
 
30
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
31
- | --- | --- | --- | --- | --- | --- | --- |
32
- | `v_final` | `v_final` | `T` | `1` | same as `v_initial` | Final rank-1 state of shape `[dim]` after the executed additions. | required |
33
- | `scan_output` | `scan_output` | `T` | `2` | — | Dense tensor of shape `[scan_steps, dim]`. Each executed row contains the updated state for that iteration; rows beyond the executed trip count are zero-filled. | required |
34
 
35
  ## Type constraints
36
 
@@ -42,7 +42,7 @@ See the [standard ONNX `Loop` spec](https://onnx.ai/onnx/operators/onnx__Loop.ht
42
 
43
  ## Files
44
 
45
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
46
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
47
  - [`test.json`](build/webgpu/test.json) — correctness cases
48
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -50,15 +50,18 @@ See the [standard ONNX `Loop` spec](https://onnx.ai/onnx/operators/onnx__Loop.ht
50
 
51
  ## Use with `@huggingface/kernels`
52
 
53
- The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
 
 
 
 
54
 
55
- The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
56
 
57
  - `scan_output`
58
 
59
- Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
60
-
61
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
62
 
63
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
64
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `m` | `M` | `I` | — | — | Required uint32 trip-count limit encoded as either a scalar tensor or a one-element rank-1 tensor. The lowering executes at most `min(M, scan_output.shape[0])` iterations. | required |
24
+ | `cond` | | `B` | — | — | Required initial condition encoded as either a scalar tensor or a one-element rank-1 tensor. A false value executes zero iterations; a true value permits all iterations selected by `M`. This fixed lowering does not update the condition inside the loop. | required |
25
+ | `v_initial` | | `T` | `1` | — | Initial rank-1 loop-carried state of shape `[dim]`. | required |
26
+ | `step` | | `T` | `1` | — | Implementation-specific rank-1 increment of shape `[dim]`, added elementwise to the state on every executed iteration. This is not a standard ONNX `Loop` input. | required |
27
 
28
  ## Outputs
29
 
30
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
31
+ | --- | --- | --- | --- | --- | --- |
32
+ | `v_final` | `T` | `1` | same as `v_initial` | Final rank-1 state of shape `[dim]` after the executed additions. | required |
33
+ | `scan_output` | `T` | `2` | — | Dense tensor of shape `[scan_steps, dim]`. Each executed row contains the updated state for that iteration; rows beyond the executed trip count are zero-filled. | required |
34
 
35
  ## Type constraints
36
 
 
42
 
43
  ## Files
44
 
45
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
46
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
47
  - [`test.json`](build/webgpu/test.json) — correctness cases
48
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
50
 
51
  ## Use with `@huggingface/kernels`
52
 
53
+ ```sh
54
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
55
+ ```
56
+
57
+ 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.
58
 
59
+ This example supplies explicit metadata for:
60
 
61
  - `scan_output`
62
 
 
 
63
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
64
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
65
 
66
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
67
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Loop",
3
  "cases": [
4
  {
5
  "name": "lowered_HEALTHY_baseline_dim128_steps128",
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "lowered_HEALTHY_baseline_dim128_steps128",
build/webgpu/manifest.json CHANGED
@@ -3,59 +3,21 @@
3
  "name": "Loop",
4
  "conformance": "internal-lowering",
5
  "sinceVersion": 25,
6
- "description": "Support status: the standard ONNX `Loop` control-flow operator is not implemented because standalone kernel packages cannot carry or execute its body graph. This internal lowering performs one fixed recurrence—adding `step` to a rank-1 loop-carried tensor for up to `M` iterations while writing a dense scan tensor; `step` is not an ONNX `Loop` input, and this lowering must not be treated as ONNX `Loop`.",
7
- "inputs": [
8
- {
9
- "role": "M",
10
- "dtype": "I",
11
- "description": "Required uint32 trip-count limit encoded as either a scalar tensor or a one-element rank-1 tensor. The lowering executes at most `min(M, scan_output.shape[0])` iterations."
12
- },
13
- {
14
- "role": "cond",
15
- "dtype": "B",
16
- "description": "Required initial condition encoded as either a scalar tensor or a one-element rank-1 tensor. A false value executes zero iterations; a true value permits all iterations selected by `M`. This fixed lowering does not update the condition inside the loop."
17
- },
18
- {
19
- "role": "v_initial",
20
- "dtype": "T",
21
- "rank": 1,
22
- "description": "Initial rank-1 loop-carried state of shape `[dim]`."
23
- },
24
- {
25
- "role": "step",
26
- "dtype": "T",
27
- "rank": 1,
28
- "description": "Implementation-specific rank-1 increment of shape `[dim]`, added elementwise to the state on every executed iteration. This is not a standard ONNX `Loop` input."
29
- }
30
- ],
31
- "outputs": [
32
- {
33
- "role": "v_final",
34
- "dtype": "T",
35
- "rank": 1,
36
- "description": "Final rank-1 state of shape `[dim]` after the executed additions.",
37
- "shape": "shapes.v_initial"
38
- },
39
- {
40
- "role": "scan_output",
41
- "dtype": "T",
42
- "rank": 2,
43
- "description": "Dense tensor of shape `[scan_steps, dim]`. Each executed row contains the updated state for that iteration; rows beyond the executed trip count are zero-filled."
44
- }
45
- ],
46
- "typeConstraints": { "T": ["float32"], "I": ["uint32"], "B": ["uint32", "bool"] },
47
- "args": {
48
- "m": { "kind": "tensor", "semantic": "M", "role": "input" },
49
- "cond": { "kind": "tensor", "semantic": "cond", "role": "input" },
50
- "v_initial": { "kind": "tensor", "semantic": "v_initial", "role": "input" },
51
- "step": { "kind": "tensor", "semantic": "step", "role": "input" },
52
- "v_final": { "kind": "tensor", "semantic": "v_final", "role": "output" },
53
- "scan_output": { "kind": "tensor", "semantic": "scan_output", "role": "output" }
54
  },
 
 
 
 
 
55
  "variants": [
56
  {
57
  "id": "lowered_add_step",
58
- "when": ["ranks.M <= 1", "numel(shapes.M) == 1", "ranks.cond <= 1", "numel(shapes.cond) == 1", "ranks.v_initial == 1", "ranks.step == 1", "ranks.v_final == 1", "ranks.scan_output == 2", "dim(shapes.step, 0) == dim(shapes.v_initial, 0)", "dim(shapes.v_final, 0) == dim(shapes.v_initial, 0)", "dim(shapes.scan_output, 1) == dim(shapes.v_initial, 0)"],
59
  "derive": { "dimBlocks": "ceil(dim(shapes.v_initial, 0) / 256)" },
60
  "passes": [
61
  {
@@ -63,61 +25,18 @@
63
  "name": "Loop",
64
  "shader": "loop-add-step.wgsl.jinja",
65
  "bindings": [
66
- {
67
- "name": "m",
68
- "arg": "m",
69
- "semantic": "M",
70
- "buffer": { "type": "read-only-storage" },
71
- "elementType": "u32",
72
- "length": 1
73
- },
74
- {
75
- "name": "cond",
76
- "arg": "cond",
77
- "semantic": "cond",
78
- "buffer": { "type": "read-only-storage" },
79
- "elementType": "u32",
80
- "length": 1
81
- },
82
- {
83
- "name": "v_initial",
84
- "arg": "v_initial",
85
- "semantic": "v_initial",
86
- "buffer": { "type": "read-only-storage" },
87
- "elementType": "f32"
88
- },
89
- {
90
- "name": "step",
91
- "arg": "step",
92
- "semantic": "step",
93
- "buffer": { "type": "read-only-storage" },
94
- "elementType": "f32"
95
- },
96
- {
97
- "name": "v_final",
98
- "arg": "v_final",
99
- "semantic": "v_final",
100
- "buffer": { "type": "storage" },
101
- "elementType": "f32"
102
- },
103
- {
104
- "name": "scan_output",
105
- "arg": "scan_output",
106
- "semantic": "scan_output",
107
- "buffer": { "type": "storage" },
108
- "elementType": "f32"
109
- },
110
  {
111
  "name": "params",
112
- "semantic": "kernel.params",
113
- "buffer": { "type": "uniform" },
114
- "struct": {
115
- "name": "Params",
116
- "fields": [
117
- { "name": "dim", "type": "u32", "value": "dim(shapes.v_initial, 0)" },
118
- { "name": "scanSteps", "type": "u32", "value": "dim(shapes.scan_output, 0)" }
119
- ]
120
- }
121
  }
122
  ],
123
  "dispatch": { "x": "dimBlocks" }
 
3
  "name": "Loop",
4
  "conformance": "internal-lowering",
5
  "sinceVersion": 25,
6
+ "inputs": {
7
+ "m": { "onnx": "M", "dtype": "I" },
8
+ "cond": { "dtype": "B" },
9
+ "v_initial": { "dtype": "T", "rank": 1 },
10
+ "step": { "dtype": "T", "rank": 1 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  },
12
+ "outputs": {
13
+ "v_final": { "dtype": "T", "rank": 1, "shape": "shapes.v_initial" },
14
+ "scan_output": { "dtype": "T", "rank": 2 }
15
+ },
16
+ "typeConstraints": { "T": ["float32"], "I": ["uint32"], "B": ["uint32", "bool"] },
17
  "variants": [
18
  {
19
  "id": "lowered_add_step",
20
+ "when": ["ranks.m <= 1", "numel(shapes.m) == 1", "ranks.cond <= 1", "numel(shapes.cond) == 1", "ranks.v_initial == 1", "ranks.step == 1", "ranks.v_final == 1", "ranks.scan_output == 2", "dim(shapes.step, 0) == dim(shapes.v_initial, 0)", "dim(shapes.v_final, 0) == dim(shapes.v_initial, 0)", "dim(shapes.scan_output, 1) == dim(shapes.v_initial, 0)"],
21
  "derive": { "dimBlocks": "ceil(dim(shapes.v_initial, 0) / 256)" },
22
  "passes": [
23
  {
 
25
  "name": "Loop",
26
  "shader": "loop-add-step.wgsl.jinja",
27
  "bindings": [
28
+ { "arg": "m", "elementType": "u32", "length": 1 },
29
+ { "arg": "cond", "elementType": "u32", "length": 1 },
30
+ { "arg": "v_initial", "elementType": "f32" },
31
+ { "arg": "step", "elementType": "f32" },
32
+ { "arg": "v_final", "elementType": "f32" },
33
+ { "arg": "scan_output", "elementType": "f32" },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  {
35
  "name": "params",
36
+ "struct": [
37
+ { "name": "dim", "type": "u32", "value": "dim(shapes.v_initial, 0)" },
38
+ { "name": "scanSteps", "type": "u32", "value": "dim(shapes.scan_output, 0)" }
39
+ ]
 
 
 
 
 
40
  }
41
  ],
42
  "dispatch": { "x": "dimBlocks" }
build/webgpu/metadata.json CHANGED
@@ -1,18 +1,18 @@
1
  {
2
  "name": "ai.onnx.Loop",
3
- "id": "_ai_onnx_loop_webgpu_fb2e7a5",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "xq5d0FI/A9ESZ8tyzz5qAxP39qxO5jGxCNji2YREKxI=",
11
  "loop-add-step.wgsl.jinja": "Sk4601cYmNLKCMIFX47m3mKo2M5oh/fekvOwLOUN1D8=",
12
- "manifest.json": "C5Kc57/iLS4XSFrNuO6zRpUHfXkgOI8Hm+LlxTfs/eg=",
13
- "test.json": "UG6yKjMh8XfBVE9AZSF2FfMZjBNwXajveageqJpYjWY="
14
  }
15
  },
16
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
17
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Loop" }
18
  }
 
1
  {
2
  "name": "ai.onnx.Loop",
3
+ "id": "_ai_onnx_loop_webgpu_801da86",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "ZSbqJEih0tA29HPs9gNfjrVWJeylslE0PXPC0uKM82s=",
11
  "loop-add-step.wgsl.jinja": "Sk4601cYmNLKCMIFX47m3mKo2M5oh/fekvOwLOUN1D8=",
12
+ "manifest.json": "H3mdjtpFSSHh+Osunjn3M82kXBVm5ga/DK+8Ha0Wwoc=",
13
+ "test.json": "Rp36C1l3Leiipq280ww8mCj4exFK53UlSD0thVu/2P0="
14
  }
15
  },
16
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
17
+ "webgpu": { "manifestSpec": "2.0", "variants": { "lowered_add_step": ["loop-add-step.wgsl.jinja"] } }
18
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Loop",
3
  "cases": [
4
  {
5
  "name": "lowered_add_step_scan",
@@ -32,7 +31,7 @@
32
  "provenance": {
33
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
34
  "test": "Loop.Opset11WithNoVariadicInputsAndOutputs",
35
- "notes": "Projection onto the framework's lowered add-step loop: one true iteration produces one scan output value."
36
  },
37
  "inputs": {
38
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
@@ -50,7 +49,7 @@
50
  "provenance": {
51
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
52
  "test": "Loop.ZeroIterations",
53
- "notes": "Projection onto the framework's lowered add-step loop: trip count zero must leave carried state unchanged and produce no scan elements."
54
  },
55
  "inputs": {
56
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
@@ -68,7 +67,7 @@
68
  "provenance": {
69
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
70
  "test": "Loop.ExitDueToMaxIterations",
71
- "notes": "Projection onto the framework's lowered add-step loop: two iterations advance the carried state and produce two scan values."
72
  },
73
  "inputs": {
74
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [2] } },
@@ -138,7 +137,7 @@
138
  "provenance": {
139
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
140
  "test": "Loop.ExitDueToMaxIterations",
141
- "notes": "ONNX Loop trip count and initial condition are scalar values. The project-lowered add-step form should accept scalar logical uint32 metadata as well as [1] tensors."
142
  },
143
  "inputs": {
144
  "m": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
@@ -192,7 +191,7 @@
192
  "provenance": {
193
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
194
  "test": "Loop.ExitDueToMaxIterations",
195
- "notes": "ONNX only requires the loop condition to be a bool; runtimes that store it as an integer may emit any nonzero value. The lowered kernel must treat any nonzero cond (here 2) as true via cond[0] != 0u, matching the reference's !== 0 test, not a literal == 1 comparison."
196
  },
197
  "inputs": {
198
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [3] } },
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "lowered_add_step_scan",
 
31
  "provenance": {
32
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
33
  "test": "Loop.Opset11WithNoVariadicInputsAndOutputs",
34
+ "notes": "The internal lowered add-step loop runs one true iteration and produces one scan output value."
35
  },
36
  "inputs": {
37
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [1] } },
 
49
  "provenance": {
50
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
51
  "test": "Loop.ZeroIterations",
52
+ "notes": "With a zero trip count, the internal lowered add-step loop leaves carried state unchanged and produces no scan elements."
53
  },
54
  "inputs": {
55
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [0] } },
 
67
  "provenance": {
68
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
69
  "test": "Loop.ExitDueToMaxIterations",
70
+ "notes": "The internal lowered add-step loop runs two iterations, advances carried state twice, and produces two scan values."
71
  },
72
  "inputs": {
73
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [2] } },
 
137
  "provenance": {
138
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
139
  "test": "Loop.ExitDueToMaxIterations",
140
+ "notes": "The fixed add-step lowering accepts scalar uint32 trip-count and condition metadata, applies three iterations, and emits one scan row per iteration."
141
  },
142
  "inputs": {
143
  "m": { "dtype": "uint32", "shape": [], "data": { "kind": "values", "values": [3] } },
 
191
  "provenance": {
192
  "source": "onnxruntime/test/providers/cpu/controlflow/loop_test.cc",
193
  "test": "Loop.ExitDueToMaxIterations",
194
+ "notes": "The lowered integer-backed condition must treat any nonzero value, including 2, as logical true rather than comparing only with 1."
195
  },
196
  "inputs": {
197
  "m": { "dtype": "uint32", "shape": [1], "data": { "kind": "values", "values": [3] } },