Skip to content

Commit cdbc302

Browse files
committed
[ExecuTorch][WebGPU] permute_copy op test suite (cases.py op-test framework)
Pull Request resolved: #20397 Registers `aten.permute_copy.default` in the `cases.py` op-test framework: a `_permute_suite` of 4 configs (3D rotation, 4D middle-dim transpose, 2D transpose, full 4D shuffle) that `generate_op_tests` exports via `VulkanPartitioner` and compares to a torch golden on Dawn. Also adds `test/ops/permute/test_permute.py` (`PermuteModule` + `CONFIGS` + `_op_delegated` smoke test) and the `aten.permute_copy.default` partitioner-allowlist entry in `tester.py`. ghstack-source-id: 397026550 @exported-using-ghexport Differential Revision: [D108793156](https://our.internmc.facebook.com/intern/diff/D108793156/)
1 parent 09eb42c commit cdbc302

3 files changed

Lines changed: 94 additions & 0 deletions

File tree

backends/webgpu/test/op_tests/cases.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
CONFIGS as _MUL_CONFIGS,
4141
MulModule,
4242
)
43+
from executorch.backends.webgpu.test.ops.test_permute import (
44+
CONFIGS as _PERMUTE_CONFIGS,
45+
PermuteModule,
46+
)
4347
from executorch.backends.webgpu.test.ops.test_select import (
4448
CONFIGS as _SELECT_CONFIGS,
4549
SelectModule,
@@ -230,3 +234,16 @@ def _unsqueeze_suite() -> WebGPUTestSuite:
230234
@register_op_test("slice")
231235
def _slice_suite() -> WebGPUTestSuite:
232236
return _fn_config_suite(SliceModule, _SLICE_CONFIGS)
237+
238+
239+
@register_op_test("permute")
240+
def _permute_suite() -> WebGPUTestSuite:
241+
# CONFIGS: name -> (shape, perm-tuple).
242+
return WebGPUTestSuite(
243+
module_factory=lambda perm: PermuteModule(perm),
244+
cases=[
245+
Case(name=n, construct={"perm": perm}, inputs=(shape,))
246+
for n, (shape, perm) in _PERMUTE_CONFIGS.items()
247+
],
248+
golden_dtype="float32", # permutation reorders values; fp64 bit-identical
249+
)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
"""`aten.permute_copy.default` module + configs for the WebGPU op-test framework.
8+
9+
`PermuteModule` + `CONFIGS` are imported by `cases.py` to drive the declarative
10+
op-test suite. `PermuteTest` is the export-delegation smoke
11+
test.
12+
"""
13+
14+
import unittest
15+
16+
import torch
17+
18+
from executorch.backends.vulkan.partitioner.vulkan_partitioner import VulkanPartitioner
19+
from executorch.exir import to_edge_transform_and_lower
20+
21+
# name -> (input_shape, perm)
22+
CONFIGS = {
23+
"rot3d": ((2, 3, 4), (2, 0, 1)),
24+
"mid4d": ((1, 8, 4, 16), (0, 2, 1, 3)),
25+
"t2d": ((3, 5), (1, 0)),
26+
"shuffle4d": ((2, 3, 4, 5), (3, 1, 0, 2)),
27+
}
28+
29+
30+
class PermuteModule(torch.nn.Module):
31+
def __init__(self, perm):
32+
super().__init__()
33+
self.perm = perm
34+
35+
def forward(self, x: torch.Tensor) -> torch.Tensor:
36+
return torch.permute(x, self.perm).contiguous()
37+
38+
39+
def _det_input(shape):
40+
g = torch.Generator().manual_seed(0)
41+
return torch.randn(*shape, generator=g, dtype=torch.float32)
42+
43+
44+
def _lower(perm, x: torch.Tensor):
45+
ep = torch.export.export(PermuteModule(perm).eval(), (x,))
46+
return to_edge_transform_and_lower(ep, partitioner=[VulkanPartitioner()])
47+
48+
49+
def _delegated(et) -> bool:
50+
return any(
51+
d.id == "VulkanBackend"
52+
for plan in et.executorch_program.execution_plan
53+
for d in plan.delegates
54+
)
55+
56+
57+
def _op_delegated(edge, op_substr: str) -> bool:
58+
# op must be absorbed into the delegate, not left as a top-level CPU-fallback node.
59+
gm = edge.exported_program().graph_module
60+
return all(op_substr not in str(getattr(n, "target", "")) for n in gm.graph.nodes)
61+
62+
63+
class PermuteTest(unittest.TestCase):
64+
def test_export_delegates(self) -> None:
65+
for name, (shape, perm) in CONFIGS.items():
66+
with self.subTest(name=name):
67+
edge = _lower(perm, _det_input(shape))
68+
et = edge.to_executorch()
69+
self.assertTrue(
70+
_delegated(et),
71+
f"Expected a VulkanBackend delegate (permute {name})",
72+
)
73+
self.assertTrue(
74+
_op_delegated(edge, "permute"),
75+
f"permute not delegated (fell back to CPU) for {name}",
76+
)

backends/webgpu/test/tester.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
exir_ops.edge.aten.squeeze_copy.dims,
2929
exir_ops.edge.aten.unsqueeze_copy.default,
3030
exir_ops.edge.aten.slice_copy.Tensor,
31+
exir_ops.edge.aten.permute_copy.default,
3132
]
3233

3334

0 commit comments

Comments
 (0)