Xenova HF Staff commited on
Commit
1844ba1
·
verified ·
1 Parent(s): 4eccb3a

sync 91d990483a17

Browse files
README.md CHANGED
@@ -12,24 +12,24 @@ tags:
12
 
13
  ## Description
14
 
15
- Implements ONNX opset-23 RotaryEmbedding for float16 and float32 tensors. Applies rotary positional embeddings (RoPE) by rotating each head's embedding vector using precomputed `cos_cache` and `sin_cache` values. A partial rotation can be applied by setting `rotary_embedding_dim` to rotate only a prefix of the head dimension. `position_ids` keeps its standard logical int64 type; valid positions are non-negative and bounded by the WebGPU-addressable cache, so the backend stores them losslessly as uint32. Other ONNX floating-point input types are not yet implemented.
16
 
17
  See the [ONNX `RotaryEmbedding` spec](https://onnx.ai/onnx/operators/onnx__RotaryEmbedding.html) for the reference semantics.
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `x` | `T` | same as logical dtype | — | — | Input token embeddings. Shape is `(batch_size, sequence_length, hidden_size)` for rank 3 or `(batch_size, num_heads, sequence_length, head_size)` for rank 4. `head_size` must be even, and the `num_heads` attribute is required for rank-3 input. | required |
24
- | `cos_cache` | `cos` | `T` | same as logical dtype | — | — | Precomputed cosine values. Without `position_ids`, shape is `(batch_size, sequence_length, rotary_dim/2)`; with `position_ids`, shape is `(max_sequence_length, rotary_dim/2)`. | required |
25
- | `sin_cache` | `sin` | `T` | same as logical dtype | — | — | Precomputed sine values with the same shape and type as `cos_cache`. | required |
26
- | `position_ids` | `positionIds` | `M` | `uint32` | `2` | — | Optional logical int64 per-token position indices of shape `(batch_size, sequence_length)`. Valid positions are non-negative cache-row indices and use uint32 WebGPU storage. When supplied, the 2D cache tables are gathered at these positions. | optional |
27
 
28
  ## Outputs
29
 
30
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
31
  | --- | --- | --- | --- | --- | --- | --- |
32
- | `Y` | `y` | `T` | same as `X` | same as `X` | Rotary-position-encoded tensor with the same shape and type as `X`. | required |
33
 
34
  ## Attributes
35
 
@@ -38,8 +38,8 @@ Attributes and default values (overridable per request):
38
  | Attribute | Default | Description |
39
  | --- | --- | --- |
40
  | `interleaved` | `0` | Set to 1 to rotate using an interleaved pattern (even/odd elements), or 0 to split the head dimension into two contiguous halves. Default is 0. |
41
- | `rotary_embedding_dim` | `0` | Number of head-dimension elements to rotate; `0` means rotate the full head dimension. When set, only the leading `rotary_embedding_dim` elements are rotated and the rest are passed through unchanged. |
42
  | `num_heads` | — | Optional number of attention heads. ONNX requires this attribute when `X` is rank 3; it is unnecessary for rank-4 input because the head count is explicit in the shape. |
 
43
 
44
  ## Type constraints
45
 
@@ -50,18 +50,23 @@ Attributes and default values (overridable per request):
50
 
51
  ## Files
52
 
53
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
54
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
55
  - [`test.json`](build/webgpu/test.json) — correctness cases
56
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
57
  - [`rotary-embedding.wgsl.jinja`](build/webgpu/rotary-embedding.wgsl.jinja)
58
 
59
  ## Use with `@huggingface/kernels`
60
 
61
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
62
- It then allocates the result tensors automatically.
 
 
 
63
 
64
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
65
 
66
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
67
 
 
12
 
13
  ## Description
14
 
15
+ Implements ONNX opset-23 RotaryEmbedding for float16 and float32 tensors. Applies rotary positional embeddings (RoPE) by rotating each head's embedding vector using precomputed `cos_cache` and `sin_cache` values. A partial rotation can be applied by setting `rotary_embedding_dim` to rotate only a prefix of the head dimension. `position_ids` keeps its standard logical int64 type; valid positions are non-negative and bounded by the WebGPU-addressable cache, so the backend stores them losslessly as uint32. Other ONNX floating-point input types are unsupported.
16
 
17
  See the [ONNX `RotaryEmbedding` spec](https://onnx.ai/onnx/operators/onnx__RotaryEmbedding.html) for the reference semantics.
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- | --- |
23
+ | `x` | `X` | `T` | same as logical dtype | — | — | Input token embeddings. Shape is `(batch_size, sequence_length, hidden_size)` for rank 3 or `(batch_size, num_heads, sequence_length, head_size)` for rank 4. `head_size` must be even, and the `num_heads` attribute is required for rank-3 input. | required |
24
+ | `cos` | `cos_cache` | `T` | same as logical dtype | — | — | Precomputed cosine values. Without `position_ids`, shape is `(batch_size, sequence_length, rotary_dim/2)`; with `position_ids`, shape is `(max_sequence_length, rotary_dim/2)`. | required |
25
+ | `sin` | `sin_cache` | `T` | same as logical dtype | — | — | Precomputed sine values with the same shape and type as `cos_cache`. | required |
26
+ | `positionIds` | `position_ids` | `M` | `uint32` | `2` | — | Optional logical int64 per-token position indices of shape `(batch_size, sequence_length)`. Valid positions are non-negative cache-row indices and use uint32 WebGPU storage. When supplied, the 2D cache tables are gathered at these positions. | optional |
27
 
28
  ## Outputs
29
 
30
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
31
  | --- | --- | --- | --- | --- | --- | --- |
32
+ | `y` | `Y` | `T` | same as `x` | same as `x` | Rotary-position-encoded tensor with the same shape and type as `X`. | required |
33
 
34
  ## Attributes
35
 
 
38
  | Attribute | Default | Description |
39
  | --- | --- | --- |
40
  | `interleaved` | `0` | Set to 1 to rotate using an interleaved pattern (even/odd elements), or 0 to split the head dimension into two contiguous halves. Default is 0. |
 
41
  | `num_heads` | — | Optional number of attention heads. ONNX requires this attribute when `X` is rank 3; it is unnecessary for rank-4 input because the head count is explicit in the shape. |
42
+ | `rotary_embedding_dim` | `0` | Number of head-dimension elements to rotate; `0` means rotate the full head dimension. When set, only the leading `rotary_embedding_dim` elements are rotated and the rest are passed through unchanged. |
43
 
44
  ## Type constraints
45
 
 
50
 
51
  ## Files
52
 
53
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
54
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
55
  - [`test.json`](build/webgpu/test.json) — correctness cases
56
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
57
+ - [`rotary-embedding-quad.wgsl.jinja`](build/webgpu/rotary-embedding-quad.wgsl.jinja)
58
  - [`rotary-embedding.wgsl.jinja`](build/webgpu/rotary-embedding.wgsl.jinja)
59
 
60
  ## Use with `@huggingface/kernels`
61
 
62
+ ```sh
63
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
64
+ ```
65
+
66
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
67
 
68
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
69
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
70
 
71
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
72
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.RotaryEmbedding",
3
  "tunableSpace": { "WORKGROUP_SIZE": [128, 256] },
4
  "cases": [
5
  {
 
1
  {
 
2
  "tunableSpace": { "WORKGROUP_SIZE": [128, 256] },
3
  "cases": [
4
  {
build/webgpu/manifest.json CHANGED
@@ -2,287 +2,324 @@
2
  "domain": "ai.onnx",
3
  "name": "RotaryEmbedding",
4
  "sinceVersion": 23,
5
- "description": "Implements ONNX opset-23 RotaryEmbedding for float16 and float32 tensors. Applies rotary positional embeddings (RoPE) by rotating each head's embedding vector using precomputed `cos_cache` and `sin_cache` values. A partial rotation can be applied by setting `rotary_embedding_dim` to rotate only a prefix of the head dimension. `position_ids` keeps its standard logical int64 type; valid positions are non-negative and bounded by the WebGPU-addressable cache, so the backend stores them losslessly as uint32. Other ONNX floating-point input types are not yet implemented.",
6
- "inputs": [
7
- {
8
- "role": "X",
9
- "dtype": "T",
10
- "description": "Input token embeddings. Shape is `(batch_size, sequence_length, hidden_size)` for rank 3 or `(batch_size, num_heads, sequence_length, head_size)` for rank 4. `head_size` must be even, and the `num_heads` attribute is required for rank-3 input."
11
- },
12
- {
13
- "role": "cos_cache",
14
- "dtype": "T",
15
- "description": "Precomputed cosine values. Without `position_ids`, shape is `(batch_size, sequence_length, rotary_dim/2)`; with `position_ids`, shape is `(max_sequence_length, rotary_dim/2)`."
16
- },
17
- {
18
- "role": "sin_cache",
19
- "dtype": "T",
20
- "description": "Precomputed sine values with the same shape and type as `cos_cache`."
21
- },
22
- {
23
- "role": "position_ids",
24
  "dtype": "M",
25
  "rank": 2,
26
  "optional": true,
27
- "description": "Optional logical int64 per-token position indices of shape `(batch_size, sequence_length)`. Valid positions are non-negative cache-row indices and use uint32 WebGPU storage. When supplied, the 2D cache tables are gathered at these positions."
28
- }
29
- ],
30
- "outputs": [
31
- {
32
- "role": "Y",
33
- "dtype": "T",
34
- "rank": "ranks.X",
35
- "shape": "shapes.X",
36
- "description": "Rotary-position-encoded tensor with the same shape and type as `X`."
37
  }
38
- ],
39
- "attributes": { "interleaved": 0, "rotary_embedding_dim": 0 },
40
- "attributeDescriptions": {
41
- "interleaved": "Set to 1 to rotate using an interleaved pattern (even/odd elements), or 0 to split the head dimension into two contiguous halves. Default is 0.",
42
- "num_heads": "Optional number of attention heads. ONNX requires this attribute when `X` is rank 3; it is unnecessary for rank-4 input because the head count is explicit in the shape.",
43
- "rotary_embedding_dim": "Number of head-dimension elements to rotate; `0` means rotate the full head dimension. When set, only the leading `rotary_embedding_dim` elements are rotated and the rest are passed through unchanged."
44
  },
 
 
45
  "attributeConstraints": { "interleaved": { "values": [0, 1] } },
46
  "typeConstraints": { "T": ["float32", "float16"], "M": ["int64"] },
47
- "args": {
48
- "x": { "kind": "tensor", "semantic": "X", "role": "input" },
49
- "cos": { "kind": "tensor", "semantic": "cos_cache", "role": "input" },
50
- "sin": { "kind": "tensor", "semantic": "sin_cache", "role": "input" },
51
- "positionIds": {
52
- "kind": "tensor",
53
- "semantic": "position_ids",
54
- "role": "input",
55
- "dtype": "uint32",
56
- "narrowing": "checked",
57
- "required": false
58
- },
59
- "y": { "kind": "tensor", "semantic": "Y", "role": "output" }
60
- },
61
- "tunables": { "WORKGROUP_SIZE": 256 },
62
  "derive": {
63
- "pairDispatchOk": "ceilDiv(numel(shapes.X) / 2, tunables.WORKGROUP_SIZE) <= device.limits.maxComputeWorkgroupsPerDimension * device.limits.maxComputeWorkgroupsPerDimension",
64
- "rank3HeadSize": "dim(shapes.X, 2) / attrs.num_heads if attrs.num_heads is defined and attrs.num_heads > 0 else 0",
65
- "rank3Contract": "f16Ok(dtypes.T) and ranks.X == 3 and ranks.cos_cache == 3 and ranks.sin_cache == 3 and ranks.Y == 3 and sameShape(shapes.X, shapes.Y) and attrs.num_heads is defined and attrs.num_heads >= 1 and dim(shapes.X, 2) % attrs.num_heads == 0 and rank3HeadSize % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= rank3HeadSize) and dim(shapes.cos_cache, 2) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else rank3HeadSize) and sameShape(shapes.cos_cache, shapes.sin_cache) and dim(shapes.cos_cache, 0) == dim(shapes.X, 0) and dim(shapes.cos_cache, 1) == dim(shapes.X, 1) and not present.positionIds",
66
- "rank4Contract": "f16Ok(dtypes.T) and ranks.X == 4 and ranks.cos_cache == 3 and ranks.sin_cache == 3 and ranks.Y == 4 and dim(shapes.X, 3) % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= dim(shapes.X, 3)) and dim(shapes.cos_cache, 2) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else dim(shapes.X, 3)) and dim(shapes.sin_cache, 2) == dim(shapes.cos_cache, 2) and sameShape(shapes.X, shapes.Y) and dim(shapes.cos_cache, 0) == dim(shapes.X, 0) and dim(shapes.sin_cache, 0) == dim(shapes.X, 0) and dim(shapes.cos_cache, 1) == dim(shapes.X, 2) and dim(shapes.sin_cache, 1) == dim(shapes.X, 2) and not present.positionIds",
67
- "rank3PosContract": "f16Ok(dtypes.T) and ranks.X == 3 and ranks.cos_cache == 2 and ranks.sin_cache == 2 and ranks.Y == 3 and sameShape(shapes.X, shapes.Y) and attrs.num_heads is defined and attrs.num_heads >= 1 and dim(shapes.X, 2) % attrs.num_heads == 0 and rank3HeadSize % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= rank3HeadSize) and dim(shapes.cos_cache, 1) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else rank3HeadSize) and sameShape(shapes.cos_cache, shapes.sin_cache) and present.positionIds and ranks.position_ids == 2 and dim(shapes.position_ids, 0) == dim(shapes.X, 0) and dim(shapes.position_ids, 1) == dim(shapes.X, 1)",
68
- "rank4PosContract": "f16Ok(dtypes.T) and ranks.X == 4 and ranks.cos_cache == 2 and ranks.sin_cache == 2 and ranks.Y == 4 and dim(shapes.X, 3) % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= dim(shapes.X, 3)) and dim(shapes.cos_cache, 1) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else dim(shapes.X, 3)) and sameShape(shapes.cos_cache, shapes.sin_cache) and sameShape(shapes.X, shapes.Y) and present.positionIds and ranks.position_ids == 2 and dim(shapes.position_ids, 0) == dim(shapes.X, 0) and dim(shapes.position_ids, 1) == dim(shapes.X, 2)"
 
 
 
 
69
  },
70
- "bindingSets": {
71
- "rank3": [
72
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
73
- {
74
- "name": "cos_cache",
75
- "arg": "cos",
76
- "semantic": "cos_cache",
77
- "buffer": { "type": "read-only-storage" },
78
- "elementType": "$T"
79
- },
80
- {
81
- "name": "sin_cache",
82
- "arg": "sin",
83
- "semantic": "sin_cache",
84
- "buffer": { "type": "read-only-storage" },
85
- "elementType": "$T"
86
- },
87
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
88
- {
89
- "name": "params",
90
- "semantic": "kernel.params",
91
- "buffer": { "type": "uniform" },
92
- "struct": {
93
- "name": "Params",
94
- "fields": [
95
- { "name": "pairCount", "type": "u32", "value": "numel(shapes.X) / 2" },
96
- { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.X, 1)" },
97
- { "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
98
- { "name": "headSize", "type": "u32", "value": "rank3HeadSize" },
99
- { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 2) * 2" },
100
- { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 2)" },
101
- {
102
- "name": "cacheBatchStride",
103
- "type": "u32",
104
- "value": "dim(shapes.cos_cache, 1) * dim(shapes.cos_cache, 2)"
105
- }
106
- ]
107
- }
108
- }
109
- ],
110
- "rank3Pos": [
111
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
112
- {
113
- "name": "cos_cache",
114
- "arg": "cos",
115
- "semantic": "cos_cache",
116
- "buffer": { "type": "read-only-storage" },
117
- "elementType": "$T"
118
- },
119
- {
120
- "name": "sin_cache",
121
- "arg": "sin",
122
- "semantic": "sin_cache",
123
- "buffer": { "type": "read-only-storage" },
124
- "elementType": "$T"
125
- },
126
- {
127
- "name": "position_ids",
128
- "arg": "positionIds",
129
- "semantic": "position_ids",
130
- "buffer": { "type": "read-only-storage" },
131
- "elementType": "$M"
132
- },
133
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
134
- {
135
- "name": "params",
136
- "semantic": "kernel.params",
137
- "buffer": { "type": "uniform" },
138
- "struct": {
139
- "name": "Params",
140
- "fields": [
141
- { "name": "pairCount", "type": "u32", "value": "numel(shapes.X) / 2" },
142
- { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.X, 1)" },
143
- { "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
144
- { "name": "headSize", "type": "u32", "value": "rank3HeadSize" },
145
- { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 1) * 2" },
146
- { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 1)" }
147
- ]
148
- }
149
- }
150
- ],
151
- "rank4Pos": [
152
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
153
- {
154
- "name": "cos_cache",
155
- "arg": "cos",
156
- "semantic": "cos_cache",
157
- "buffer": { "type": "read-only-storage" },
158
- "elementType": "$T"
159
- },
160
- {
161
- "name": "sin_cache",
162
- "arg": "sin",
163
- "semantic": "sin_cache",
164
- "buffer": { "type": "read-only-storage" },
165
- "elementType": "$T"
166
- },
167
- {
168
- "name": "position_ids",
169
- "arg": "positionIds",
170
- "semantic": "position_ids",
171
- "buffer": { "type": "read-only-storage" },
172
- "elementType": "$M"
173
- },
174
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
175
- {
176
- "name": "params",
177
- "semantic": "kernel.params",
178
- "buffer": { "type": "uniform" },
179
- "struct": {
180
- "name": "Params",
181
- "fields": [
182
- { "name": "pairCount", "type": "u32", "value": "numel(shapes.X) / 2" },
183
- { "name": "numHeads", "type": "u32", "value": "dim(shapes.X, 1)" },
184
- { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.X, 2)" },
185
- { "name": "headSize", "type": "u32", "value": "dim(shapes.X, 3)" },
186
- { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 1) * 2" },
187
- { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 1)" }
188
- ]
189
- }
190
- }
191
- ],
192
- "rank4": [
193
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
194
- {
195
- "name": "cos_cache",
196
- "arg": "cos",
197
- "semantic": "cos_cache",
198
- "buffer": { "type": "read-only-storage" },
199
- "elementType": "$T"
200
- },
201
- {
202
- "name": "sin_cache",
203
- "arg": "sin",
204
- "semantic": "sin_cache",
205
- "buffer": { "type": "read-only-storage" },
206
- "elementType": "$T"
207
- },
208
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
209
- {
210
- "name": "params",
211
- "semantic": "kernel.params",
212
- "buffer": { "type": "uniform" },
213
- "struct": {
214
- "name": "Params",
215
- "fields": [
216
- { "name": "pairCount", "type": "u32", "value": "numel(shapes.X) / 2" },
217
- { "name": "numHeads", "type": "u32", "value": "dim(shapes.X, 1)" },
218
- { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.X, 2)" },
219
- { "name": "headSize", "type": "u32", "value": "dim(shapes.X, 3)" },
220
- { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 2) * 2" },
221
- { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 2)" },
222
- {
223
- "name": "cacheBatchStride",
224
- "type": "u32",
225
- "value": "dim(shapes.cos_cache, 1) * dim(shapes.cos_cache, 2)"
226
- }
227
- ]
228
- }
229
- }
230
- ]
231
  },
232
  "variants": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  {
234
  "id": "rank3_cache2_pos",
 
235
  "when": ["rank3PosContract", "pairDispatchOk"],
236
- "constants": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
237
  "passes": [
238
  {
239
  "id": "main",
240
  "name": "rotary_embedding3d_pos",
241
- "source": { "shader": "rotary-embedding.wgsl.jinja", "inputs": { "rank": 3, "hasPositionIds": "true" } },
242
- "bindings": "rank3Pos",
243
- "dispatch": { "threads": "numel(shapes.X) / 2", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
  ]
246
  },
247
  {
248
  "id": "rank4_cache2_pos",
 
249
  "when": ["rank4PosContract", "pairDispatchOk"],
250
- "constants": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
251
  "passes": [
252
  {
253
  "id": "main",
254
  "name": "rotary_embedding4d_pos",
255
- "source": { "shader": "rotary-embedding.wgsl.jinja", "inputs": { "rank": 4, "hasPositionIds": "true" } },
256
- "bindings": "rank4Pos",
257
- "dispatch": { "threads": "numel(shapes.X) / 2", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
259
  ]
260
  },
261
  {
262
  "id": "rank3_cache3",
 
263
  "when": ["rank3Contract", "pairDispatchOk"],
264
- "constants": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
265
  "passes": [
266
  {
267
  "id": "main",
268
  "name": "rotary_embedding3d",
269
- "source": { "shader": "rotary-embedding.wgsl.jinja", "inputs": { "rank": 3 } },
270
- "bindings": "rank3",
271
- "dispatch": { "threads": "numel(shapes.X) / 2", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  }
273
  ]
274
  },
275
  {
276
  "id": "rank4_cache3",
 
277
  "when": ["rank4Contract", "pairDispatchOk"],
278
- "constants": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
279
  "passes": [
280
  {
281
  "id": "main",
282
  "name": "rotary_embedding4d",
283
- "source": { "shader": "rotary-embedding.wgsl.jinja", "inputs": { "rank": 4 } },
284
- "bindings": "rank4",
285
- "dispatch": { "threads": "numel(shapes.X) / 2", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
286
  }
287
  ]
288
  }
 
2
  "domain": "ai.onnx",
3
  "name": "RotaryEmbedding",
4
  "sinceVersion": 23,
5
+ "inputs": {
6
+ "x": { "onnx": "X", "dtype": "T" },
7
+ "cos": { "onnx": "cos_cache", "dtype": "T" },
8
+ "sin": { "onnx": "sin_cache", "dtype": "T" },
9
+ "positionIds": {
10
+ "onnx": "position_ids",
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  "dtype": "M",
12
  "rank": 2,
13
  "optional": true,
14
+ "storage": "uint32",
15
+ "narrowing": "checked"
 
 
 
 
 
 
 
 
16
  }
 
 
 
 
 
 
17
  },
18
+ "outputs": { "y": { "onnx": "Y", "dtype": "T", "rank": "ranks.x", "shape": "shapes.x" } },
19
+ "attributes": { "interleaved": { "default": 0 }, "rotary_embedding_dim": { "default": 0 }, "num_heads": {} },
20
  "attributeConstraints": { "interleaved": { "values": [0, 1] } },
21
  "typeConstraints": { "T": ["float32", "float16"], "M": ["int64"] },
22
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  "derive": {
24
+ "pairDispatchOk": "ceilDiv(numel(shapes.x) / 2, tunables.WORKGROUP_SIZE) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) * min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
25
+ "rank3HeadSize": "dim(shapes.x, 2) / attrs.num_heads if attrs.num_heads is defined and attrs.num_heads > 0 else 0",
26
+ "rank3Contract": "f16Ok(dtypes.T) and ranks.x == 3 and ranks.cos == 3 and ranks.sin == 3 and ranks.y == 3 and sameShape(shapes.x, shapes.y) and attrs.num_heads is defined and attrs.num_heads >= 1 and dim(shapes.x, 2) % attrs.num_heads == 0 and rank3HeadSize % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= rank3HeadSize) and dim(shapes.cos, 2) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else rank3HeadSize) and sameShape(shapes.cos, shapes.sin) and dim(shapes.cos, 0) == dim(shapes.x, 0) and dim(shapes.cos, 1) == dim(shapes.x, 1) and not present.positionIds",
27
+ "rank4Contract": "f16Ok(dtypes.T) and ranks.x == 4 and ranks.cos == 3 and ranks.sin == 3 and ranks.y == 4 and dim(shapes.x, 3) % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= dim(shapes.x, 3)) and dim(shapes.cos, 2) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else dim(shapes.x, 3)) and dim(shapes.sin, 2) == dim(shapes.cos, 2) and sameShape(shapes.x, shapes.y) and dim(shapes.cos, 0) == dim(shapes.x, 0) and dim(shapes.sin, 0) == dim(shapes.x, 0) and dim(shapes.cos, 1) == dim(shapes.x, 2) and dim(shapes.sin, 1) == dim(shapes.x, 2) and not present.positionIds",
28
+ "rank3PosContract": "f16Ok(dtypes.T) and ranks.x == 3 and ranks.cos == 2 and ranks.sin == 2 and ranks.y == 3 and sameShape(shapes.x, shapes.y) and attrs.num_heads is defined and attrs.num_heads >= 1 and dim(shapes.x, 2) % attrs.num_heads == 0 and rank3HeadSize % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= rank3HeadSize) and dim(shapes.cos, 1) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else rank3HeadSize) and sameShape(shapes.cos, shapes.sin) and present.positionIds and ranks.positionIds == 2 and dim(shapes.positionIds, 0) == dim(shapes.x, 0) and dim(shapes.positionIds, 1) == dim(shapes.x, 1)",
29
+ "rank4PosContract": "f16Ok(dtypes.T) and ranks.x == 4 and ranks.cos == 2 and ranks.sin == 2 and ranks.y == 4 and dim(shapes.x, 3) % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= dim(shapes.x, 3)) and dim(shapes.cos, 1) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else dim(shapes.x, 3)) and sameShape(shapes.cos, shapes.sin) and sameShape(shapes.x, shapes.y) and present.positionIds and ranks.positionIds == 2 and dim(shapes.positionIds, 0) == dim(shapes.x, 0) and dim(shapes.positionIds, 1) == dim(shapes.x, 2)",
30
+ "quadCacheOk": "ranks.cos >= 1 and dim(shapes.cos, ranks.cos - 1) % 4 == 0",
31
+ "quad3Ok": "rank3HeadSize % 8 == 0 and quadCacheOk",
32
+ "quad4Ok": "ranks.x == 4 and dim(shapes.x, 3) % 8 == 0 and quadCacheOk",
33
+ "quadDispatchOk": "ceilDiv(numel(shapes.x) / 8, tunables.WORKGROUP_SIZE) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) * min(device.limits.maxComputeWorkgroupsPerDimension, 65535)"
34
  },
35
+ "bindings": {
36
+ "x": { "buffer": "read-only-storage", "elementType": "$vector" },
37
+ "cos_cache": { "arg": "cos", "buffer": "read-only-storage", "elementType": "$vector" },
38
+ "sin_cache": { "arg": "sin", "buffer": "read-only-storage", "elementType": "$vector" },
39
+ "position_ids": { "arg": "positionIds", "buffer": "read-only-storage", "elementType": "$M" },
40
+ "y": { "buffer": "storage", "elementType": "$vector" },
41
+ "params": {
42
+ "buffer": "uniform",
43
+ "struct": [
44
+ { "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
45
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
46
+ { "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
47
+ { "name": "headVec4", "type": "u32", "value": "rank3HeadSize / 4" },
48
+ { "name": "headQuads", "type": "u32", "value": "rank3HeadSize / 8" },
49
+ { "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 2" },
50
+ { "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 4" }
51
+ ]
52
+ },
53
+ "x_2": { "name": "x", "buffer": "read-only-storage", "elementType": "$T" },
54
+ "cos_cache_2": { "arg": "cos", "name": "cos_cache", "buffer": "read-only-storage", "elementType": "$T" },
55
+ "sin_cache_2": { "arg": "sin", "name": "sin_cache", "buffer": "read-only-storage", "elementType": "$T" },
56
+ "y_2": { "name": "y", "buffer": "storage", "elementType": "$T" },
57
+ "params_2": {
58
+ "name": "params",
59
+ "buffer": "uniform",
60
+ "struct": [
61
+ { "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
62
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
63
+ { "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
64
+ { "name": "headSize", "type": "u32", "value": "rank3HeadSize" },
65
+ { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 1) * 2" },
66
+ { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 1)" }
67
+ ]
68
+ },
69
+ "params_3": {
70
+ "name": "params",
71
+ "buffer": "uniform",
72
+ "struct": [
73
+ { "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
74
+ { "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
75
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
76
+ { "name": "headVec4", "type": "u32", "value": "dim(shapes.x, 3) / 4" },
77
+ { "name": "headQuads", "type": "u32", "value": "dim(shapes.x, 3) / 8" },
78
+ { "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 2" },
79
+ { "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 4" }
80
+ ]
81
+ },
82
+ "params_4": {
83
+ "name": "params",
84
+ "buffer": "uniform",
85
+ "struct": [
86
+ { "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
87
+ { "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
88
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
89
+ { "name": "headSize", "type": "u32", "value": "dim(shapes.x, 3)" },
90
+ { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 1) * 2" },
91
+ { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 1)" }
92
+ ]
93
+ },
94
+ "params_5": {
95
+ "name": "params",
96
+ "buffer": "uniform",
97
+ "struct": [
98
+ { "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
99
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
100
+ { "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
101
+ { "name": "headVec4", "type": "u32", "value": "rank3HeadSize / 4" },
102
+ { "name": "headQuads", "type": "u32", "value": "rank3HeadSize / 8" },
103
+ { "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 2" },
104
+ { "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 4" },
105
+ { "name": "cacheBatchStrideVec4", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2) / 4" }
106
+ ]
107
+ },
108
+ "params_6": {
109
+ "name": "params",
110
+ "buffer": "uniform",
111
+ "struct": [
112
+ { "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
113
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
114
+ { "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
115
+ { "name": "headSize", "type": "u32", "value": "rank3HeadSize" },
116
+ { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 2) * 2" },
117
+ { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 2)" },
118
+ { "name": "cacheBatchStride", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2)" }
119
+ ]
120
+ },
121
+ "params_7": {
122
+ "name": "params",
123
+ "buffer": "uniform",
124
+ "struct": [
125
+ { "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
126
+ { "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
127
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
128
+ { "name": "headVec4", "type": "u32", "value": "dim(shapes.x, 3) / 4" },
129
+ { "name": "headQuads", "type": "u32", "value": "dim(shapes.x, 3) / 8" },
130
+ { "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 2" },
131
+ { "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 4" },
132
+ { "name": "cacheBatchStrideVec4", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2) / 4" }
133
+ ]
134
+ },
135
+ "params_8": {
136
+ "name": "params",
137
+ "buffer": "uniform",
138
+ "struct": [
139
+ { "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
140
+ { "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
141
+ { "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
142
+ { "name": "headSize", "type": "u32", "value": "dim(shapes.x, 3)" },
143
+ { "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 2) * 2" },
144
+ { "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 2)" },
145
+ { "name": "cacheBatchStride", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2)" }
146
+ ]
147
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  },
149
  "variants": [
150
+ {
151
+ "id": "rank3_cache2_pos_quad",
152
+ "priority": 10,
153
+ "when": ["rank3PosContract", "quad3Ok", "quadDispatchOk"],
154
+ "derive": {
155
+ "interleaved": "attrs.interleaved != 0",
156
+ "usesF16": "dtypes.T == \"f16\"",
157
+ "vector": "\"vec4<\" ~ dtypes.T ~ \">\""
158
+ },
159
+ "passes": [
160
+ {
161
+ "id": "main",
162
+ "name": "rotary_embedding3d_pos_quad",
163
+ "shader": "rotary-embedding-quad.wgsl.jinja",
164
+ "derive": { "rank": 3, "hasPositionIds": "true" },
165
+ "bindings": ["x", "cos_cache", "sin_cache", "position_ids", "y", "params"],
166
+ "dispatch": {
167
+ "x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
168
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
169
+ "z": 1
170
+ }
171
+ }
172
+ ]
173
+ },
174
  {
175
  "id": "rank3_cache2_pos",
176
+ "priority": 0,
177
  "when": ["rank3PosContract", "pairDispatchOk"],
178
+ "derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
179
  "passes": [
180
  {
181
  "id": "main",
182
  "name": "rotary_embedding3d_pos",
183
+ "shader": "rotary-embedding.wgsl.jinja",
184
+ "derive": { "rank": 3, "hasPositionIds": "true" },
185
+ "bindings": ["x_2", "cos_cache_2", "sin_cache_2", "position_ids", "y_2", "params_2"],
186
+ "dispatch": {
187
+ "x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
188
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
189
+ "z": 1
190
+ }
191
+ }
192
+ ]
193
+ },
194
+ {
195
+ "id": "rank4_cache2_pos_quad",
196
+ "priority": 10,
197
+ "when": ["rank4PosContract", "quad4Ok", "quadDispatchOk"],
198
+ "derive": {
199
+ "interleaved": "attrs.interleaved != 0",
200
+ "usesF16": "dtypes.T == \"f16\"",
201
+ "vector": "\"vec4<\" ~ dtypes.T ~ \">\""
202
+ },
203
+ "passes": [
204
+ {
205
+ "id": "main",
206
+ "name": "rotary_embedding4d_pos_quad",
207
+ "shader": "rotary-embedding-quad.wgsl.jinja",
208
+ "derive": { "rank": 4, "hasPositionIds": "true" },
209
+ "bindings": ["x", "cos_cache", "sin_cache", "position_ids", "y", "params_3"],
210
+ "dispatch": {
211
+ "x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
212
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
213
+ "z": 1
214
+ }
215
  }
216
  ]
217
  },
218
  {
219
  "id": "rank4_cache2_pos",
220
+ "priority": 0,
221
  "when": ["rank4PosContract", "pairDispatchOk"],
222
+ "derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
223
  "passes": [
224
  {
225
  "id": "main",
226
  "name": "rotary_embedding4d_pos",
227
+ "shader": "rotary-embedding.wgsl.jinja",
228
+ "derive": { "rank": 4, "hasPositionIds": "true" },
229
+ "bindings": ["x_2", "cos_cache_2", "sin_cache_2", "position_ids", "y_2", "params_4"],
230
+ "dispatch": {
231
+ "x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
232
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
233
+ "z": 1
234
+ }
235
+ }
236
+ ]
237
+ },
238
+ {
239
+ "id": "rank3_cache3_quad",
240
+ "priority": 10,
241
+ "when": ["rank3Contract", "quad3Ok", "quadDispatchOk"],
242
+ "derive": {
243
+ "interleaved": "attrs.interleaved != 0",
244
+ "usesF16": "dtypes.T == \"f16\"",
245
+ "vector": "\"vec4<\" ~ dtypes.T ~ \">\""
246
+ },
247
+ "passes": [
248
+ {
249
+ "id": "main",
250
+ "name": "rotary_embedding3d_quad",
251
+ "shader": "rotary-embedding-quad.wgsl.jinja",
252
+ "derive": { "rank": 3 },
253
+ "bindings": ["x", "cos_cache", "sin_cache", "y", "params_5"],
254
+ "dispatch": {
255
+ "x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
256
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
257
+ "z": 1
258
+ }
259
  }
260
  ]
261
  },
262
  {
263
  "id": "rank3_cache3",
264
+ "priority": 0,
265
  "when": ["rank3Contract", "pairDispatchOk"],
266
+ "derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
267
  "passes": [
268
  {
269
  "id": "main",
270
  "name": "rotary_embedding3d",
271
+ "shader": "rotary-embedding.wgsl.jinja",
272
+ "derive": { "rank": 3 },
273
+ "bindings": ["x_2", "cos_cache_2", "sin_cache_2", "y_2", "params_6"],
274
+ "dispatch": {
275
+ "x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
276
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
277
+ "z": 1
278
+ }
279
+ }
280
+ ]
281
+ },
282
+ {
283
+ "id": "rank4_cache3_quad",
284
+ "priority": 10,
285
+ "when": ["rank4Contract", "quad4Ok", "quadDispatchOk"],
286
+ "derive": {
287
+ "interleaved": "attrs.interleaved != 0",
288
+ "usesF16": "dtypes.T == \"f16\"",
289
+ "vector": "\"vec4<\" ~ dtypes.T ~ \">\""
290
+ },
291
+ "passes": [
292
+ {
293
+ "id": "main",
294
+ "name": "rotary_embedding4d_quad",
295
+ "shader": "rotary-embedding-quad.wgsl.jinja",
296
+ "derive": { "rank": 4 },
297
+ "bindings": ["x", "cos_cache", "sin_cache", "y", "params_7"],
298
+ "dispatch": {
299
+ "x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
300
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
301
+ "z": 1
302
+ }
303
  }
304
  ]
305
  },
306
  {
307
  "id": "rank4_cache3",
308
+ "priority": 0,
309
  "when": ["rank4Contract", "pairDispatchOk"],
310
+ "derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
311
  "passes": [
312
  {
313
  "id": "main",
314
  "name": "rotary_embedding4d",
315
+ "shader": "rotary-embedding.wgsl.jinja",
316
+ "derive": { "rank": 4 },
317
+ "bindings": ["x_2", "cos_cache_2", "sin_cache_2", "y_2", "params_8"],
318
+ "dispatch": {
319
+ "x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
320
+ "y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
321
+ "z": 1
322
+ }
323
  }
324
  ]
325
  }
build/webgpu/metadata.json CHANGED
@@ -1,18 +1,31 @@
1
  {
2
  "name": "ai.onnx.RotaryEmbedding",
3
- "id": "_ai_onnx_rotaryembedding_webgpu_23c1d92",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "Uu/zPgVC3ubArN3FjvwWkoSP9bbEqLHRRppel4wsIeA=",
11
- "manifest.json": "yfsgfGknbiGBH5nzsmJmqJWPq1CskrxPgTz0vc+Qe/A=",
12
- "rotary-embedding.wgsl.jinja": "dkJWgoDB2IWxw7espG+q4D1MX04J2ofR9xHvntuxg9w=",
13
- "test.json": "p4hC6UXkzEdV+pQlTtZ0t2LcfXq3psczZWXmN0fRGes="
 
14
  }
15
  },
16
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
17
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.RotaryEmbedding" }
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
 
1
  {
2
  "name": "ai.onnx.RotaryEmbedding",
3
+ "id": "_ai_onnx_rotaryembedding_webgpu_5abb823",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "Ii8Ve/7TIfVkcj95zDTEbsfND6RUQkTEHTKUFQhHaEk=",
11
+ "manifest.json": "OeUM+N0V8ex5eCGPP+KmT0LDzPNRCc50xnAuaYDS8o0=",
12
+ "rotary-embedding-quad.wgsl.jinja": "vGNZeSSXSpPhhBz/0Wh9Z1zY5lSqp0VlCsfWmIM98CQ=",
13
+ "rotary-embedding.wgsl.jinja": "DNauIDqC9Gs5N07X/rqra/Q72p6LnkcYueWx0UAaXlo=",
14
+ "test.json": "394nZIOecEWid/Qm/+4eUAtAw0l+WnhmDdAuuFb96IA="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": {
21
+ "rank3_cache2_pos_quad": ["rotary-embedding-quad.wgsl.jinja"],
22
+ "rank3_cache2_pos": ["rotary-embedding.wgsl.jinja"],
23
+ "rank4_cache2_pos_quad": ["rotary-embedding-quad.wgsl.jinja"],
24
+ "rank4_cache2_pos": ["rotary-embedding.wgsl.jinja"],
25
+ "rank3_cache3_quad": ["rotary-embedding-quad.wgsl.jinja"],
26
+ "rank3_cache3": ["rotary-embedding.wgsl.jinja"],
27
+ "rank4_cache3_quad": ["rotary-embedding-quad.wgsl.jinja"],
28
+ "rank4_cache3": ["rotary-embedding.wgsl.jinja"]
29
+ }
30
+ }
31
  }
build/webgpu/rotary-embedding-quad.wgsl.jinja ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
+ {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
+ {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
+ {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
+ {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
+ {% elif note == "element-limit" %}
15
+ // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
+ {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
+ {% endif %}
21
+ {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
+ {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
+ if ({{ name }} >= {{ bound }}) { return; }
26
+ {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
+ if ({{ name }} >= {{ bound }}) {
29
+ return;
30
+ }
31
+ {%- endif %}
32
+ {% endmacro %}
33
+
34
+ {% if usesF16 %}enable f16;
35
+ {% endif %}{{ env.wgsl.resourceDeclarations }}
36
+ // One invocation owns four consecutive rotated pairs, so x, cos and sin are read as vec4s and
37
+ // both rotated outputs are written as vec4s; the head size and rotary dim are multiples of
38
+ // eight, so no quad straddles a head or the rotated/tail boundary. Partial rotations use the
39
+ // remaining quad lanes to copy two unchanged tail vec4s. Every index below counts vec4s.
40
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
41
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
42
+ {{ flat_index_2d("q", "params.quadCount", note="") }}
43
+
44
+ let task = q % params.headQuads;
45
+ let headFlat = q / params.headQuads;
46
+ let base = headFlat * params.headVec4;
47
+
48
+ if (task >= params.halfRotaryVec4) {
49
+ let tail = params.rotaryVec4 + (task - params.halfRotaryVec4) * 2u;
50
+ y[base + tail] = x[base + tail];
51
+ y[base + tail + 1u] = x[base + tail + 1u];
52
+ return;
53
+ }
54
+
55
+ let pair = task;
56
+ {% if rank == 3 %}
57
+ let token = (headFlat / params.numHeads) % params.sequenceLength;
58
+ let batch = headFlat / (params.numHeads * params.sequenceLength);
59
+ {% else %}
60
+ let token = headFlat % params.sequenceLength;
61
+ let batch = (headFlat / params.sequenceLength) / params.numHeads;
62
+ {% endif %}
63
+ {% if hasPositionIds is defined and hasPositionIds %}
64
+ // Gather the 2D cache tables through this token's position index.
65
+ let cache = u32(position_ids[batch * params.sequenceLength + token]) * params.halfRotaryVec4 + pair;
66
+ {% else %}
67
+ // The standard no-index cache owns one row per (batch, token).
68
+ let cache = batch * params.cacheBatchStrideVec4 + token * params.halfRotaryVec4 + pair;
69
+ {% endif %}
70
+ let cf = vec4<f32>(cos_cache[cache]);
71
+ let sf = vec4<f32>(sin_cache[cache]);
72
+ {% if interleaved %}
73
+ // Four interleaved pairs span two vec4s: (x0.x, x0.y), (x0.z, x0.w), (x1.x, x1.y), (x1.z, x1.w).
74
+ let x0 = vec4<f32>(x[base + pair * 2u]);
75
+ let x1 = vec4<f32>(x[base + pair * 2u + 1u]);
76
+ let a = vec4<f32>(x0.x, x0.z, x1.x, x1.z);
77
+ let b = vec4<f32>(x0.y, x0.w, x1.y, x1.w);
78
+ let ra = a * cf - b * sf;
79
+ let rb = a * sf + b * cf;
80
+ y[base + pair * 2u] = {{ vector }}(vec4<f32>(ra.x, rb.x, ra.y, rb.y));
81
+ y[base + pair * 2u + 1u] = {{ vector }}(vec4<f32>(ra.z, rb.z, ra.w, rb.w));
82
+ {% else %}
83
+ let a = vec4<f32>(x[base + pair]);
84
+ let b = vec4<f32>(x[base + params.halfRotaryVec4 + pair]);
85
+ y[base + pair] = {{ vector }}(a * cf - b * sf);
86
+ y[base + params.halfRotaryVec4 + pair] = {{ vector }}(a * sf + b * cf);
87
+ {% endif %}
88
+ }
build/webgpu/rotary-embedding.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
  }
@@ -36,7 +36,7 @@
36
  // One invocation owns both outputs of a rotated pair, so x/cos/sin are read once.
37
  // Partial rotations use the remaining pair lanes to copy two unchanged tail values.
38
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
39
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
40
  {{ flat_index_2d("p", "params.pairCount", note="") }}
41
 
42
  let tasksPerHead = params.headSize / 2u;
@@ -52,14 +52,14 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
52
  }
53
 
54
  let pair = task;
55
- {% if source.rank == 3 %}
56
  let token = (headFlat / params.numHeads) % params.sequenceLength;
57
  let batch = headFlat / (params.numHeads * params.sequenceLength);
58
  {% else %}
59
  let token = headFlat % params.sequenceLength;
60
  let batch = (headFlat / params.sequenceLength) / params.numHeads;
61
  {% endif %}
62
- {% if source.hasPositionIds is defined and source.hasPositionIds %}
63
  // Gather the 2D cache tables through this token's position index.
64
  let cache = u32(position_ids[batch * params.sequenceLength + token]) * params.halfRotaryDim + pair;
65
  {% else %}
 
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
  }
 
36
  // One invocation owns both outputs of a rotated pair, so x/cos/sin are read once.
37
  // Partial rotations use the remaining pair lanes to copy two unchanged tail values.
38
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
39
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
40
  {{ flat_index_2d("p", "params.pairCount", note="") }}
41
 
42
  let tasksPerHead = params.headSize / 2u;
 
52
  }
53
 
54
  let pair = task;
55
+ {% if rank == 3 %}
56
  let token = (headFlat / params.numHeads) % params.sequenceLength;
57
  let batch = headFlat / (params.numHeads * params.sequenceLength);
58
  {% else %}
59
  let token = headFlat % params.sequenceLength;
60
  let batch = (headFlat / params.sequenceLength) / params.numHeads;
61
  {% endif %}
62
+ {% if hasPositionIds is defined and hasPositionIds %}
63
  // Gather the 2D cache tables through this token's position index.
64
  let cache = u32(position_ids[batch * params.sequenceLength + token]) * params.halfRotaryDim + pair;
65
  {% else %}
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.RotaryEmbedding",
3
  "fixtureArrays": {
4
  "position_id_cos_table": [1, 0.994, 0.9553, 0.9171, 0.8253, 0.7584, 0.6216, 0.5319, 0.3624, 0.2579, 0.0707, -0.0392, -0.2272, -0.3327, -0.5048, -0.5966, -0.7374, -0.8071, -0.9041, -0.9455, -0.99, -0.9995, -0.9875, -0.9642, -0.8968, -0.8428, -0.7259, -0.646, -0.4903, -0.3916, -0.2108, -0.1022],
5
  "position_id_sin_table": [0, 0.1692, 0.2955, 0.4529, 0.5646, 0.6961, 0.7833, 0.8772, 0.932, 0.9799, 0.9975, 0.9951, 0.9738, 0.9214, 0.8632, 0.7654, 0.6755, 0.541, 0.4274, 0.2683, 0.1411, -0.0284, -0.1577, -0.3225, -0.4425, -0.5879, -0.6878, -0.8007, -0.8716, -0.942, -0.9775, -0.9991],
@@ -34,7 +33,7 @@
34
  "provenance": {
35
  "source": "onnxruntime/test/providers/cpu/llm/rotary_embedding_op_test.cc",
36
  "test": "RotaryEmbedding",
37
- "notes": "Rank-4 companion for identity-angle rotary embedding preserving subnormal activations."
38
  },
39
  "inputs": {
40
  "x": { "dtype": "float32", "shape": [1, 1, 1, 2], "data": { "kind": "values", "values": [1e-40, -1e-40] } },
@@ -628,7 +627,7 @@
628
  {
629
  "name": "dispatch_fold_rank4_pair_last_row_seq65540",
630
  "provenance": {
631
- "notes": "Regression guard for the 2D dispatch fold: pairCount = numel(X)/2 = 16,781,440 -> ceil(/256)=65540 > 65535 forces the gid.y high-bit fold. Confirms the highest pair indices are written at the maxComputeWorkgroupsPerDimension cliff. Periodic inputs preserve coverage without evaluating 33M host transcendentals."
632
  },
633
  "inputs": {
634
  "x": {
@@ -825,6 +824,112 @@
825
  "test": "RotaryEmbedding position_ids gather (opset-23 input 3)",
826
  "notes": "A standard [1,1] position table selects cache row 9 for the single decode token; both heads use that row."
827
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
828
  }
829
  ]
830
  }
 
1
  {
 
2
  "fixtureArrays": {
3
  "position_id_cos_table": [1, 0.994, 0.9553, 0.9171, 0.8253, 0.7584, 0.6216, 0.5319, 0.3624, 0.2579, 0.0707, -0.0392, -0.2272, -0.3327, -0.5048, -0.5966, -0.7374, -0.8071, -0.9041, -0.9455, -0.99, -0.9995, -0.9875, -0.9642, -0.8968, -0.8428, -0.7259, -0.646, -0.4903, -0.3916, -0.2108, -0.1022],
4
  "position_id_sin_table": [0, 0.1692, 0.2955, 0.4529, 0.5646, 0.6961, 0.7833, 0.8772, 0.932, 0.9799, 0.9975, 0.9951, 0.9738, 0.9214, 0.8632, 0.7654, 0.6755, 0.541, 0.4274, 0.2683, 0.1411, -0.0284, -0.1577, -0.3225, -0.4425, -0.5879, -0.6878, -0.8007, -0.8716, -0.942, -0.9775, -0.9991],
 
33
  "provenance": {
34
  "source": "onnxruntime/test/providers/cpu/llm/rotary_embedding_op_test.cc",
35
  "test": "RotaryEmbedding",
36
+ "notes": "A rank-4 identity-angle rotary embedding must preserve subnormal activations."
37
  },
38
  "inputs": {
39
  "x": { "dtype": "float32", "shape": [1, 1, 1, 2], "data": { "kind": "values", "values": [1e-40, -1e-40] } },
 
627
  {
628
  "name": "dispatch_fold_rank4_pair_last_row_seq65540",
629
  "provenance": {
630
+ "notes": "Shape [1,1,65540,512] requires 65,540 workgroups for paired rotation, so dispatch folds into two dimensions. The highest pair indices must still be written with the correct periodic values."
631
  },
632
  "inputs": {
633
  "x": {
 
824
  "test": "RotaryEmbedding position_ids gather (opset-23 input 3)",
825
  "notes": "A standard [1,1] position table selects cache row 9 for the single decode token; both heads use that row."
826
  }
827
+ },
828
+ {
829
+ "name": "quad_interleaved_rank3_head16_multihead_f16",
830
+ "provenance": {
831
+ "notes": "Interleaved rotation on the quad route: head size 16 over 2 heads with f16 storage, so each invocation rotates four interleaved pairs read as two vec4s and writes them back in the same interleaving."
832
+ },
833
+ "attrs": { "interleaved": 1, "num_heads": 2 },
834
+ "inputs": {
835
+ "x": {
836
+ "dtype": "float16",
837
+ "shape": [1, 3, 32],
838
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.13, "cosStep": 0.21 }
839
+ },
840
+ "cos": {
841
+ "dtype": "float16",
842
+ "shape": [1, 3, 8],
843
+ "data": { "kind": "rotaryCos", "thetaStart": 0.05, "thetaStep": 0.07 }
844
+ },
845
+ "sin": {
846
+ "dtype": "float16",
847
+ "shape": [1, 3, 8],
848
+ "data": { "kind": "rotarySin", "thetaStart": 0.05, "thetaStep": 0.07 }
849
+ }
850
+ },
851
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 3, 32], "tolerance": 0.002 } }
852
+ },
853
+ {
854
+ "name": "quad_partial_rotary_tail_rank4_head24_rd8",
855
+ "provenance": {
856
+ "notes": "Partial rotation on the quad route: rotary dim 8 of head size 24 leaves a 16-element tail that the remaining quad lanes copy as two vec4s each, with a batch-strided cache."
857
+ },
858
+ "attrs": { "rotary_embedding_dim": 8 },
859
+ "inputs": {
860
+ "x": {
861
+ "dtype": "float32",
862
+ "shape": [2, 2, 3, 24],
863
+ "data": { "kind": "fillFloat32", "scale": 0.45, "sinStep": 0.19, "cosStep": 0.11 }
864
+ },
865
+ "cos": {
866
+ "dtype": "float32",
867
+ "shape": [2, 3, 4],
868
+ "data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.31, "cosStep": 0.17 }
869
+ },
870
+ "sin": {
871
+ "dtype": "float32",
872
+ "shape": [2, 3, 4],
873
+ "data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.23, "cosStep": 0.29 }
874
+ }
875
+ },
876
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 3, 24], "tolerance": 0.000001 } }
877
+ },
878
+ {
879
+ "name": "quad_pos_ids_rank3_head16_gather",
880
+ "provenance": {
881
+ "notes": "Position-id gather on the quad route: 2D cache tables of half width 8 are gathered per (batch, token) as vec4 rows, head size 16 over 2 heads."
882
+ },
883
+ "attrs": { "num_heads": 2 },
884
+ "inputs": {
885
+ "x": {
886
+ "dtype": "float32",
887
+ "shape": [2, 3, 32],
888
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.23 }
889
+ },
890
+ "cos": {
891
+ "dtype": "float32",
892
+ "shape": [12, 8],
893
+ "data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.27, "cosStep": 0.13 }
894
+ },
895
+ "sin": {
896
+ "dtype": "float32",
897
+ "shape": [12, 8],
898
+ "data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.19, "cosStep": 0.31 }
899
+ },
900
+ "positionIds": {
901
+ "dtype": "uint32",
902
+ "shape": [2, 3],
903
+ "data": { "kind": "values", "values": [4, 5, 11, 0, 2, 9] }
904
+ }
905
+ },
906
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 32], "tolerance": 0.0001 } }
907
+ },
908
+ {
909
+ "name": "quad_pos_ids_rank4_interleaved_head8",
910
+ "provenance": {
911
+ "notes": "Position-id gather with interleaved rotation on the rank-4 quad route: head size 8 is exactly one quad per head, so the rotate lanes cover every element and the tail branch never runs."
912
+ },
913
+ "attrs": { "interleaved": 1 },
914
+ "inputs": {
915
+ "x": {
916
+ "dtype": "float32",
917
+ "shape": [1, 2, 3, 8],
918
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.11, "cosStep": 0.29 }
919
+ },
920
+ "cos": {
921
+ "dtype": "float32",
922
+ "shape": [8, 4],
923
+ "data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.33, "cosStep": 0.21 }
924
+ },
925
+ "sin": {
926
+ "dtype": "float32",
927
+ "shape": [8, 4],
928
+ "data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.15, "cosStep": 0.37 }
929
+ },
930
+ "positionIds": { "dtype": "uint32", "shape": [1, 3], "data": { "kind": "values", "values": [7, 0, 3] } }
931
+ },
932
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 3, 8], "tolerance": 0.0001 } }
933
  }
934
  ]
935
  }