Xenova HF Staff commited on
Commit
30e6997
·
verified ·
1 Parent(s): 2fa2da0

sync 2e7068faf55e

Browse files
README.md CHANGED
@@ -1,3 +1,79 @@
1
  ---
 
2
  license: apache-2.0
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ library_name: kernels
3
  license: apache-2.0
4
+ tags:
5
+ - kernel
6
+ - webgpu
7
+ - wgsl
8
  ---
9
+ # ai.onnx.Transpose
10
+
11
+ `ai.onnx` · standard ONNX operator · ONNX opset ≥ 13
12
+
13
+ ## Description
14
+
15
+ Transposes the input tensor by permuting its axes according to the `perm` attribute. Axis `i` of the output corresponds to axis `perm[i]` of the input; if `perm` is omitted, the axes are reversed (`n-1, ..., 0`).
16
+
17
+ See the [ONNX `Transpose` spec](https://onnx.ai/onnx/operators/onnx__Transpose.html) for the reference semantics.
18
+
19
+ ## Inputs
20
+
21
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- | --- |
23
+ | `data` | `x` | `T` | — | — | The input tensor to transpose. | required |
24
+
25
+ ## Outputs
26
+
27
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
28
+ | --- | --- | --- | --- | --- | --- | --- |
29
+ | `transposed` | `y` | `T` | same as `data` | — | The transposed output tensor with permuted axes. | required |
30
+
31
+ ## Attributes
32
+
33
+ Attributes and default values (overridable per request):
34
+
35
+ | Attribute | Default | Description |
36
+ | --- | --- | --- |
37
+ | `perm` | — | Optional permutation of the input axes. It must contain every axis from 0 through rank - 1 exactly once. When omitted, the axes are reversed. |
38
+
39
+ ## Type constraints
40
+
41
+ | Variable | Allowed dtypes |
42
+ | --- | --- |
43
+ | `T` | `float32`, `float16`, `int32`, `int16`, `uint32`, `uint8`, `int8`, `bool` |
44
+
45
+ ## Files
46
+
47
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
48
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
49
+ - [`test.json`](build/webgpu/test.json) — correctness cases
50
+ - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
51
+ - [`datamove-flat-copy.wgsl.jinja`](build/webgpu/datamove-flat-copy.wgsl.jinja)
52
+ - [`datamove-transpose-2d-tiled-scalar.wgsl.jinja`](build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja)
53
+ - [`datamove-transpose-2d-tiled.wgsl.jinja`](build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja)
54
+ - [`datamove-transpose-vec4.wgsl.jinja`](build/webgpu/datamove-transpose-vec4.wgsl.jinja)
55
+ - [`transpose.wgsl.jinja`](build/webgpu/transpose.wgsl.jinja)
56
+
57
+ ## Use with `@huggingface/kernels`
58
+
59
+ The loader automatically allocates outputs whose metadata it can derive from the manifest contract and this call.
60
+
61
+ The explicit `outputs` entries provide shape and logical dtype metadata for the results listed below:
62
+
63
+ - `y`
64
+
65
+ Each entry either requests an optional result or supplies metadata that cannot be inferred from the inputs.
66
+
67
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
68
+
69
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
70
+
71
+ ```js
72
+ import { getKernel } from "@huggingface/kernels";
73
+
74
+ const kernel = await getKernel("webgpu-kernels/ai.onnx.Transpose", { version: 1 });
75
+ // Explicit destinations request optional results or supply metadata that cannot be inferred.
76
+ const { y } = await kernel({ x: { data: xData, shape: [] } }, {
77
+ outputs: { y: { shape: [], dtype: "float32" } },
78
+ });
79
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Transpose",
3
+ "tunableSpace": {
4
+ "WORKGROUP_SIZE": [64, 128, 256],
5
+ "INNER_VEC4_ITEMS_PER_THREAD": [1, 2, 4, 8],
6
+ "INNER_VEC4_MIN_THREADS": [16384, 65536, 262144]
7
+ },
8
+ "cases": [
9
+ {
10
+ "name": "transpose-f32-1024x1024",
11
+ "preset": "smoke",
12
+ "vars": { "rows": 1024, "cols": 1024 },
13
+ "attrs": { "perm": [1, 0] },
14
+ "inputs": { "x": { "shape": [1024, 1024], "dtype": "float32", "dist": "normal", "seed": 120, "scale": 0.2 } },
15
+ "outputs": { "y": { "shape": [1024, 1024], "dtype": "float32" } },
16
+ "bench": { "primary": true, "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * 4 * 2" }] }
17
+ },
18
+ {
19
+ "name": "transpose-f32-rank1-1m",
20
+ "preset": "smoke",
21
+ "vars": { "count": 1048576, "dtype": "float32" },
22
+ "inputs": { "x": { "shape": [1048576], "dtype": "float32", "dist": "normal", "seed": 121, "scale": 0.2 } },
23
+ "outputs": { "y": { "shape": [1048576], "dtype": "float32" } },
24
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
25
+ },
26
+ {
27
+ "name": "transpose-tiled-f32-2048x2048",
28
+ "preset": "smoke",
29
+ "vars": { "rows": 2048, "cols": 2048, "dtype": "float32" },
30
+ "attrs": { "perm": [1, 0] },
31
+ "inputs": { "x": { "shape": [2048, 2048], "dtype": "float32", "dist": "normal", "seed": 122, "scale": 0.2 } },
32
+ "outputs": { "y": { "shape": [2048, 2048], "dtype": "float32" } },
33
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
34
+ },
35
+ {
36
+ "name": "transpose-tiled-f16-4096x1024",
37
+ "preset": "smoke",
38
+ "vars": { "rows": 4096, "cols": 1024, "dtype": "float16" },
39
+ "attrs": { "perm": [1, 0] },
40
+ "inputs": { "x": { "shape": [4096, 1024], "dtype": "float16", "dist": "normal", "seed": 123, "scale": 0.2 } },
41
+ "outputs": { "y": { "shape": [1024, 4096], "dtype": "float16" } },
42
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
43
+ },
44
+ {
45
+ "name": "transpose-batched-f32-8x512x256",
46
+ "preset": "smoke",
47
+ "vars": { "batch": 8, "rows": 512, "cols": 256, "dtype": "float32" },
48
+ "attrs": { "perm": [0, 2, 1] },
49
+ "inputs": { "x": { "shape": [8, 512, 256], "dtype": "float32", "dist": "normal", "seed": 124, "scale": 0.2 } },
50
+ "outputs": { "y": { "shape": [8, 256, 512], "dtype": "float32" } },
51
+ "bench": {
52
+ "metrics": [{ "type": "bandwidth", "value": "args.batch * args.rows * args.cols * dtypeBytes(args.dtype) * 2" }]
53
+ }
54
+ },
55
+ {
56
+ "name": "transpose-inner-vec4-f32-8x64x32x128",
57
+ "preset": "smoke",
58
+ "vars": { "count": 2097152, "dtype": "float32" },
59
+ "attrs": { "perm": [0, 2, 1, 3] },
60
+ "inputs": { "x": { "shape": [8, 64, 32, 128], "dtype": "float32", "dist": "normal", "seed": 125, "scale": 0.2 } },
61
+ "outputs": { "y": { "shape": [8, 32, 64, 128], "dtype": "float32" } },
62
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
63
+ },
64
+ {
65
+ "name": "transpose-identity-f32-4m",
66
+ "preset": "edge",
67
+ "vars": { "count": 4194304, "dtype": "float32" },
68
+ "attrs": { "perm": [0, 1] },
69
+ "inputs": { "x": { "shape": [2048, 2048], "dtype": "float32", "dist": "normal", "seed": 126, "scale": 0.2 } },
70
+ "outputs": { "y": { "shape": [2048, 2048], "dtype": "float32" } },
71
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
72
+ },
73
+ {
74
+ "name": "transpose-scalar-f32-1024x1023",
75
+ "preset": "edge",
76
+ "vars": { "rows": 1024, "cols": 1023, "dtype": "float32" },
77
+ "attrs": { "perm": [1, 0] },
78
+ "inputs": { "x": { "shape": [1024, 1023], "dtype": "float32", "dist": "normal", "seed": 130, "scale": 0.2 } },
79
+ "outputs": { "y": { "shape": [1023, 1024], "dtype": "float32" } },
80
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
81
+ },
82
+ {
83
+ "name": "transpose-scalar-f32-768x50257-vocab",
84
+ "preset": "edge",
85
+ "vars": { "rows": 768, "cols": 50257, "dtype": "float32" },
86
+ "attrs": { "perm": [1, 0] },
87
+ "inputs": { "x": { "shape": [768, 50257], "dtype": "float32", "dist": "normal", "seed": 131, "scale": 0.2 } },
88
+ "outputs": { "y": { "shape": [50257, 768], "dtype": "float32" } },
89
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
90
+ },
91
+ {
92
+ "name": "transpose-scalar-f32-rank3-8x512x255",
93
+ "preset": "edge",
94
+ "vars": { "batch": 8, "rows": 512, "cols": 255, "dtype": "float32" },
95
+ "attrs": { "perm": [0, 2, 1] },
96
+ "inputs": { "x": { "shape": [8, 512, 255], "dtype": "float32", "dist": "normal", "seed": 132, "scale": 0.2 } },
97
+ "outputs": { "y": { "shape": [8, 255, 512], "dtype": "float32" } },
98
+ "bench": {
99
+ "metrics": [{ "type": "bandwidth", "value": "args.batch * args.rows * args.cols * dtypeBytes(args.dtype) * 2" }]
100
+ }
101
+ },
102
+ {
103
+ "name": "transpose-tiled-i32-1024x1024",
104
+ "preset": "edge",
105
+ "vars": { "rows": 1024, "cols": 1024, "dtype": "int32" },
106
+ "attrs": { "perm": [1, 0] },
107
+ "inputs": {
108
+ "x": { "shape": [1024, 1024], "dtype": "int32", "dist": "randint", "seed": 133, "min": -1000, "max": 1000 }
109
+ },
110
+ "outputs": { "y": { "shape": [1024, 1024], "dtype": "int32" } },
111
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
112
+ },
113
+ {
114
+ "name": "transpose-scalar-f32-rank4-nhwc-to-nchw-8x64x96x96",
115
+ "preset": "stress",
116
+ "vars": { "n": 8, "h": 64, "w": 96, "c": 96, "dtype": "float32" },
117
+ "attrs": { "perm": [0, 3, 1, 2] },
118
+ "inputs": { "x": { "shape": [8, 64, 96, 96], "dtype": "float32", "dist": "normal", "seed": 140, "scale": 0.2 } },
119
+ "outputs": { "y": { "shape": [8, 96, 64, 96], "dtype": "float32" } },
120
+ "bench": {
121
+ "metrics": [{ "type": "bandwidth", "value": "args.n * args.h * args.w * args.c * dtypeBytes(args.dtype) * 2" }]
122
+ }
123
+ },
124
+ {
125
+ "name": "transpose-scalar-f32-rank3-default-reverse-96x200x128",
126
+ "preset": "stress",
127
+ "vars": { "d0": 96, "d1": 200, "d2": 128, "dtype": "float32" },
128
+ "inputs": { "x": { "shape": [96, 200, 128], "dtype": "float32", "dist": "normal", "seed": 141, "scale": 0.2 } },
129
+ "outputs": { "y": { "shape": [128, 200, 96], "dtype": "float32" } },
130
+ "bench": {
131
+ "metrics": [{ "type": "bandwidth", "value": "args.d0 * args.d1 * args.d2 * dtypeBytes(args.dtype) * 2" }]
132
+ }
133
+ },
134
+ {
135
+ "name": "transpose-scalar-f32-rank5-ncdhw-to-ndhwc-volumetric",
136
+ "preset": "stress",
137
+ "vars": { "count": 8388608, "dtype": "float32" },
138
+ "attrs": { "perm": [0, 2, 3, 4, 1] },
139
+ "inputs": {
140
+ "x": { "shape": [1, 64, 32, 64, 64], "dtype": "float32", "dist": "normal", "seed": 142, "scale": 0.2 }
141
+ },
142
+ "outputs": { "y": { "shape": [1, 32, 64, 64, 64], "dtype": "float32", "dist": "empty" } },
143
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
144
+ },
145
+ {
146
+ "name": "transpose-scalar-f32-rank5-ndhwc-to-ncdhw-volumetric",
147
+ "preset": "stress",
148
+ "vars": { "count": 8388608, "dtype": "float32" },
149
+ "attrs": { "perm": [0, 4, 1, 2, 3] },
150
+ "inputs": {
151
+ "x": { "shape": [1, 32, 64, 64, 64], "dtype": "float32", "dist": "normal", "seed": 143, "scale": 0.2 }
152
+ },
153
+ "outputs": { "y": { "shape": [1, 64, 32, 64, 64], "dtype": "float32", "dist": "empty" } },
154
+ "bench": {
155
+ "primary": true,
156
+ "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }]
157
+ }
158
+ },
159
+ {
160
+ "name": "transpose-f32-rank4-nchw-to-nhwc-8x64x96x96",
161
+ "preset": "stress",
162
+ "vars": { "n": 8, "h": 64, "w": 96, "c": 96, "dtype": "float32" },
163
+ "attrs": { "perm": [0, 2, 3, 1] },
164
+ "inputs": { "x": { "shape": [8, 64, 96, 96], "dtype": "float32", "dist": "normal", "seed": 140, "scale": 0.2 } },
165
+ "outputs": { "y": { "shape": [8, 96, 96, 64], "dtype": "float32" } },
166
+ "bench": {
167
+ "metrics": [{ "type": "bandwidth", "value": "args.n * args.h * args.w * args.c * dtypeBytes(args.dtype) * 2" }]
168
+ }
169
+ },
170
+ {
171
+ "name": "transpose-unitdim-memcpy-f32-4096x1024x1-perm201",
172
+ "preset": "smoke",
173
+ "vars": { "tokens": 4096, "hidden": 1024, "dtype": "float32" },
174
+ "attrs": { "perm": [2, 0, 1] },
175
+ "inputs": { "x": { "shape": [4096, 1024, 1], "dtype": "float32", "dist": "normal", "seed": 141, "scale": 0.2 } },
176
+ "outputs": { "y": { "shape": [1, 4096, 1024], "dtype": "float32" } },
177
+ "bench": {
178
+ "metrics": [{ "type": "bandwidth", "value": "args.tokens * args.hidden * dtypeBytes(args.dtype) * 2" }]
179
+ }
180
+ },
181
+ {
182
+ "name": "transpose-inner-vec4-f32-dinov2-qkv-1814x3x6x64",
183
+ "preset": "all",
184
+ "provenance": {
185
+ "source": "Depth Anything V2 Small backbone",
186
+ "notes": "Depth Anything V2 Small's stacked q/k/v permute at 518x686, repeated once per DINOv2 layer. It exercises the inner-vec4 route at the model's 1814-row shape."
187
+ },
188
+ "vars": { "count": 2089728, "dtype": "float32" },
189
+ "attrs": { "perm": [1, 2, 0, 3] },
190
+ "inputs": { "x": { "shape": [1814, 3, 6, 64], "dtype": "float32", "dist": "normal", "seed": 9411, "scale": 0.2 } },
191
+ "outputs": { "y": { "shape": [3, 6, 1814, 64], "dtype": "float32" } },
192
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
193
+ },
194
+ {
195
+ "name": "transpose-f32-dpt-patchgrid-1813x384",
196
+ "preset": "all",
197
+ "provenance": {
198
+ "source": "Depth Anything V2 Small neck reassemble",
199
+ "notes": "Probe pair (a): the real 37x49 patch grid has 1813 rows and is not vec4-aligned. Together with pair (b), it verifies that extent parity does not determine whether the transpose is staged."
200
+ },
201
+ "vars": { "count": 696192, "dtype": "float32" },
202
+ "attrs": { "perm": [1, 0] },
203
+ "inputs": { "x": { "shape": [1813, 384], "dtype": "float32", "dist": "normal", "seed": 9441, "scale": 0.2 } },
204
+ "outputs": { "y": { "shape": [384, 1813], "dtype": "float32" } },
205
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
206
+ },
207
+ {
208
+ "name": "transpose-f32-dpt-patchgrid-1812x384-aligned",
209
+ "preset": "all",
210
+ "provenance": {
211
+ "source": "Depth Anything V2 Small neck reassemble",
212
+ "notes": "Probe pair (b): the same transpose one row shorter, making the row extent vec4-aligned. It is the aligned control for pair (a)."
213
+ },
214
+ "vars": { "count": 695808, "dtype": "float32" },
215
+ "attrs": { "perm": [1, 0] },
216
+ "inputs": { "x": { "shape": [1812, 384], "dtype": "float32", "dist": "normal", "seed": 9451, "scale": 0.2 } },
217
+ "outputs": { "y": { "shape": [384, 1812], "dtype": "float32" } },
218
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "args.count * dtypeBytes(args.dtype) * 2" }] }
219
+ }
220
+ ]
221
+ }
build/webgpu/datamove-flat-copy.wgsl.jinja ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if usesF16 %}
2
+ enable f16;
3
+ {% endif %}
4
+ {{ env.wgsl.resourceDeclarations }}
5
+
6
+ const COUNT: u32 = {{ source.count }}u;
7
+ const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
8
+
9
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
10
+ fn main(
11
+ @builtin(global_invocation_id) gid: vec3<u32>,
12
+ @builtin(num_workgroups) nwg: vec3<u32>
13
+ ) {
14
+ let stride = nwg.x * WG;
15
+ for (var i = gid.x; i < COUNT; i += stride) {
16
+ dst[i] = src[i];
17
+ }
18
+ }
build/webgpu/datamove-transpose-2d-tiled-scalar.wgsl.jinja ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Tiled, optionally batched 2-D transpose over scalar elements.
2
+ //
3
+ // A 32x32 tile enters workgroup memory through coalesced reads and leaves
4
+ // through coalesced writes. Guarded edges make this valid for any extent,
5
+ // including shapes that cannot use the aligned vec4 path and would otherwise
6
+ // fall back to a generic copy whose reads and writes cannot both be coalesced.
7
+ //
8
+ // Layout contract:
9
+ // - src is row-major [batch, ROWS, COLS]
10
+ // - dst is row-major [batch, COLS, ROWS]
11
+ // - dispatch is x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
12
+ //
13
+ // The extra column avoids bank conflicts on 32-bank GPUs.
14
+ {% if usesF16 %}
15
+ enable f16;
16
+ {% endif %}
17
+ {{ env.wgsl.resourceDeclarations }}
18
+
19
+ const ROWS: u32 = {{ source.rows }}u;
20
+ const COLS: u32 = {{ source.cols }}u;
21
+ const MAT: u32 = {{ source.rows * source.cols }}u;
22
+ const TILE: u32 = 32u;
23
+ const ROW_STEP: u32 = 8u;
24
+
25
+ var<workgroup> tile: array<array<{{ scalar }}, 33>, 32>;
26
+
27
+ @compute @workgroup_size(32, 8, 1)
28
+ fn main(
29
+ @builtin(workgroup_id) wid: vec3<u32>,
30
+ @builtin(local_invocation_id) lid: vec3<u32>
31
+ ) {
32
+ let batch_base = wid.z * MAT;
33
+ let row_base = wid.y * TILE;
34
+ let col_base = wid.x * TILE;
35
+
36
+ // Read: lid.x runs along the source row, so a subgroup covers 32 consecutive
37
+ // source elements.
38
+ for (var i = 0u; i < TILE; i = i + ROW_STEP) {
39
+ let r = row_base + lid.y + i;
40
+ let c = col_base + lid.x;
41
+ if (r < ROWS && c < COLS) {
42
+ tile[lid.y + i][lid.x] = x[batch_base + r * COLS + c];
43
+ }
44
+ }
45
+ workgroupBarrier();
46
+
47
+ // Write: lid.x runs along the destination row, which is the source column
48
+ // direction — the transpose happens in the tile index, not in the memory
49
+ // access, so this side is coalesced too.
50
+ for (var i = 0u; i < TILE; i = i + ROW_STEP) {
51
+ let out_row = col_base + lid.y + i;
52
+ let out_col = row_base + lid.x;
53
+ if (out_row < COLS && out_col < ROWS) {
54
+ y[batch_base + out_row * ROWS + out_col] = tile[lid.x][lid.y + i];
55
+ }
56
+ }
57
+ }
build/webgpu/datamove-transpose-2d-tiled.wgsl.jinja ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Tiled 2D transpose (optionally batched) over vec4 elements.
2
+ //
3
+ // Layout contract:
4
+ // - src is row-major [batch, ROWS, COLS], dst is row-major [batch, COLS, ROWS]
5
+ // - ROWS % 4 == 0 and COLS % 4 == 0, so both buffers bind as vec4<T>
6
+ // - dispatch: x = ceil(COLS / 32), y = ceil(ROWS / 32), z = batch
7
+ //
8
+ // Each 8x32 workgroup moves one 32x32 scalar tile through workgroup memory, so
9
+ // both global loads and global stores are coalesced vec4 accesses. The +1
10
+ // column pad avoids bank conflicts on 32-bank GPUs.
11
+ {% if usesF16 %}
12
+ enable f16;
13
+ {% endif %}
14
+ {{ env.wgsl.resourceDeclarations }}
15
+
16
+ const ROWS: u32 = {{ source.rows }}u;
17
+ const COLS: u32 = {{ source.cols }}u;
18
+ const ROWS_V: u32 = {{ ((source.rows / 4) | int) }}u;
19
+ const COLS_V: u32 = {{ ((source.cols / 4) | int) }}u;
20
+ const MAT_V: u32 = {{ (((source.rows * source.cols) / 4) | int) }}u;
21
+ const TILE: u32 = 32u;
22
+
23
+ var<workgroup> tile: array<array<{{ scalar }}, 33>, 32>;
24
+
25
+ @compute @workgroup_size(8, 32, 1)
26
+ fn main(
27
+ @builtin(workgroup_id) wid: vec3<u32>,
28
+ @builtin(local_invocation_id) lid: vec3<u32>
29
+ ) {
30
+ let batch_base = wid.z * MAT_V;
31
+ let row_base = wid.y * TILE;
32
+ let col_base = wid.x * TILE;
33
+
34
+ let r = row_base + lid.y;
35
+ let cv = (col_base / 4u) + lid.x;
36
+ if (r < ROWS && cv < COLS_V) {
37
+ let v = src[batch_base + r * COLS_V + cv];
38
+ tile[lid.y][lid.x * 4u + 0u] = v.x;
39
+ tile[lid.y][lid.x * 4u + 1u] = v.y;
40
+ tile[lid.y][lid.x * 4u + 2u] = v.z;
41
+ tile[lid.y][lid.x * 4u + 3u] = v.w;
42
+ }
43
+ workgroupBarrier();
44
+
45
+ let out_row = col_base + lid.y;
46
+ let rv = (row_base / 4u) + lid.x;
47
+ if (out_row < COLS && rv < ROWS_V) {
48
+ dst[batch_base + out_row * ROWS_V + rv] = {{ vectorScalar }}(
49
+ tile[lid.x * 4u + 0u][lid.y],
50
+ tile[lid.x * 4u + 1u][lid.y],
51
+ tile[lid.x * 4u + 2u][lid.y],
52
+ tile[lid.x * 4u + 3u][lid.y]
53
+ );
54
+ }
55
+ }
build/webgpu/datamove-transpose-vec4.wgsl.jinja ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Permuted copy over vec4 elements for permutations that keep the innermost
2
+ // axis in place. Its dimension is a multiple of four, so both buffers bind as
3
+ // vec4<T>. The index fold uses effective shapes whose last dimension is divided
4
+ // by four, enabling 128-bit loads and stores.
5
+ {% if usesF16 %}
6
+ enable f16;
7
+ {% endif %}
8
+ {{ env.wgsl.resourceDeclarations }}
9
+
10
+ const COUNT: u32 = {{ source.count }}u;
11
+ const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
12
+
13
+ fn input_offset(out_index: u32) -> u32 {
14
+ var rem = out_index;
15
+ var offset = 0u;
16
+ {% for out_axis in range(source.rank) %}
17
+ {% set out_stride = namespace(value=1) %}
18
+ {% for j in range(out_axis + 1, source.rank) %}
19
+ {% set dj = ((source.outputShape[j] / 4) | int) if j == source.rank - 1 else source.outputShape[j] %}
20
+ {% set out_stride.value = out_stride.value * dj %}
21
+ {% endfor %}
22
+ {% set in_axis = source.perm[out_axis] %}
23
+ {% set in_stride = namespace(value=1) %}
24
+ {% for j in range(in_axis + 1, source.rank) %}
25
+ {% set dj = ((source.inputShape[j] / 4) | int) if j == source.rank - 1 else source.inputShape[j] %}
26
+ {% set in_stride.value = in_stride.value * dj %}
27
+ {% endfor %}
28
+ let coord{{ out_axis }} = rem / {{ out_stride.value }}u;
29
+ rem = rem % {{ out_stride.value }}u;
30
+ offset = offset + coord{{ out_axis }} * {{ in_stride.value }}u;
31
+ {% endfor %}
32
+ return offset;
33
+ }
34
+
35
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
36
+ fn main(
37
+ @builtin(global_invocation_id) gid: vec3<u32>,
38
+ @builtin(num_workgroups) nwg: vec3<u32>
39
+ ) {
40
+ let stride = nwg.x * WG;
41
+ for (var i = gid.x; i < COUNT; i += stride) {
42
+ dst[i] = src[input_offset(i)];
43
+ }
44
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "ai.onnx",
3
+ "name": "Transpose",
4
+ "sinceVersion": 13,
5
+ "description": "Transposes the input tensor by permuting its axes according to the `perm` attribute. Axis `i` of the output corresponds to axis `perm[i]` of the input; if `perm` is omitted, the axes are reversed (`n-1, ..., 0`).",
6
+ "inputs": [{ "role": "data", "dtype": "T", "description": "The input tensor to transpose." }],
7
+ "outputs": [
8
+ {
9
+ "role": "transposed",
10
+ "dtype": "T",
11
+ "description": "The transposed output tensor with permuted axes.",
12
+ "rank": "ranks.data"
13
+ }
14
+ ],
15
+ "attributes": {},
16
+ "attributeDescriptions": {
17
+ "perm": "Optional permutation of the input axes. It must contain every axis from 0 through rank - 1 exactly once. When omitted, the axes are reversed."
18
+ },
19
+ "typeConstraints": { "T": ["float32", "float16", "int32", "int16", "uint32", "uint8", "int8", "bool"] },
20
+ "args": {
21
+ "x": { "kind": "tensor", "semantic": "data", "role": "input" },
22
+ "y": { "kind": "tensor", "semantic": "transposed", "role": "output" }
23
+ },
24
+ "tunables": { "WORKGROUP_SIZE": 256, "INNER_VEC4_ITEMS_PER_THREAD": 4, "INNER_VEC4_MIN_THREADS": 65536 },
25
+ "tunableDescriptions": {
26
+ "INNER_VEC4_ITEMS_PER_THREAD": "Sets the target maximum number of vec4 items assigned to each invocation by the inner-axis vector route.",
27
+ "INNER_VEC4_MIN_THREADS": "Sets the minimum logical thread count retained when shrinking the inner-axis vector dispatch."
28
+ },
29
+ "derive": {
30
+ "dtypeSupported": "f16Ok(dtypes.T)",
31
+ "rank0IO": "ranks.data == 0 and ranks.transposed == 0",
32
+ "rank1IO": "ranks.data == 1 and ranks.transposed == 1",
33
+ "rank2IO": "ranks.data == 2 and ranks.transposed == 2",
34
+ "rank3IO": "ranks.data == 3 and ranks.transposed == 3",
35
+ "rank4IO": "ranks.data == 4 and ranks.transposed == 4",
36
+ "rank5IO": "ranks.data == 5 and ranks.transposed == 5",
37
+ "rank6IO": "ranks.data == 6 and ranks.transposed == 6",
38
+ "rank7IO": "ranks.data == 7 and ranks.transposed == 7",
39
+ "permPresent": "has(attrs, \"perm\")",
40
+ "permContract": "not permPresent or ((attrs.perm | length) == ranks.data and isUniqueIntList(attrs.perm, 0, ranks.data))",
41
+ "rank0Permutation": "rank0IO and permContract",
42
+ "rank1Permutation": "rank1IO and permContract and dim(shapes.transposed, 0) == dim(shapes.data, 0)",
43
+ "explicitRank2": "rank2IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1])",
44
+ "explicitRank3": "rank3IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2])",
45
+ "explicitRank4": "rank4IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2]) and dim(shapes.transposed, 3) == dim(shapes.data, attrs.perm[3])",
46
+ "explicitRank5": "rank5IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2]) and dim(shapes.transposed, 3) == dim(shapes.data, attrs.perm[3]) and dim(shapes.transposed, 4) == dim(shapes.data, attrs.perm[4])",
47
+ "explicitRank6": "rank6IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2]) and dim(shapes.transposed, 3) == dim(shapes.data, attrs.perm[3]) and dim(shapes.transposed, 4) == dim(shapes.data, attrs.perm[4]) and dim(shapes.transposed, 5) == dim(shapes.data, attrs.perm[5])",
48
+ "explicitRank7": "rank7IO and permContract and permPresent and dim(shapes.transposed, 0) == dim(shapes.data, attrs.perm[0]) and dim(shapes.transposed, 1) == dim(shapes.data, attrs.perm[1]) and dim(shapes.transposed, 2) == dim(shapes.data, attrs.perm[2]) and dim(shapes.transposed, 3) == dim(shapes.data, attrs.perm[3]) and dim(shapes.transposed, 4) == dim(shapes.data, attrs.perm[4]) and dim(shapes.transposed, 5) == dim(shapes.data, attrs.perm[5]) and dim(shapes.transposed, 6) == dim(shapes.data, attrs.perm[6])",
49
+ "defaultRank2": "rank2IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 1) and dim(shapes.transposed, 1) == dim(shapes.data, 0)",
50
+ "defaultRank3": "rank3IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 2) and dim(shapes.transposed, 1) == dim(shapes.data, 1) and dim(shapes.transposed, 2) == dim(shapes.data, 0)",
51
+ "defaultRank4": "rank4IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 3) and dim(shapes.transposed, 1) == dim(shapes.data, 2) and dim(shapes.transposed, 2) == dim(shapes.data, 1) and dim(shapes.transposed, 3) == dim(shapes.data, 0)",
52
+ "defaultRank5": "rank5IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 4) and dim(shapes.transposed, 1) == dim(shapes.data, 3) and dim(shapes.transposed, 2) == dim(shapes.data, 2) and dim(shapes.transposed, 3) == dim(shapes.data, 1) and dim(shapes.transposed, 4) == dim(shapes.data, 0)",
53
+ "defaultRank6": "rank6IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 5) and dim(shapes.transposed, 1) == dim(shapes.data, 4) and dim(shapes.transposed, 2) == dim(shapes.data, 3) and dim(shapes.transposed, 3) == dim(shapes.data, 2) and dim(shapes.transposed, 4) == dim(shapes.data, 1) and dim(shapes.transposed, 5) == dim(shapes.data, 0)",
54
+ "defaultRank7": "rank7IO and permContract and not permPresent and dim(shapes.transposed, 0) == dim(shapes.data, 6) and dim(shapes.transposed, 1) == dim(shapes.data, 5) and dim(shapes.transposed, 2) == dim(shapes.data, 4) and dim(shapes.transposed, 3) == dim(shapes.data, 3) and dim(shapes.transposed, 4) == dim(shapes.data, 2) and dim(shapes.transposed, 5) == dim(shapes.data, 1) and dim(shapes.transposed, 6) == dim(shapes.data, 0)",
55
+ "genericTransposeOk": "rank0Permutation or rank1Permutation or explicitRank2 or defaultRank2 or explicitRank3 or defaultRank3 or explicitRank4 or defaultRank4 or explicitRank5 or defaultRank5 or explicitRank6 or defaultRank6 or explicitRank7 or defaultRank7",
56
+ "defaultPerm": "pick([[rank0IO, []], [rank1IO, [0]], [rank2IO, [1, 0]], [rank3IO, [2, 1, 0]], [rank4IO, [3, 2, 1, 0]], [rank5IO, [4, 3, 2, 1, 0]], [rank6IO, [5, 4, 3, 2, 1, 0]], [rank7IO, [6, 5, 4, 3, 2, 1, 0]]], [])",
57
+ "genericPerm": "pick([[permPresent, attrs.perm]], defaultPerm)",
58
+ "innerVec4Rank3Ok": "explicitRank3 and attrs.perm[2] == 2 and dim(shapes.data, 2) % 4 == 0 and numel(shapes.transposed) > 0 and dtypeSupported",
59
+ "innerVec4Rank4Ok": "explicitRank4 and attrs.perm[3] == 3 and dim(shapes.data, 3) % 4 == 0 and numel(shapes.transposed) > 0 and dtypeSupported",
60
+ "innerVec4Ok": "innerVec4Rank3Ok or innerVec4Rank4Ok",
61
+ "innerVec4Perm": "attrs.perm",
62
+ "tiled2dShapeOk": "(defaultRank2 or (explicitRank2 and attrs.perm[0] == 1 and attrs.perm[1] == 0)) and numel(shapes.data) > 0 and ceil(dim(shapes.data, 0) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 1) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
63
+ "tiled2dOk": "tiled2dShapeOk and dim(shapes.data, 0) % 4 == 0 and dim(shapes.data, 1) % 4 == 0",
64
+ "tiled3dShapeOk": "explicitRank3 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 1 and numel(shapes.data) > 0 and dim(shapes.data, 0) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 1) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 2) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
65
+ "tiled3dOk": "tiled3dShapeOk and dim(shapes.data, 1) % 4 == 0 and dim(shapes.data, 2) % 4 == 0",
66
+ "tiled4dShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 3 and attrs.perm[3] == 2 and numel(shapes.data) > 0 and dim(shapes.data, 0) * dim(shapes.data, 1) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 2) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 3) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
67
+ "tiled4dOk": "tiled4dShapeOk and dim(shapes.data, 2) % 4 == 0 and dim(shapes.data, 3) % 4 == 0",
68
+ "tiled5dNcdhwToNdhwcShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 4 and attrs.perm[4] == 1 and numel(shapes.data) > 0 and dim(shapes.data, 0) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 1) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil((dim(shapes.data, 2) * dim(shapes.data, 3) * dim(shapes.data, 4)) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
69
+ "tiled5dNcdhwToNdhwcOk": "tiled5dNcdhwToNdhwcShapeOk and dim(shapes.data, 1) % 4 == 0 and (dim(shapes.data, 2) * dim(shapes.data, 3) * dim(shapes.data, 4)) % 4 == 0",
70
+ "tiled5dNdhwcToNcdhwShapeOk": "explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 4 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and attrs.perm[4] == 3 and numel(shapes.data) > 0 and dim(shapes.data, 0) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 1) * dim(shapes.data, 2) * dim(shapes.data, 3) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 4) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
71
+ "tiled5dNdhwcToNcdhwOk": "tiled5dNdhwcToNcdhwShapeOk and dim(shapes.data, 1) * dim(shapes.data, 2) * dim(shapes.data, 3) % 4 == 0 and dim(shapes.data, 4) % 4 == 0",
72
+ "memcpyPerm3": "explicitRank3 and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.data, attrs.perm[1]) == 1 or dim(shapes.data, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2])",
73
+ "memcpyPerm4": "explicitRank4 and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[3]) == 1 or attrs.perm[0] < attrs.perm[3]) and (dim(shapes.data, attrs.perm[1]) == 1 or dim(shapes.data, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2]) and (dim(shapes.data, attrs.perm[1]) == 1 or dim(shapes.data, attrs.perm[3]) == 1 or attrs.perm[1] < attrs.perm[3]) and (dim(shapes.data, attrs.perm[2]) == 1 or dim(shapes.data, attrs.perm[3]) == 1 or attrs.perm[2] < attrs.perm[3])",
74
+ "memcpyPerm5": "explicitRank5 and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[1]) == 1 or attrs.perm[0] < attrs.perm[1]) and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[2]) == 1 or attrs.perm[0] < attrs.perm[2]) and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[3]) == 1 or attrs.perm[0] < attrs.perm[3]) and (dim(shapes.data, attrs.perm[0]) == 1 or dim(shapes.data, attrs.perm[4]) == 1 or attrs.perm[0] < attrs.perm[4]) and (dim(shapes.data, attrs.perm[1]) == 1 or dim(shapes.data, attrs.perm[2]) == 1 or attrs.perm[1] < attrs.perm[2]) and (dim(shapes.data, attrs.perm[1]) == 1 or dim(shapes.data, attrs.perm[3]) == 1 or attrs.perm[1] < attrs.perm[3]) and (dim(shapes.data, attrs.perm[1]) == 1 or dim(shapes.data, attrs.perm[4]) == 1 or attrs.perm[1] < attrs.perm[4]) and (dim(shapes.data, attrs.perm[2]) == 1 or dim(shapes.data, attrs.perm[3]) == 1 or attrs.perm[2] < attrs.perm[3]) and (dim(shapes.data, attrs.perm[2]) == 1 or dim(shapes.data, attrs.perm[4]) == 1 or attrs.perm[2] < attrs.perm[4]) and (dim(shapes.data, attrs.perm[3]) == 1 or dim(shapes.data, attrs.perm[4]) == 1 or attrs.perm[3] < attrs.perm[4])",
75
+ "memcpyPermOk": "memcpyPerm3 or memcpyPerm4 or memcpyPerm5",
76
+ "identityPermutation": "rank1Permutation or (explicitRank2 and attrs.perm[0] == 0 and attrs.perm[1] == 1) or (explicitRank3 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 2) or (explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 2 and attrs.perm[3] == 3) or (explicitRank5 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 2 and attrs.perm[3] == 3 and attrs.perm[4] == 4) or (explicitRank6 and attrs.perm[0] == 0 and attrs.perm[1] == 1 and attrs.perm[2] == 2 and attrs.perm[3] == 3 and attrs.perm[4] == 4 and attrs.perm[5] == 5)",
77
+ "identityCopyOk": "numel(shapes.transposed) > 0 and numel(shapes.transposed) % 4 == 0 and (identityPermutation or memcpyPermOk) and dtypeSupported",
78
+ "tiled4dNchwToNhwcShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 2 and attrs.perm[2] == 3 and attrs.perm[3] == 1 and numel(shapes.data) > 0 and dim(shapes.data, 0) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 1) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil((dim(shapes.data, 2) * dim(shapes.data, 3)) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
79
+ "tiled4dNchwToNhwcOk": "tiled4dNchwToNhwcShapeOk and dim(shapes.data, 1) % 4 == 0 and (dim(shapes.data, 2) * dim(shapes.data, 3)) % 4 == 0",
80
+ "tiled4dNhwcToNchwShapeOk": "explicitRank4 and attrs.perm[0] == 0 and attrs.perm[1] == 3 and attrs.perm[2] == 1 and attrs.perm[3] == 2 and numel(shapes.data) > 0 and dim(shapes.data, 0) <= device.limits.maxComputeWorkgroupsPerDimension and ceil((dim(shapes.data, 1) * dim(shapes.data, 2)) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceil(dim(shapes.data, 3) / 32) <= device.limits.maxComputeWorkgroupsPerDimension and dtypeSupported",
81
+ "tiled4dNhwcToNchwOk": "tiled4dNhwcToNchwShapeOk and (dim(shapes.data, 1) * dim(shapes.data, 2)) % 4 == 0 and dim(shapes.data, 3) % 4 == 0",
82
+ "tiledTransposeOk": "tiled2dOk or tiled3dOk or tiled4dOk or tiled5dNcdhwToNdhwcOk or tiled5dNdhwcToNcdhwOk or tiled4dNchwToNhwcOk or tiled4dNhwcToNchwOk",
83
+ "tiledScalarTransposeOk": "tiled2dShapeOk or tiled3dShapeOk or tiled4dShapeOk or tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk",
84
+ "tiledRows": "dim(shapes.data, 0) if tiled2dShapeOk else (dim(shapes.data, 1) if tiled3dShapeOk else (dim(shapes.data, 2) if tiled4dShapeOk else (dim(shapes.data, 1) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.data, 1) * dim(shapes.data, 2) * dim(shapes.data, 3) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.data, 1) if tiled4dNchwToNhwcShapeOk else (dim(shapes.data, 1) * dim(shapes.data, 2) if tiled4dNhwcToNchwShapeOk else 1))))))",
85
+ "tiledCols": "dim(shapes.data, 1) if tiled2dShapeOk else (dim(shapes.data, 2) if tiled3dShapeOk else (dim(shapes.data, 3) if tiled4dShapeOk else (dim(shapes.data, 2) * dim(shapes.data, 3) * dim(shapes.data, 4) if tiled5dNcdhwToNdhwcShapeOk else (dim(shapes.data, 4) if tiled5dNdhwcToNcdhwShapeOk else (dim(shapes.data, 2) * dim(shapes.data, 3) if tiled4dNchwToNhwcShapeOk else (dim(shapes.data, 3) if tiled4dNhwcToNchwShapeOk else 1))))))",
86
+ "tiledBatch": "1 if tiled2dShapeOk else (dim(shapes.data, 0) if tiled3dShapeOk else (dim(shapes.data, 0) * dim(shapes.data, 1) if tiled4dShapeOk else (dim(shapes.data, 0) if tiled5dNcdhwToNdhwcShapeOk or tiled5dNdhwcToNcdhwShapeOk or tiled4dNchwToNhwcShapeOk or tiled4dNhwcToNchwShapeOk else 1)))"
87
+ },
88
+ "constants": { "usesF16": "dtypes.T == \"f16\"" },
89
+ "bindingSets": {
90
+ "vectorData": [
91
+ {
92
+ "name": "src",
93
+ "arg": "x",
94
+ "semantic": "data",
95
+ "buffer": { "type": "read-only-storage" },
96
+ "elementType": "$vectorScalar"
97
+ },
98
+ {
99
+ "name": "dst",
100
+ "arg": "y",
101
+ "semantic": "transposed",
102
+ "buffer": { "type": "storage" },
103
+ "elementType": "$vectorScalar"
104
+ }
105
+ ],
106
+ "scalarData": [
107
+ {
108
+ "name": "x",
109
+ "arg": "x",
110
+ "semantic": "data",
111
+ "buffer": { "type": "read-only-storage" },
112
+ "elementType": "$scalar"
113
+ },
114
+ { "name": "y", "arg": "y", "semantic": "transposed", "buffer": { "type": "storage" }, "elementType": "$scalar" },
115
+ {
116
+ "name": "params",
117
+ "semantic": "kernel.params",
118
+ "buffer": { "type": "uniform" },
119
+ "struct": {
120
+ "name": "Params",
121
+ "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.transposed)" }]
122
+ }
123
+ }
124
+ ],
125
+ "scalarDataNoParams": [
126
+ {
127
+ "name": "x",
128
+ "arg": "x",
129
+ "semantic": "data",
130
+ "buffer": { "type": "read-only-storage" },
131
+ "elementType": "$scalar"
132
+ },
133
+ { "name": "y", "arg": "y", "semantic": "transposed", "buffer": { "type": "storage" }, "elementType": "$scalar" }
134
+ ]
135
+ },
136
+ "variants": [
137
+ {
138
+ "id": "tiled_vec4",
139
+ "priority": 22,
140
+ "when": ["tiledTransposeOk"],
141
+ "constants": { "scalar": "dtypes.T", "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
142
+ "passes": [
143
+ {
144
+ "id": "main",
145
+ "name": "Transpose.tiled",
146
+ "source": {
147
+ "shader": "datamove-transpose-2d-tiled.wgsl.jinja",
148
+ "inputs": { "rows": "tiledRows", "cols": "tiledCols" }
149
+ },
150
+ "bindings": "vectorData",
151
+ "dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
152
+ }
153
+ ]
154
+ },
155
+ {
156
+ "id": "tiled_scalar",
157
+ "description": "Stages ragged two-dimensional transpose planes through a padded workgroup tile so reads and writes remain coalesced when vec4 alignment is unavailable.",
158
+ "priority": 19,
159
+ "when": ["tiledScalarTransposeOk", "dtypeSupported"],
160
+ "constants": { "scalar": "dtypes.T" },
161
+ "passes": [
162
+ {
163
+ "id": "main",
164
+ "name": "Transpose.tiledScalar",
165
+ "source": {
166
+ "shader": "datamove-transpose-2d-tiled-scalar.wgsl.jinja",
167
+ "inputs": { "rows": "tiledRows", "cols": "tiledCols" }
168
+ },
169
+ "bindings": "scalarDataNoParams",
170
+ "dispatch": { "x": "ceil(tiledCols / 32)", "y": "ceil(tiledRows / 32)", "z": "tiledBatch" }
171
+ }
172
+ ]
173
+ },
174
+ {
175
+ "id": "identity_vec4",
176
+ "priority": 21,
177
+ "when": ["identityCopyOk"],
178
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
179
+ "passes": [
180
+ {
181
+ "id": "main",
182
+ "name": "Transpose.identityVec4",
183
+ "source": { "shader": "datamove-flat-copy.wgsl.jinja", "inputs": { "count": "numel(shapes.transposed) / 4" } },
184
+ "bindings": "vectorData",
185
+ "dispatch": { "gridStride": "numel(shapes.transposed) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
186
+ }
187
+ ]
188
+ },
189
+ {
190
+ "id": "inner_vec4",
191
+ "description": "Vectorizes the contiguous innermost dimension for permutations that leave that axis in place. It assigns multiple vectors per invocation only when enough invocations remain to keep the dispatch populated.",
192
+ "priority": 20,
193
+ "when": ["innerVec4Ok"],
194
+ "constants": { "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"" },
195
+ "passes": [
196
+ {
197
+ "id": "main",
198
+ "name": "Transpose.innerVec4",
199
+ "source": {
200
+ "shader": "datamove-transpose-vec4.wgsl.jinja",
201
+ "inputs": {
202
+ "inputShape": "shapes.data",
203
+ "outputShape": "shapes.transposed",
204
+ "rank": "ranks.data",
205
+ "perm": "innerVec4Perm",
206
+ "count": "numel(shapes.transposed) / 4"
207
+ }
208
+ },
209
+ "bindings": "vectorData",
210
+ "dispatch": {
211
+ "gridStride": "max(min(numel(shapes.transposed) / 4, tunables.INNER_VEC4_MIN_THREADS), ceilDiv(numel(shapes.transposed) / 4, tunables.INNER_VEC4_ITEMS_PER_THREAD))",
212
+ "workgroupSize": "tunables.WORKGROUP_SIZE"
213
+ }
214
+ }
215
+ ]
216
+ },
217
+ {
218
+ "id": "generic",
219
+ "when": ["genericTransposeOk", "dtypeSupported"],
220
+ "constants": { "scalar": "dtypes.T" },
221
+ "passes": [
222
+ {
223
+ "id": "main",
224
+ "name": "Transpose",
225
+ "source": {
226
+ "shader": "transpose.wgsl.jinja",
227
+ "inputs": {
228
+ "inputShape": "shapes.data",
229
+ "outputShape": "shapes.transposed",
230
+ "rank": "ranks.data",
231
+ "perm": "genericPerm"
232
+ }
233
+ },
234
+ "bindings": "scalarData",
235
+ "dispatch": { "threads": "numel(shapes.transposed)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
236
+ }
237
+ ]
238
+ }
239
+ ]
240
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ai.onnx.Transpose",
3
+ "id": "_ai_onnx_transpose_webgpu_dff5d66",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "wT5OzWQiJ0OSKnc+JuSj0vOoFzY1Q/JzgJW1jpIbv5Q=",
11
+ "datamove-flat-copy.wgsl.jinja": "rEpeYMvIZQuQnB2f3lt51jrrklKICsqUTzCJPRTknNk=",
12
+ "datamove-transpose-2d-tiled-scalar.wgsl.jinja": "zy7rlN8Se+mn8RB9akbBTHq1RK8ot6NBboxG3vX1Xdg=",
13
+ "datamove-transpose-2d-tiled.wgsl.jinja": "AtmY7NlBNCV1unIf2epvpBO3hfnLBmgGKGB7WiWMCQk=",
14
+ "datamove-transpose-vec4.wgsl.jinja": "RgwjTbu7SHDVsUW7W2f5S81gv1UJBpT93udQX/MZdJ0=",
15
+ "manifest.json": "qACTV3+3jqKTescFk98we7Nm2pwajZoBwGAo1n3dL9M=",
16
+ "test.json": "acNlC/DcnpoXb3ni/Z4QMbpYD7C1YR16iHXX20mNp0Y=",
17
+ "transpose.wgsl.jinja": "BbRpj9r38KShHUF3RXf518/bYtZ8BHFbGDZLRbOuP9g="
18
+ }
19
+ },
20
+ "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
21
+ "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Transpose" }
22
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,1097 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "ai.onnx.Transpose",
3
+ "fixtureArrays": {
4
+ "onnx_backend_transpose_input_x": [0.54881352186203, 0.7151893377304077, 0.6027633547782898, 0.5448831915855408, 0.42365479469299316, 0.6458941102027893, 0.4375872015953064, 0.891772985458374, 0.9636627435684204, 0.3834415078163147, 0.7917250394821167, 0.5288949012756348, 0.5680445432662964, 0.9255966544151306, 0.07103605568408966, 0.08712930232286453, 0.020218396559357643, 0.832619845867157, 0.7781567573547363, 0.8700121641159058, 0.978618323802948, 0.7991585731506348, 0.4614793658256531, 0.7805292010307312],
5
+ "ort_transpose021_f32_input_x": [1, 2, 3, 4, 5, 6, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 1.3, 2.3, 3.3, 4.3, 5.3, 6.3],
6
+ "ort_nhwc_to_nchw_uint32_input_x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
7
+ },
8
+ "cases": [
9
+ {
10
+ "name": "f32_subnormal_rank2_preserve_data",
11
+ "provenance": {
12
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
13
+ "test": "TransposeOpTest.TwoDim",
14
+ "notes": "Transpose is data movement with reindexing; selected finite subnormal values must survive the permutation."
15
+ },
16
+ "attrs": { "perm": [1, 0] },
17
+ "inputs": {
18
+ "x": {
19
+ "dtype": "float32",
20
+ "shape": [2, 3],
21
+ "data": { "kind": "values", "values": [1e-40, -1e-40, 0.0, 1e-39, -1e-39, 2.0] }
22
+ }
23
+ },
24
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 2], "tolerance": 0 } }
25
+ },
26
+ {
27
+ "name": "rank1_default_perm",
28
+ "inputs": {
29
+ "x": {
30
+ "dtype": "float32",
31
+ "shape": [7],
32
+ "data": { "kind": "values", "values": [0.0, 1.0, -2.0, 3.5, 4.0, -5.0, 6.0] }
33
+ }
34
+ },
35
+ "outputs": { "y": { "dtype": "float32", "shape": [7], "tolerance": 0.000001 } }
36
+ },
37
+ {
38
+ "name": "rank1_explicit_identity_perm",
39
+ "attrs": { "perm": [0] },
40
+ "inputs": {
41
+ "x": {
42
+ "dtype": "float32",
43
+ "shape": [7],
44
+ "data": { "kind": "values", "values": [0.0, 1.0, -2.0, 3.5, 4.0, -5.0, 6.0] }
45
+ }
46
+ },
47
+ "outputs": { "y": { "dtype": "float32", "shape": [7], "tolerance": 0.000001 } }
48
+ },
49
+ {
50
+ "name": "matrix_2d",
51
+ "attrs": { "perm": [1, 0] },
52
+ "inputs": {
53
+ "x": {
54
+ "dtype": "float32",
55
+ "shape": [3, 4],
56
+ "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.07 }
57
+ }
58
+ },
59
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3], "tolerance": 0.000001 } }
60
+ },
61
+ {
62
+ "name": "matrix_2d_default_perm",
63
+ "inputs": {
64
+ "x": {
65
+ "dtype": "float32",
66
+ "shape": [2, 3],
67
+ "data": { "kind": "values", "values": [0.0, 1.0, 2.0, 10.0, 11.0, 12.0] }
68
+ }
69
+ },
70
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 2], "tolerance": 0.000001 } }
71
+ },
72
+ {
73
+ "name": "rank3_0_2_1",
74
+ "attrs": { "perm": [0, 2, 1] },
75
+ "inputs": {
76
+ "x": {
77
+ "dtype": "float32",
78
+ "shape": [2, 3, 4],
79
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.23 }
80
+ }
81
+ },
82
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3], "tolerance": 0.000001 } }
83
+ },
84
+ {
85
+ "name": "rank3_default_perm",
86
+ "inputs": {
87
+ "x": {
88
+ "dtype": "float32",
89
+ "shape": [2, 3, 4],
90
+ "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.27 }
91
+ }
92
+ },
93
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3, 2], "tolerance": 0.000001 } }
94
+ },
95
+ {
96
+ "name": "rank4_attention_layout",
97
+ "attrs": { "perm": [0, 2, 1, 3] },
98
+ "inputs": {
99
+ "x": {
100
+ "dtype": "float32",
101
+ "shape": [2, 3, 4, 5],
102
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.19 }
103
+ }
104
+ },
105
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3, 5], "tolerance": 0.000001 } }
106
+ },
107
+ {
108
+ "name": "rank4_default_perm",
109
+ "inputs": {
110
+ "x": {
111
+ "dtype": "float32",
112
+ "shape": [2, 3, 4, 5],
113
+ "data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.21 }
114
+ }
115
+ },
116
+ "outputs": { "y": { "dtype": "float32", "shape": [5, 4, 3, 2], "tolerance": 0.000001 } }
117
+ },
118
+ {
119
+ "name": "rank5_default_perm",
120
+ "inputs": {
121
+ "x": {
122
+ "dtype": "float32",
123
+ "shape": [1, 2, 1, 3, 2],
124
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.17 }
125
+ }
126
+ },
127
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 1, 2, 1], "tolerance": 0.000001 } }
128
+ },
129
+ {
130
+ "name": "rank6_default_perm",
131
+ "inputs": {
132
+ "x": {
133
+ "dtype": "float32",
134
+ "shape": [1, 2, 1, 3, 1, 2],
135
+ "data": {
136
+ "kind": "values",
137
+ "values": [0.0, 1.0, 10.0, 11.0, 20.0, 21.0, 100.0, 101.0, 110.0, 111.0, 120.0, 121.0]
138
+ }
139
+ }
140
+ },
141
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 1, 3, 1, 2, 1], "tolerance": 0.000001 } }
142
+ },
143
+ {
144
+ "name": "rank6_explicit_interleaved_perm",
145
+ "attrs": { "perm": [0, 2, 4, 1, 3, 5] },
146
+ "inputs": {
147
+ "x": {
148
+ "dtype": "float32",
149
+ "shape": [2, 3, 1, 4, 2, 1],
150
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29 }
151
+ }
152
+ },
153
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 1, 2, 3, 4, 1], "tolerance": 0.000001 } }
154
+ },
155
+ {
156
+ "name": "rank0_scalar_default_perm",
157
+ "inputs": { "x": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [12.25] } } },
158
+ "outputs": { "y": { "dtype": "float32", "shape": [], "tolerance": 0.000001 } }
159
+ },
160
+ {
161
+ "name": "rank0_scalar_explicit_empty_perm",
162
+ "attrs": { "perm": [] },
163
+ "inputs": { "x": { "dtype": "float32", "shape": [], "data": { "kind": "values", "values": [-7.5] } } },
164
+ "outputs": { "y": { "dtype": "float32", "shape": [], "tolerance": 0.000001 } }
165
+ },
166
+ {
167
+ "name": "zero_dim_middle_to_last_axis_empty_output",
168
+ "attrs": { "perm": [0, 2, 1] },
169
+ "inputs": { "x": { "dtype": "float32", "shape": [2, 0, 3], "data": { "kind": "values", "values": [] } } },
170
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 0], "tolerance": 0 } }
171
+ },
172
+ {
173
+ "name": "ort_two_dim_default_f32",
174
+ "provenance": {
175
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
176
+ "test": "TransposeOpTest.TwoDimNoAttr"
177
+ },
178
+ "inputs": {
179
+ "x": {
180
+ "dtype": "float32",
181
+ "shape": [2, 3],
182
+ "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
183
+ }
184
+ },
185
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 2], "tolerance": 0.000001 } }
186
+ },
187
+ {
188
+ "name": "ort_two_dim_default_bool",
189
+ "provenance": {
190
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
191
+ "test": "TransposeOpTest.TwoDimNoAttr",
192
+ "notes": "Same two-dimensional data-movement fixture with an ONNX-valid bool payload."
193
+ },
194
+ "inputs": {
195
+ "x": { "dtype": "bool", "shape": [2, 3], "data": { "kind": "values", "values": [1, 0, 1, 0, 1, 0] } }
196
+ },
197
+ "outputs": { "y": { "dtype": "bool", "shape": [3, 2], "tolerance": 0 } }
198
+ },
199
+ {
200
+ "name": "ort_two_dim_default_f16",
201
+ "provenance": {
202
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
203
+ "test": "TransposeOpTest.TwoDimNoAttr"
204
+ },
205
+ "inputs": {
206
+ "x": {
207
+ "dtype": "float16",
208
+ "shape": [2, 3],
209
+ "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
210
+ }
211
+ },
212
+ "outputs": { "y": { "dtype": "float16", "shape": [3, 2], "tolerance": 0.001 } }
213
+ },
214
+ {
215
+ "name": "ort_two_dim_explicit_f32",
216
+ "provenance": {
217
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
218
+ "test": "TransposeOpTest.TwoDim"
219
+ },
220
+ "attrs": { "perm": [1, 0] },
221
+ "inputs": {
222
+ "x": {
223
+ "dtype": "float32",
224
+ "shape": [2, 3],
225
+ "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
226
+ }
227
+ },
228
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 2], "tolerance": 0.000001 } }
229
+ },
230
+ {
231
+ "name": "ort_two_dim_explicit_f16",
232
+ "provenance": {
233
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
234
+ "test": "TransposeOpTest.TwoDim_mlfloat16",
235
+ "notes": "Uses normal f16 numeric values instead of ORT's raw MLFloat16 bit-pattern payloads."
236
+ },
237
+ "attrs": { "perm": [1, 0] },
238
+ "inputs": {
239
+ "x": {
240
+ "dtype": "float16",
241
+ "shape": [2, 3],
242
+ "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
243
+ }
244
+ },
245
+ "outputs": { "y": { "dtype": "float16", "shape": [3, 2], "tolerance": 0.001 } }
246
+ },
247
+ {
248
+ "name": "ort_two_dim_uint32_adapted",
249
+ "provenance": {
250
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
251
+ "test": "TransposeOpTest.TwoDim_int32",
252
+ "notes": "ORT covers int32; this framework covers the same 2D explicit transpose with supported uint32 data."
253
+ },
254
+ "attrs": { "perm": [1, 0] },
255
+ "inputs": {
256
+ "x": { "dtype": "uint32", "shape": [2, 3], "data": { "kind": "values", "values": [1, 2, 3, 4, 5, 6] } }
257
+ },
258
+ "outputs": { "y": { "dtype": "uint32", "shape": [3, 2], "tolerance": 0 } }
259
+ },
260
+ {
261
+ "name": "ort_transpose021_f32",
262
+ "provenance": {
263
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
264
+ "test": "TransposeOpTest.Transpose021"
265
+ },
266
+ "attrs": { "perm": [0, 2, 1] },
267
+ "inputs": {
268
+ "x": {
269
+ "dtype": "float32",
270
+ "shape": [4, 2, 3],
271
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_transpose021_f32_input_x" } }
272
+ }
273
+ },
274
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3, 2], "tolerance": 0.000001 } }
275
+ },
276
+ {
277
+ "name": "ort_transpose120_f32",
278
+ "provenance": {
279
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
280
+ "test": "TransposeOpTest.Transpose120"
281
+ },
282
+ "attrs": { "perm": [1, 2, 0] },
283
+ "inputs": {
284
+ "x": {
285
+ "dtype": "float32",
286
+ "shape": [4, 2, 3],
287
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_transpose021_f32_input_x" } }
288
+ }
289
+ },
290
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 4], "tolerance": 0.000001 } }
291
+ },
292
+ {
293
+ "name": "ort_transpose102_suffix_preserved_f32",
294
+ "provenance": {
295
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
296
+ "test": "TransposeOpTest.Transpose102"
297
+ },
298
+ "attrs": { "perm": [1, 0, 2] },
299
+ "inputs": {
300
+ "x": {
301
+ "dtype": "float32",
302
+ "shape": [4, 2, 3],
303
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_transpose021_f32_input_x" } }
304
+ }
305
+ },
306
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3], "tolerance": 0.000001 } }
307
+ },
308
+ {
309
+ "name": "ort_transpose_reshape_rank5_f32",
310
+ "provenance": {
311
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
312
+ "test": "TransposeOpTest.TransposeReshape"
313
+ },
314
+ "attrs": { "perm": [1, 3, 2, 4, 0] },
315
+ "inputs": {
316
+ "x": {
317
+ "dtype": "float32",
318
+ "shape": [1, 4, 2, 1, 3],
319
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_transpose021_f32_input_x" } }
320
+ }
321
+ },
322
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 1, 2, 3, 1], "tolerance": 0.000001 } }
323
+ },
324
+ {
325
+ "name": "ort_six_dim_swap_outer_axes_f32",
326
+ "provenance": {
327
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
328
+ "test": "TransposeOpTest.SixDim"
329
+ },
330
+ "attrs": { "perm": [1, 0, 2, 3, 4, 5] },
331
+ "inputs": {
332
+ "x": {
333
+ "dtype": "float32",
334
+ "shape": [2, 2, 2, 2, 2, 2],
335
+ "data": {
336
+ "kind": "values",
337
+ "values": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0]
338
+ }
339
+ }
340
+ },
341
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2, 2, 2, 2], "tolerance": 0.000001 } }
342
+ },
343
+ {
344
+ "name": "ort_nchw_to_nhwc_uint32",
345
+ "provenance": {
346
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
347
+ "test": "TransposeOpTest.NCHW2NHWC"
348
+ },
349
+ "attrs": { "perm": [0, 2, 3, 1] },
350
+ "inputs": {
351
+ "x": {
352
+ "dtype": "uint32",
353
+ "shape": [1, 3, 2, 2],
354
+ "data": { "kind": "values", "values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] }
355
+ }
356
+ },
357
+ "outputs": { "y": { "dtype": "uint32", "shape": [1, 2, 2, 3], "tolerance": 0 } }
358
+ },
359
+ {
360
+ "name": "ort_nhwc_to_nchw_uint32",
361
+ "provenance": {
362
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
363
+ "test": "TransposeOpTest.NHWC2NCHW"
364
+ },
365
+ "attrs": { "perm": [0, 3, 1, 2] },
366
+ "inputs": {
367
+ "x": {
368
+ "dtype": "uint32",
369
+ "shape": [2, 2, 2, 2],
370
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_nhwc_to_nchw_uint32_input_x" } }
371
+ }
372
+ },
373
+ "outputs": { "y": { "dtype": "uint32", "shape": [2, 2, 2, 2], "tolerance": 0 } }
374
+ },
375
+ {
376
+ "name": "ort_two_dim_int32",
377
+ "provenance": {
378
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
379
+ "test": "TransposeOpTest.TwoDim_int32"
380
+ },
381
+ "attrs": { "perm": [1, 0] },
382
+ "inputs": {
383
+ "x": { "dtype": "int32", "shape": [2, 3], "data": { "kind": "values", "values": [1, 2, 3, 4, 5, 6] } }
384
+ },
385
+ "outputs": { "y": { "dtype": "int32", "shape": [3, 2], "tolerance": 0 } }
386
+ },
387
+ {
388
+ "name": "ort_two_dim_int16",
389
+ "provenance": {
390
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
391
+ "test": "TransposeOpTest.TwoDim_int16"
392
+ },
393
+ "attrs": { "perm": [1, 0] },
394
+ "inputs": {
395
+ "x": { "dtype": "int16", "shape": [2, 3], "data": { "kind": "values", "values": [1, 2, 3, 4, 5, 6] } }
396
+ },
397
+ "outputs": { "y": { "dtype": "int16", "shape": [3, 2], "tolerance": 0 } }
398
+ },
399
+ {
400
+ "name": "ort_single_axis_moving_inwards_block_copy_uint32",
401
+ "provenance": {
402
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
403
+ "test": "TransposeOpTest.SingleAxisMovingInwardsBlockCopy",
404
+ "notes": "ORT uses uint64 payload values; this framework stores the representable test values as uint32."
405
+ },
406
+ "attrs": { "perm": [1, 2, 0, 3] },
407
+ "inputs": {
408
+ "x": {
409
+ "dtype": "uint32",
410
+ "shape": [2, 2, 2, 2],
411
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_nhwc_to_nchw_uint32_input_x" } }
412
+ }
413
+ },
414
+ "outputs": { "y": { "dtype": "uint32", "shape": [2, 2, 2, 2], "tolerance": 0 } }
415
+ },
416
+ {
417
+ "name": "ort_do_transpose_impl_rank4_f32",
418
+ "provenance": {
419
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
420
+ "test": "TransposeOpTest.DoTransposeImpl"
421
+ },
422
+ "attrs": { "perm": [2, 1, 0, 3] },
423
+ "inputs": {
424
+ "x": {
425
+ "dtype": "float32",
426
+ "shape": [5, 2, 1, 3],
427
+ "data": {
428
+ "kind": "values",
429
+ "values": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0]
430
+ }
431
+ }
432
+ },
433
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 5, 3], "tolerance": 0 } }
434
+ },
435
+ {
436
+ "name": "ort_transpose3dimpl_flatten_dims23_f32",
437
+ "provenance": {
438
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
439
+ "test": "TransposeOpTest.Transpose3DImpl",
440
+ "notes": "Compact projection of ORT's rank-4 [0,2,3,1] case that flattens source dims 2 and 3 into one moved block."
441
+ },
442
+ "attrs": { "perm": [0, 2, 3, 1] },
443
+ "inputs": { "x": { "dtype": "float32", "shape": [2, 3, 4, 5], "data": { "kind": "fillFloat32" } } },
444
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 5, 3], "tolerance": 0 } }
445
+ },
446
+ {
447
+ "name": "ort_transpose3dimpl_flatten_dims12_f32",
448
+ "provenance": {
449
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
450
+ "test": "TransposeOpTest.Transpose3DImpl",
451
+ "notes": "Compact projection of ORT's rank-4 [0,3,1,2] case that flattens source dims 1 and 2 into one moved block."
452
+ },
453
+ "attrs": { "perm": [0, 3, 1, 2] },
454
+ "inputs": { "x": { "dtype": "float32", "shape": [2, 3, 4, 5], "data": { "kind": "fillFloat32" } } },
455
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 5, 3, 4], "tolerance": 0 } }
456
+ },
457
+ {
458
+ "name": "ort_ndim_rank4_swap_outer_axes_f32",
459
+ "provenance": {
460
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
461
+ "test": "TransposeOpTest.NDim"
462
+ },
463
+ "attrs": { "perm": [1, 0, 2, 3] },
464
+ "inputs": {
465
+ "x": {
466
+ "dtype": "float32",
467
+ "shape": [2, 2, 2, 2],
468
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_nhwc_to_nchw_uint32_input_x" } }
469
+ }
470
+ },
471
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2, 2], "tolerance": 0 } }
472
+ },
473
+ {
474
+ "name": "ort_ndim_rank4_swap_outer_and_inner_axes_f32",
475
+ "provenance": {
476
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
477
+ "test": "TransposeOpTest.NDim"
478
+ },
479
+ "attrs": { "perm": [1, 0, 3, 2] },
480
+ "inputs": {
481
+ "x": {
482
+ "dtype": "float32",
483
+ "shape": [2, 2, 2, 2],
484
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_nhwc_to_nchw_uint32_input_x" } }
485
+ }
486
+ },
487
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2, 2], "tolerance": 0 } }
488
+ },
489
+ {
490
+ "name": "onnx_backend_transpose_all_permutations_0",
491
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_all_permutations_0" },
492
+ "attrs": { "perm": [0, 1, 2] },
493
+ "inputs": {
494
+ "x": {
495
+ "dtype": "float32",
496
+ "shape": [2, 3, 4],
497
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
498
+ }
499
+ },
500
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 4], "tolerance": 0 } }
501
+ },
502
+ {
503
+ "name": "onnx_backend_transpose_all_permutations_1",
504
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_all_permutations_1" },
505
+ "attrs": { "perm": [0, 2, 1] },
506
+ "inputs": {
507
+ "x": {
508
+ "dtype": "float32",
509
+ "shape": [2, 3, 4],
510
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
511
+ }
512
+ },
513
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3], "tolerance": 0 } }
514
+ },
515
+ {
516
+ "name": "onnx_backend_transpose_all_permutations_2",
517
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_all_permutations_2" },
518
+ "attrs": { "perm": [1, 0, 2] },
519
+ "inputs": {
520
+ "x": {
521
+ "dtype": "float32",
522
+ "shape": [2, 3, 4],
523
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
524
+ }
525
+ },
526
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 2, 4], "tolerance": 0 } }
527
+ },
528
+ {
529
+ "name": "onnx_backend_transpose_all_permutations_3",
530
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_all_permutations_3" },
531
+ "attrs": { "perm": [1, 2, 0] },
532
+ "inputs": {
533
+ "x": {
534
+ "dtype": "float32",
535
+ "shape": [2, 3, 4],
536
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
537
+ }
538
+ },
539
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 4, 2], "tolerance": 0 } }
540
+ },
541
+ {
542
+ "name": "onnx_backend_transpose_all_permutations_4",
543
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_all_permutations_4" },
544
+ "attrs": { "perm": [2, 0, 1] },
545
+ "inputs": {
546
+ "x": {
547
+ "dtype": "float32",
548
+ "shape": [2, 3, 4],
549
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
550
+ }
551
+ },
552
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 2, 3], "tolerance": 0 } }
553
+ },
554
+ {
555
+ "name": "onnx_backend_transpose_all_permutations_5",
556
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_all_permutations_5" },
557
+ "attrs": { "perm": [2, 1, 0] },
558
+ "inputs": {
559
+ "x": {
560
+ "dtype": "float32",
561
+ "shape": [2, 3, 4],
562
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
563
+ }
564
+ },
565
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3, 2], "tolerance": 0 } }
566
+ },
567
+ {
568
+ "name": "onnx_backend_transpose_default",
569
+ "provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_transpose_default" },
570
+ "inputs": {
571
+ "x": {
572
+ "dtype": "float32",
573
+ "shape": [2, 3, 4],
574
+ "data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_transpose_input_x" } }
575
+ }
576
+ },
577
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3, 2], "tolerance": 0 } }
578
+ },
579
+ {
580
+ "name": "ort_int8_two_dim_edge_values",
581
+ "provenance": {
582
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
583
+ "test": "TransposeOpTest.TwoDim_int8",
584
+ "notes": "Two-dimensional transpose with logical int8 edge values."
585
+ },
586
+ "attrs": { "perm": [1, 0] },
587
+ "inputs": {
588
+ "x": { "dtype": "int8", "shape": [2, 3], "data": { "kind": "values", "values": [-128, -1, 0, 1, 2, 127] } }
589
+ },
590
+ "outputs": { "y": { "dtype": "int8", "shape": [3, 2], "tolerance": 0 } }
591
+ },
592
+ {
593
+ "name": "uint8_transpose021_edge_values",
594
+ "provenance": {
595
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
596
+ "test": "TransposeOpTest.Transpose021",
597
+ "notes": "Rank-3 non-default permutation with logical uint8 edge values."
598
+ },
599
+ "attrs": { "perm": [0, 2, 1] },
600
+ "inputs": {
601
+ "x": {
602
+ "dtype": "uint8",
603
+ "shape": [2, 3, 2],
604
+ "data": { "kind": "values", "values": [0, 1, 2, 3, 4, 5, 250, 251, 252, 253, 254, 255] }
605
+ }
606
+ },
607
+ "outputs": { "y": { "dtype": "uint8", "shape": [2, 2, 3], "tolerance": 0 } }
608
+ },
609
+ {
610
+ "name": "tiled_2d_f32_64x32",
611
+ "attrs": { "perm": [1, 0] },
612
+ "inputs": {
613
+ "x": {
614
+ "dtype": "float32",
615
+ "shape": [64, 32],
616
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
617
+ }
618
+ },
619
+ "outputs": { "y": { "dtype": "float32", "shape": [32, 64], "tolerance": 0.000001 } }
620
+ },
621
+ {
622
+ "name": "tiled_2d_f32_36x20_partial_tile",
623
+ "attrs": { "perm": [1, 0] },
624
+ "inputs": {
625
+ "x": {
626
+ "dtype": "float32",
627
+ "shape": [36, 20],
628
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
629
+ }
630
+ },
631
+ "outputs": { "y": { "dtype": "float32", "shape": [20, 36], "tolerance": 0.000001 } }
632
+ },
633
+ {
634
+ "name": "tiled_2d_f32_256x256",
635
+ "attrs": { "perm": [1, 0] },
636
+ "inputs": {
637
+ "x": {
638
+ "dtype": "float32",
639
+ "shape": [256, 256],
640
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
641
+ }
642
+ },
643
+ "outputs": { "y": { "dtype": "float32", "shape": [256, 256], "tolerance": 0.000001 } }
644
+ },
645
+ {
646
+ "name": "tiled_2d_f16_32x64",
647
+ "attrs": { "perm": [1, 0] },
648
+ "inputs": {
649
+ "x": {
650
+ "dtype": "float16",
651
+ "shape": [32, 64],
652
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
653
+ }
654
+ },
655
+ "outputs": { "y": { "dtype": "float16", "shape": [64, 32], "tolerance": 0.001 } }
656
+ },
657
+ {
658
+ "name": "tiled_2d_u32_8x12",
659
+ "attrs": { "perm": [1, 0] },
660
+ "inputs": {
661
+ "x": {
662
+ "dtype": "uint32",
663
+ "shape": [8, 12],
664
+ "data": { "kind": "cycle", "values": [7, 1, 9, 4, 3, 12, 5, 0, 2, 8, 6, 11, 10] }
665
+ }
666
+ },
667
+ "outputs": { "y": { "dtype": "uint32", "shape": [12, 8], "tolerance": 0 } }
668
+ },
669
+ {
670
+ "name": "tiled_2d_default_perm_f32_16x44",
671
+ "inputs": {
672
+ "x": {
673
+ "dtype": "float32",
674
+ "shape": [16, 44],
675
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
676
+ }
677
+ },
678
+ "outputs": { "y": { "dtype": "float32", "shape": [44, 16], "tolerance": 0.000001 } }
679
+ },
680
+ {
681
+ "name": "tiled_3d_batched_f32_3x36x12",
682
+ "attrs": { "perm": [0, 2, 1] },
683
+ "inputs": {
684
+ "x": {
685
+ "dtype": "float32",
686
+ "shape": [3, 36, 12],
687
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
688
+ }
689
+ },
690
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 12, 36], "tolerance": 0.000001 } }
691
+ },
692
+ {
693
+ "name": "tiled_3d_batched_f16_2x32x16",
694
+ "attrs": { "perm": [0, 2, 1] },
695
+ "inputs": {
696
+ "x": {
697
+ "dtype": "float16",
698
+ "shape": [2, 32, 16],
699
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
700
+ }
701
+ },
702
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 16, 32], "tolerance": 0.001 } }
703
+ },
704
+ {
705
+ "name": "tiled_4d_batched_f32_2x3x16x8",
706
+ "attrs": { "perm": [0, 1, 3, 2] },
707
+ "inputs": {
708
+ "x": {
709
+ "dtype": "float32",
710
+ "shape": [2, 3, 16, 8],
711
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
712
+ }
713
+ },
714
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 8, 16], "tolerance": 0.000001 } }
715
+ },
716
+ {
717
+ "name": "identity_vec4_rank1_f32_12",
718
+ "inputs": {
719
+ "x": { "dtype": "float32", "shape": [12], "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 } }
720
+ },
721
+ "outputs": { "y": { "dtype": "float32", "shape": [12], "tolerance": 0.000001 } }
722
+ },
723
+ {
724
+ "name": "identity_vec4_rank2_f32_6x10",
725
+ "attrs": { "perm": [0, 1] },
726
+ "inputs": {
727
+ "x": {
728
+ "dtype": "float32",
729
+ "shape": [6, 10],
730
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
731
+ }
732
+ },
733
+ "outputs": { "y": { "dtype": "float32", "shape": [6, 10], "tolerance": 0.000001 } }
734
+ },
735
+ {
736
+ "name": "identity_vec4_rank3_f16_2x3x4",
737
+ "attrs": { "perm": [0, 1, 2] },
738
+ "inputs": {
739
+ "x": {
740
+ "dtype": "float16",
741
+ "shape": [2, 3, 4],
742
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
743
+ }
744
+ },
745
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 4], "tolerance": 0.001 } }
746
+ },
747
+ {
748
+ "name": "identity_vec4_rank4_f32_1x2x2x4",
749
+ "attrs": { "perm": [0, 1, 2, 3] },
750
+ "inputs": {
751
+ "x": {
752
+ "dtype": "float32",
753
+ "shape": [1, 2, 2, 4],
754
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
755
+ }
756
+ },
757
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 2, 4], "tolerance": 0.000001 } }
758
+ },
759
+ {
760
+ "name": "inner_vec4_rank3_f32_5x3x8",
761
+ "attrs": { "perm": [1, 0, 2] },
762
+ "inputs": {
763
+ "x": {
764
+ "dtype": "float32",
765
+ "shape": [5, 3, 8],
766
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
767
+ }
768
+ },
769
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 5, 8], "tolerance": 0.000001 } }
770
+ },
771
+ {
772
+ "name": "inner_vec4_rank3_f16_4x6x12",
773
+ "attrs": { "perm": [1, 0, 2] },
774
+ "inputs": {
775
+ "x": {
776
+ "dtype": "float16",
777
+ "shape": [4, 6, 12],
778
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
779
+ }
780
+ },
781
+ "outputs": { "y": { "dtype": "float16", "shape": [6, 4, 12], "tolerance": 0.001 } }
782
+ },
783
+ {
784
+ "name": "inner_vec4_rank4_attention_f32_2x3x4x8",
785
+ "attrs": { "perm": [0, 2, 1, 3] },
786
+ "inputs": {
787
+ "x": {
788
+ "dtype": "float32",
789
+ "shape": [2, 3, 4, 8],
790
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
791
+ }
792
+ },
793
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3, 8], "tolerance": 0.000001 } }
794
+ },
795
+ {
796
+ "name": "inner_vec4_rank4_f32_2x3x4x8_perm2103",
797
+ "attrs": { "perm": [2, 1, 0, 3] },
798
+ "inputs": {
799
+ "x": {
800
+ "dtype": "float32",
801
+ "shape": [2, 3, 4, 8],
802
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 }
803
+ }
804
+ },
805
+ "outputs": { "y": { "dtype": "float32", "shape": [4, 3, 2, 8], "tolerance": 0.000001 } }
806
+ },
807
+ {
808
+ "name": "rank2_scalar_odd_width_5x7",
809
+ "provenance": {
810
+ "notes": "Compact correctness sibling for the odd-width rank2 scalar transpose benchmark; the dimensions avoid vec4/tiled alignment while preserving the same rank2 permutation."
811
+ },
812
+ "attrs": { "perm": [1, 0] },
813
+ "inputs": {
814
+ "x": {
815
+ "dtype": "float32",
816
+ "shape": [5, 7],
817
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.29 }
818
+ }
819
+ },
820
+ "outputs": { "y": { "dtype": "float32", "shape": [7, 5], "tolerance": 0.000001 } }
821
+ },
822
+ {
823
+ "name": "rank7_explicit_perm",
824
+ "provenance": {
825
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
826
+ "test": "TransposeOpTest (generic N-dim)",
827
+ "notes": "Rank-7 explicit-permutation coverage for the generic transpose path. The manifest supplies the rank and permutation as template inputs instead of maintaining a rank-specific variant."
828
+ },
829
+ "attrs": { "perm": [1, 0, 3, 2, 5, 4, 6] },
830
+ "inputs": {
831
+ "x": {
832
+ "dtype": "float32",
833
+ "shape": [2, 3, 2, 2, 2, 2, 2],
834
+ "data": {
835
+ "kind": "values",
836
+ "values": [-10.0, -9.5, -9.0, -8.5, -8.0, -7.5, -7.0, -6.5, -6.0, -5.5, -5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0, 16.5, 17.0, 17.5, 18.0, 18.5, 19.0, 19.5, 20.0, 20.5, 21.0, 21.5, 22.0, 22.5, 23.0, 23.5, 24.0, 24.5, 25.0, 25.5, 26.0, 26.5, 27.0, 27.5, 28.0, 28.5, 29.0, 29.5, 30.0, 30.5, 31.0, 31.5, 32.0, 32.5, 33.0, 33.5, 34.0, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0, 37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0, 41.5, 42.0, 42.5, 43.0, 43.5, 44.0, 44.5, 45.0, 45.5, 46.0, 46.5, 47.0, 47.5, 48.0, 48.5, 49.0, 49.5, 50.0, 50.5, 51.0, 51.5, 52.0, 52.5, 53.0, 53.5, 54.0, 54.5, 55.0, 55.5, 56.0, 56.5, 57.0, 57.5, 58.0, 58.5, 59.0, 59.5, 60.0, 60.5, 61.0, 61.5, 62.0, 62.5, 63.0, 63.5, 64.0, 64.5, 65.0, 65.5, 66.0, 66.5, 67.0, 67.5, 68.0, 68.5, 69.0, 69.5, 70.0, 70.5, 71.0, 71.5, 72.0, 72.5, 73.0, 73.5, 74.0, 74.5, 75.0, 75.5, 76.0, 76.5, 77.0, 77.5, 78.0, 78.5, 79.0, 79.5, 80.0, 80.5, 81.0, 81.5, 82.0, 82.5, 83.0, 83.5, 84.0, 84.5, 85.0, 85.5]
837
+ }
838
+ }
839
+ },
840
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 2, 2, 2, 2, 2, 2], "tolerance": 0 } }
841
+ },
842
+ {
843
+ "name": "rank2_bool_explicit_tiled",
844
+ "provenance": {
845
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
846
+ "test": "TransposeOpTest.TwoDim (bool payload)",
847
+ "notes": "bool is in typeConstraints T and scalarType maps it to u32, so an aligned [4,4] bool transpose selects tiled_vec4 (gate is only f16Ok, true for u32). The pre-existing ort_two_dim_default_bool_gpu_gap fixture is stale-named; this NON-gpu-gapped case gives bool its first real GPU validation on the vec4 path."
848
+ },
849
+ "attrs": { "perm": [1, 0] },
850
+ "inputs": {
851
+ "x": {
852
+ "dtype": "bool",
853
+ "shape": [4, 4],
854
+ "data": { "kind": "values", "values": [1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1] }
855
+ }
856
+ },
857
+ "outputs": { "y": { "dtype": "bool", "shape": [4, 4], "tolerance": 0 } }
858
+ },
859
+ {
860
+ "name": "rank5_scrambled_perm_f32",
861
+ "provenance": {
862
+ "source": "onnxruntime/test/providers/cpu/tensor/transpose_test.cc",
863
+ "test": "TransposeOpTest.TransposeReshape (scrambled rank5)",
864
+ "notes": "Genuinely scrambled rank-5 explicit perm [4,2,0,3,1] exercising the generic scalar-gather path with rank-specific template inputs; prior rank-5 coverage only exercised a default reversal and a near-reshape perm."
865
+ },
866
+ "attrs": { "perm": [4, 2, 0, 3, 1] },
867
+ "inputs": {
868
+ "x": {
869
+ "dtype": "float32",
870
+ "shape": [2, 3, 2, 3, 2],
871
+ "data": {
872
+ "kind": "values",
873
+ "values": [-5.0, -4.75, -4.5, -4.25, -4.0, -3.75, -3.5, -3.25, -3.0, -2.75, -2.5, -2.25, -2.0, -1.75, -1.5, -1.25, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.25, 4.5, 4.75, 5.0, 5.25, 5.5, 5.75, 6.0, 6.25, 6.5, 6.75, 7.0, 7.25, 7.5, 7.75, 8.0, 8.25, 8.5, 8.75, 9.0, 9.25, 9.5, 9.75, 10.0, 10.25, 10.5, 10.75, 11.0, 11.25, 11.5, 11.75, 12.0, 12.25, 12.5, 12.75]
874
+ }
875
+ }
876
+ },
877
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2, 3, 3], "tolerance": 0.000001 } }
878
+ },
879
+ {
880
+ "name": "rank7_default_full_reversal",
881
+ "provenance": {
882
+ "notes": "A rank-7 default transpose (no perm) reverses all seven dimensions through the generic transpose path."
883
+ },
884
+ "inputs": {
885
+ "x": {
886
+ "dtype": "float32",
887
+ "shape": [2, 1, 2, 1, 2, 1, 3],
888
+ "data": { "kind": "linspace", "start": -1.0, "end": 1.0 }
889
+ }
890
+ },
891
+ "outputs": { "y": { "dtype": "float32", "shape": [3, 1, 2, 1, 2, 1, 2], "tolerance": 0 } }
892
+ },
893
+ {
894
+ "name": "rank1_2d_fold_boundary_last_element_guard",
895
+ "provenance": {
896
+ "notes": "numel = 2^24+1 = 16777217 forces ceilDiv(numel,256)=65536 > maxComputeWorkgroupsPerDimension(65535), engaging the scalar path's y-axis 2D fold (i = gid.x + gid.y*nwg.x*WG). rank1 numel%4 != 0 bypasses identity_vec4 and lands on the generic scalar path, isolating the fold last-row guard. Referenceless: TS reference supplies truth so a corrupted/dropped tail element in the second y-row fails the compare."
897
+ },
898
+ "inputs": {
899
+ "x": {
900
+ "dtype": "float32",
901
+ "shape": [16777217],
902
+ "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.017 }
903
+ }
904
+ },
905
+ "outputs": { "y": { "dtype": "float32", "shape": [16777217], "tolerance": 0 } }
906
+ },
907
+ {
908
+ "name": "tiled_2d_vec4_naninf_signedzero_payload_preserved",
909
+ "provenance": {
910
+ "notes": "Aligned [8,8] perm[1,0] f32 selects tiled_vec4 (both dims %4==0, %32 partial). Verifies NaN / +-Inf / signed-zero survive the vec4->scalar->workgroup->vec4 staging bit-exact. Expected values are the [8,8] input transposed (column-major read)."
911
+ },
912
+ "attrs": { "perm": [1, 0] },
913
+ "inputs": {
914
+ "x": {
915
+ "dtype": "float32",
916
+ "shape": [8, 8],
917
+ "data": {
918
+ "kind": "values",
919
+ "values": ["NaN", "Infinity", "-Infinity", 0.0, 0.0, 1.0, -1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0, 57.0, 58.0]
920
+ }
921
+ }
922
+ },
923
+ "outputs": {
924
+ "y": {
925
+ "dtype": "float32",
926
+ "shape": [8, 8],
927
+ "allowNaN": true,
928
+ "tolerance": 0,
929
+ "data": {
930
+ "kind": "values",
931
+ "values": ["NaN", 3.0, 11.0, 19.0, 27.0, 35.0, 43.0, 51.0, "Infinity", 4.0, 12.0, 20.0, 28.0, 36.0, 44.0, 52.0, "-Infinity", 5.0, 13.0, 21.0, 29.0, 37.0, 45.0, 53.0, 0.0, 6.0, 14.0, 22.0, 30.0, 38.0, 46.0, 54.0, 0.0, 7.0, 15.0, 23.0, 31.0, 39.0, 47.0, 55.0, 1.0, 8.0, 16.0, 24.0, 32.0, 40.0, 48.0, 56.0, -1.0, 9.0, 17.0, 25.0, 33.0, 41.0, 49.0, 57.0, 2.0, 10.0, 18.0, 26.0, 34.0, 42.0, 50.0, 58.0]
932
+ }
933
+ }
934
+ }
935
+ },
936
+ {
937
+ "name": "scalar_rank4_default_reverse_naninf_preserved",
938
+ "provenance": {
939
+ "notes": "No perm => default full reversal [3,2,1,0] on the generic scalar path. Values placed so index [i,j,k,l] holds 8*i+4*j+2*k+l; output[l,k,j,i] = input[i,j,k,l]. NaN/Inf/-0 payloads at select positions must survive the scalar input_offset gather."
940
+ },
941
+ "inputs": {
942
+ "x": {
943
+ "dtype": "float32",
944
+ "shape": [2, 2, 2, 2],
945
+ "data": {
946
+ "kind": "values",
947
+ "values": ["NaN", 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, "Infinity", "-Infinity", 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 0.0]
948
+ }
949
+ }
950
+ },
951
+ "outputs": {
952
+ "y": {
953
+ "dtype": "float32",
954
+ "shape": [2, 2, 2, 2],
955
+ "allowNaN": true,
956
+ "tolerance": 0,
957
+ "data": {
958
+ "kind": "values",
959
+ "values": ["NaN", "-Infinity", 4.0, 12.0, 2.0, 10.0, 6.0, 14.0, 1.0, 9.0, 5.0, 13.0, 3.0, 11.0, "Infinity", 0.0]
960
+ }
961
+ }
962
+ }
963
+ },
964
+ {
965
+ "name": "rank5_ncdhw_to_ndhwc_tiled_vec4",
966
+ "provenance": {
967
+ "notes": "Compact route lock for the flattened batched-2D NCDHW-to-NDHWC tile. C=4 and D*H*W=32 exercise one complete spatial tile boundary while preserving exact copy semantics."
968
+ },
969
+ "attrs": { "perm": [0, 2, 3, 4, 1] },
970
+ "inputs": {
971
+ "x": {
972
+ "dtype": "float32",
973
+ "shape": [1, 4, 2, 4, 4],
974
+ "data": { "kind": "linspace", "start": -64.0, "end": 63.0 }
975
+ }
976
+ },
977
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 4, 4, 4], "tolerance": 0 } }
978
+ },
979
+ {
980
+ "name": "rank5_ndhwc_to_ncdhw_tiled_candidate",
981
+ "provenance": {
982
+ "notes": "Inverse rank-5 layout lock: NDHWC [N,D,H,W,C] becomes NCDHW via perm [0,4,1,2,3]. C=4 and D*H*W=32 match one flattened 2D transpose boundary with bit-exact copy semantics."
983
+ },
984
+ "attrs": { "perm": [0, 4, 1, 2, 3] },
985
+ "inputs": {
986
+ "x": {
987
+ "dtype": "float32",
988
+ "shape": [1, 2, 4, 4, 4],
989
+ "data": { "kind": "linspace", "start": -64.0, "end": 63.0 }
990
+ }
991
+ },
992
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 4, 2, 4, 4], "tolerance": 0 } }
993
+ },
994
+ {
995
+ "name": "rank4_nchw_to_nhwc_perm0231_tiled",
996
+ "provenance": {
997
+ "source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
998
+ "test": "channels-last conversions use the tiled shared-memory kernel",
999
+ "notes": "perm (0,2,3,1)/(0,3,1,2) are batched 2D transposes with merged row/col dims and must reach the tiled kernel, not the scalar generic path."
1000
+ },
1001
+ "attrs": { "perm": [0, 2, 3, 1] },
1002
+ "inputs": {
1003
+ "x": {
1004
+ "dtype": "float32",
1005
+ "shape": [2, 8, 4, 12],
1006
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.29 }
1007
+ }
1008
+ },
1009
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 12, 8], "tolerance": 0 } }
1010
+ },
1011
+ {
1012
+ "name": "rank4_nhwc_to_nchw_perm0312_tiled",
1013
+ "provenance": {
1014
+ "source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
1015
+ "test": "channels-last conversions use the tiled shared-memory kernel",
1016
+ "notes": "perm (0,2,3,1)/(0,3,1,2) are batched 2D transposes with merged row/col dims and must reach the tiled kernel, not the scalar generic path."
1017
+ },
1018
+ "attrs": { "perm": [0, 3, 1, 2] },
1019
+ "inputs": {
1020
+ "x": {
1021
+ "dtype": "float32",
1022
+ "shape": [2, 4, 12, 8],
1023
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
1024
+ }
1025
+ },
1026
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 8, 4, 12], "tolerance": 0 } }
1027
+ },
1028
+ {
1029
+ "name": "rank4_unitdim_memcpy_perm0213",
1030
+ "provenance": {
1031
+ "source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
1032
+ "test": "unit-dim permutations lower to a flat copy",
1033
+ "notes": "A permutation that only moves size-1 axes preserves the row-major layout, so the flat vec4 copy is exact."
1034
+ },
1035
+ "attrs": { "perm": [0, 2, 1, 3] },
1036
+ "inputs": {
1037
+ "x": {
1038
+ "dtype": "float32",
1039
+ "shape": [1, 6, 1, 8],
1040
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.29 }
1041
+ }
1042
+ },
1043
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 1, 6, 8], "tolerance": 0 } }
1044
+ },
1045
+ {
1046
+ "name": "rank3_unitdim_memcpy_perm201",
1047
+ "provenance": {
1048
+ "source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
1049
+ "test": "unit-dim permutations lower to a flat copy",
1050
+ "notes": "A permutation that only moves size-1 axes preserves the row-major layout, so the flat vec4 copy is exact."
1051
+ },
1052
+ "attrs": { "perm": [2, 0, 1] },
1053
+ "inputs": {
1054
+ "x": {
1055
+ "dtype": "float32",
1056
+ "shape": [6, 8, 1],
1057
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.31 }
1058
+ }
1059
+ },
1060
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 6, 8], "tolerance": 0 } }
1061
+ },
1062
+ {
1063
+ "name": "rank5_unitdim_memcpy_perm20134",
1064
+ "provenance": {
1065
+ "source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
1066
+ "test": "unit-dim permutations lower to a flat copy",
1067
+ "notes": "A permutation that only moves size-1 axes preserves the row-major layout, so the flat vec4 copy is exact."
1068
+ },
1069
+ "attrs": { "perm": [2, 0, 1, 3, 4] },
1070
+ "inputs": {
1071
+ "x": {
1072
+ "dtype": "float32",
1073
+ "shape": [1, 2, 1, 3, 4],
1074
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.07 }
1075
+ }
1076
+ },
1077
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 1, 2, 3, 4], "tolerance": 0 } }
1078
+ },
1079
+ {
1080
+ "name": "rank4_nonmemcpy_perm0213_canary",
1081
+ "provenance": {
1082
+ "source": "onnxruntime/core/providers/webgpu/tensor/transpose.cc",
1083
+ "test": "unit-dim permutations lower to a flat copy",
1084
+ "notes": "A permutation that only moves size-1 axes preserves the row-major layout, so the flat vec4 copy is exact."
1085
+ },
1086
+ "attrs": { "perm": [0, 2, 1, 3] },
1087
+ "inputs": {
1088
+ "x": {
1089
+ "dtype": "float32",
1090
+ "shape": [2, 3, 4, 4],
1091
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.11 }
1092
+ }
1093
+ },
1094
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 3, 4], "tolerance": 0 } }
1095
+ }
1096
+ ]
1097
+ }
build/webgpu/transpose.wgsl.jinja ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if usesF16 %}
2
+ enable f16;
3
+ {% endif %}
4
+ {{ env.wgsl.resourceDeclarations }}
5
+
6
+ fn input_offset({% if source.rank > 0 %}out_index: u32{% endif %}) -> u32 {
7
+ {% if source.rank > 0 %}
8
+ var rem = out_index;
9
+ {% endif %}
10
+ var offset = 0u;
11
+ {% for out_axis in range(source.rank) %}
12
+ {% set out_stride = namespace(value=1) %}
13
+ {% for j in range(out_axis + 1, source.rank) %}
14
+ {% set out_stride.value = out_stride.value * source.outputShape[j] %}
15
+ {% endfor %}
16
+ {% set out_stride_divisor = out_stride.value if out_stride.value != 0 else 1 %}
17
+ {% set in_axis = source.perm[out_axis] %}
18
+ {% set in_stride = namespace(value=1) %}
19
+ {% for j in range(in_axis + 1, source.rank) %}
20
+ {% set in_stride.value = in_stride.value * source.inputShape[j] %}
21
+ {% endfor %}
22
+ let coord{{ out_axis }} = rem / {{ out_stride_divisor }}u;
23
+ rem = rem % {{ out_stride_divisor }}u;
24
+ offset = offset + coord{{ out_axis }} * {{ in_stride.value }}u;
25
+ {% endfor %}
26
+ return offset;
27
+ }
28
+
29
+ @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
30
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
31
+ // 2D-folded flat index: gid.y carries the high bits past the
32
+ // maxComputeWorkgroupsPerDimension limit.
33
+ let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
34
+ if (i >= params.count) {
35
+ return;
36
+ }
37
+ y[i] = x[input_offset({% if source.rank > 0 %}i{% endif %})];
38
+ }