From 7469f12c3dd38b133f83969fa666c620bab94a76 Mon Sep 17 00:00:00 2001 From: Julian Ng-Thow-Hing Date: Thu, 18 Jun 2026 14:35:24 -0700 Subject: [PATCH] Update [ghstack-poisoned] --- backends/webgpu/test/op_tests/cases.py | 31 ++++++++++ backends/webgpu/test/ops/sigmoid/__init__.py | 5 ++ .../webgpu/test/ops/sigmoid/test_sigmoid.py | 59 +++++++++++++++++++ backends/webgpu/test/tester.py | 1 + 4 files changed, 96 insertions(+) create mode 100644 backends/webgpu/test/ops/sigmoid/__init__.py create mode 100644 backends/webgpu/test/ops/sigmoid/test_sigmoid.py diff --git a/backends/webgpu/test/op_tests/cases.py b/backends/webgpu/test/op_tests/cases.py index 8c8f5682c1f..30bd86004f7 100644 --- a/backends/webgpu/test/op_tests/cases.py +++ b/backends/webgpu/test/op_tests/cases.py @@ -44,6 +44,11 @@ CONFIGS as _SELECT_CONFIGS, SelectModule, ) +from executorch.backends.webgpu.test.ops.sigmoid.test_sigmoid import ( + _det_input as _sigmoid_det_input, + N as _SIGMOID_N, + SigmoidModule, +) from executorch.backends.webgpu.test.ops.view_copy.test_view_copy import ( CONFIGS as _VIEW_CONFIGS, ViewModule, @@ -153,3 +158,29 @@ def _view_copy_suite() -> WebGPUTestSuite: @register_op_test("select") def _select_suite() -> WebGPUTestSuite: return _fn_config_suite(SelectModule, _SELECT_CONFIGS) + + +def _sigmoid_full_range(_shape) -> torch.Tensor: + # Reuses the monolith's saturation-tail input (linspace(-12, 12)). + return _sigmoid_det_input() + + +@register_op_test("sigmoid") +def _sigmoid_suite() -> WebGPUTestSuite: + # sigmoid has no CONFIGS table; cover unary shapes directly (tol 1e-4). + return WebGPUTestSuite( + module_factory=lambda: SigmoidModule(), + cases=[ + Case(name="vec", inputs=((M1,),)), + Case(name="mat", inputs=((M1, M2),)), + Case(name="rank3", inputs=((S1, M1, M2),)), + Case(name="rank4", inputs=((S1, S2, S2, M2),)), + # Saturation tails sigmoid(+-12) (~6e-6 / 0.999994) that randn shapes miss. + Case( + name="saturation", + inputs=(InputSpec(shape=(_SIGMOID_N,), gen=_sigmoid_full_range),), + ), + ], + atol=1e-4, + rtol=1e-4, + ) diff --git a/backends/webgpu/test/ops/sigmoid/__init__.py b/backends/webgpu/test/ops/sigmoid/__init__.py new file mode 100644 index 00000000000..2e41cd717f6 --- /dev/null +++ b/backends/webgpu/test/ops/sigmoid/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. diff --git a/backends/webgpu/test/ops/sigmoid/test_sigmoid.py b/backends/webgpu/test/ops/sigmoid/test_sigmoid.py new file mode 100644 index 00000000000..1346f36fbcf --- /dev/null +++ b/backends/webgpu/test/ops/sigmoid/test_sigmoid.py @@ -0,0 +1,59 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""`aten.sigmoid.default` module + input for the WebGPU op-test framework. + +`SigmoidModule`, `N`, and `_det_input` are imported by `cases.py` to drive the +declarative op-test suite. `TestSigmoid` is the export-delegation + eager-correctness +smoke test. Sigmoid is on the Llama critical path (`F.silu` -> `sigmoid` + `mul`); the +deterministic input spans the saturation tails. +""" + +import unittest + +import torch + +from executorch.backends.vulkan import VulkanPartitioner +from executorch.exir import to_edge_transform_and_lower + +# Input length; the deterministic input spans the saturation tails. +N = 64 + + +class SigmoidModule(torch.nn.Module): + def forward(self, x: torch.Tensor) -> torch.Tensor: + return torch.sigmoid(x) + + +def _det_input() -> torch.Tensor: + """Deterministic fp32 input spanning negatives, zero, and large magnitudes.""" + return torch.linspace(-12.0, 12.0, N, dtype=torch.float32) + + +def _export(m: torch.nn.Module, x: torch.Tensor): + ep = torch.export.export(m, (x,)) + return to_edge_transform_and_lower( + ep, partitioner=[VulkanPartitioner()] + ).to_executorch() + + +class TestSigmoid(unittest.TestCase): + def test_export_delegates(self) -> None: + et = _export(SigmoidModule().eval(), _det_input()) + found = any( + d.id == "VulkanBackend" + for plan in et.executorch_program.execution_plan + for d in plan.delegates + ) + self.assertTrue(found, "Expected a VulkanBackend delegate (sigmoid)") + + def test_golden_matches_eager(self) -> None: + x = _det_input() + torch.testing.assert_close(SigmoidModule()(x), torch.sigmoid(x)) + + +if __name__ == "__main__": + unittest.main() diff --git a/backends/webgpu/test/tester.py b/backends/webgpu/test/tester.py index 9ba9a4d9ad4..e5dd510d49b 100644 --- a/backends/webgpu/test/tester.py +++ b/backends/webgpu/test/tester.py @@ -24,6 +24,7 @@ exir_ops.edge.aten.mul.Tensor, exir_ops.edge.aten.view_copy.default, exir_ops.edge.aten.select_copy.int, + exir_ops.edge.aten.sigmoid.default, ]