Skip to content

Commit b1bdf8e

Browse files
authored
Merge branch 'main' into ph-mypy-ops-enable-mypy-tests
2 parents e5c1a5d + 556142c commit b1bdf8e

File tree

8 files changed

+217
-19
lines changed

8 files changed

+217
-19
lines changed

backends/arm/_passes/arm_pass_manager.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,12 @@ def _transform(self, graph_module: GraphModule):
159159
def _tosa_pipeline(
160160
self, exported_program: ExportedProgram, graph_module: GraphModule
161161
) -> GraphModule:
162+
# Preprocessing passes
163+
162164
self.add_pass(AnnotateOutputDimOrderPass())
165+
166+
# Node transformation passes (pre q/dq folding)
167+
163168
self.add_pass(FuseQuantizedActivationPass())
164169
self.add_pass(RemoveGetItemPass())
165170
self.add_pass(ConvertToClampPass())
@@ -174,8 +179,19 @@ def _tosa_pipeline(
174179
self.add_pass(ConvertELUParamsPass())
175180
self.add_pass(ConvertSplitToSlicePass())
176181
self.add_pass(QuantizeOperatorArguments())
182+
183+
# Fold Q/DQ nodes, insert INT8/INT32 rescales.
184+
177185
self.add_pass(FoldAndAnnotateQParamsPass(exported_program)) # type: ignore[call-arg]
178186
self.add_pass(FuseDuplicateUsersPass())
187+
# TODO: DecomposeLinearPass should run after InsertRescaleInt32Pass or
188+
# before FoldAndAnnotateQParamsPass but is unable to at the moment.
189+
# Ticket: MLETORCH-1539
190+
self.add_pass(DecomposeLinearPass())
191+
self.add_pass(InsertRescaleInt32Pass())
192+
193+
# Node transformation passes (post q/dq folding)
194+
179195
self.add_pass(DecomposeExpm1Pass())
180196
self.add_pass(DecomposeLogitPass())
181197
self.add_pass(DecomposeMaskedFill())
@@ -196,57 +212,67 @@ def _tosa_pipeline(
196212
self.add_pass(DecomposeSignPass())
197213
self.add_pass(DecomposeFloorDividePass())
198214
self.add_pass(DecomposeDivTensorModePass())
215+
self.add_pass(DecomposeGeluPass())
216+
self.add_pass(DecomposeAddSubAlphaPass())
217+
self.add_pass(DecomposeGroupedConv())
218+
self.add_pass(Conv1dUnsqueezePass())
219+
220+
# Scalars -> tensors, match tensor dtypes and ranks.
221+
199222
self.add_pass(ReplaceScalarWithTensorByProfilePass())
223+
self.add_pass(ConvertFullLikeToFullPass())
224+
self.add_pass(MatchArgDtypePass())
225+
self.add_pass(UnsqueezeScalarPlaceholdersPass(exported_program))
226+
# TODO: Move DecomposeNotEqualPass to before or after this block of
227+
# passes. Ticket: MLETORCH-1540
228+
self.add_pass(DecomposeNotEqualPass())
229+
self.add_pass(MatchArgRanksPass(exported_program))
230+
self.add_pass(FuseConstantArgsPass(exported_program))
231+
232+
# Node transformation passes (post scalar-removal)
233+
200234
self.add_pass(DecomposeRemainderPass())
201235
self.add_pass(DecomposeDivTensorModePass())
202236
self.add_pass(DecomposeEmbeddingPass())
203237
self.add_pass(FuseBatchnorm2DPass(exported_program))
204238
self.add_pass(ConvertMmToBmmPass())
205239
self.add_pass(DecomposeGluPass())
206-
self.add_pass(DecomposeLinearPass())
207240
self.add_pass(DecomposeLeakyReLUPass())
208-
self.add_pass(DecomposeNotEqualPass())
209241
self.add_pass(DecomposeDivPass())
210-
self.add_pass(DecomposeAddSubAlphaPass())
211242
self.add_pass(DecomposeSoftmaxPass())
212-
self.add_pass(DecomposeGeluPass())
213-
self.add_pass(ConvertFullLikeToFullPass())
214243
self.add_pass(ConvertMinMaxPass())
215244
self.add_pass(ConvertAnyDefaultDimDimsPass())
216-
self.add_pass(MatchArgDtypePass())
217-
self.add_pass(UnsqueezeScalarPlaceholdersPass(exported_program))
218-
self.add_pass(MatchArgRanksPass(exported_program))
219245
self.add_pass(DecomposeAdaptiveAvgPool2dPass())
220246
self.add_pass(DecomposeAvgPool2d())
221247
self.add_pass(
222248
DecorateFp32toInt32CastingPass()
223249
) # Require that no new fp32->int32 is introduced after this pass
224250
self.add_pass(ComputeConstantOpsAOT(exported_program))
225-
226-
self.add_pass(DecomposeGroupedConv())
227251
self.add_pass(ConvertExpandCopyToRepeatPass())
228252
self.add_pass(UnsqueezeBeforeRepeatPass())
229253
self.add_pass(DecomposeCumsumPass(exported_program))
230-
self.add_pass(Conv1dUnsqueezePass())
231254
self.add_pass(DecomposeMaxPool2DPass())
232255
self.add_pass(SizeAdjustInputPass())
233256
self.add_pass(DecomposeSelectPass())
234257
self.add_pass(ConvertSqueezesToViewPass())
235258
self.add_pass(CastToInt32Pass())
236259
self.add_pass(BroadcastArgsPass())
237-
238260
self.add_pass(ConvertPermuteSingletonToViewPass())
239261
self.add_pass(FuseViewCopyTransform())
240-
self.add_pass(FuseConstantArgsPass(exported_program))
241262
self.add_pass(DecomposeConv2dWithInt16ActivationPass())
242-
self.add_pass(CastInt64BuffersToInt32Pass(exported_program))
263+
self.add_pass(DecomposeSumPass())
243264
self.add_pass(InsertTableOpsPass(exported_program))
265+
266+
# Aten -> TOSA transformation passes
267+
244268
self.add_pass(RewriteUpsamplePass())
245269
self.add_pass(RewriteConv2dPass(exported_program))
246270
self.add_pass(RewriteMatmulPass())
271+
272+
# Postprocessing/cleanup passes
273+
274+
self.add_pass(CastInt64BuffersToInt32Pass(exported_program))
247275
self.add_pass(FuseEqualPlaceholdersPass(exported_program))
248-
self.add_pass(InsertRescaleInt32Pass())
249-
self.add_pass(DecomposeSumPass())
250276
self.add_pass(ToTosaMemoryFormatPass(exported_program))
251277
self.add_pass(RemoveNoopPass())
252278
self.add_pass(InsertRescalePass())

backends/arm/_passes/decompose_linear_pass.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
create_node,
1313
get_first_fake_tensor,
1414
)
15+
from executorch.backends.arm._passes.insert_rescales_pass import InsertRescaleInt32Pass
1516
from executorch.exir.dialects._ops import ops as exir_ops
1617
from executorch.exir.pass_base import ExportPass, PassResult
1718

@@ -26,7 +27,7 @@ class DecomposeLinearPass(ArmPass):
2627
output = view(conv2d)
2728
"""
2829

29-
_passes_required_after: Set[Type[ExportPass]] = set()
30+
_passes_required_after: Set[Type[ExportPass]] = {InsertRescaleInt32Pass}
3031

3132
def call(self, graph_module):
3233
for node in graph_module.graph.nodes:

backends/arm/_passes/match_arg_ranks_pass.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def __init__(self, exported_program: ExportedProgram) -> None:
5757
exir_ops.edge.aten.lt.Tensor,
5858
exir_ops.edge.aten.le.Tensor,
5959
exir_ops.edge.aten.pow.Tensor_Tensor,
60+
exir_ops.edge.aten.remainder.Tensor,
6061
exir_ops.edge.aten.where.self,
6162
exir_ops.edge.aten.bitwise_and.Tensor,
6263
exir_ops.edge.aten.bitwise_xor.Tensor,

backends/arm/operator_support/reduce_sum_support.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
#
33
# This source code is licensed under the BSD-style license found in the
44
# LICENSE file in the root directory of this source tree.
5+
"""Declare operator support for ``aten.sum.dim_IntList`` in TOSA.
56
7+
Provide shape constraints for U55 subsets; otherwise allow reductions.
8+
9+
"""
610
from typing import cast
711

812
import torch.fx as fx
@@ -16,14 +20,25 @@
1620

1721
@register_tosa_support_check
1822
class SumSupported(SupportedTOSAOperatorCheck):
23+
"""Provide TOSA support check for sum over dimensions."""
24+
1925
targets = [exir_ops.edge.aten.sum.dim_IntList]
2026

2127
tosa_specs = [
2228
TosaSpecification.create_from_string("TOSA-1.0+INT"),
2329
TosaSpecification.create_from_string("TOSA-1.0+FP"),
2430
]
2531

26-
def is_node_tosa_supported(self, node: fx.Node, tosa_spec: TosaSpecification):
32+
def is_node_tosa_supported(
33+
self, node: fx.Node, tosa_spec: TosaSpecification
34+
) -> bool:
35+
"""Return True if the node is supported by TOSA.
36+
37+
On U55 subsets, enforce bounds on the reduced dimension and the products
38+
of sizes before/after the reduction axis. On other targets, accept the
39+
operation unconditionally.
40+
41+
"""
2742
if not tosa_spec.is_U55_subset:
2843
return True
2944

backends/arm/requirements-arm-models-test.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# This source code is licensed under the BSD-style license found in the
44
# LICENSE file in the root directory of this source tree.
55

6-
diffusers[torch] == 0.33.1
6+
diffusers[torch] == 0.33.1

backends/arm/scripts/install_models_for_test.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,16 @@
66

77
set -e
88
pip install -r backends/arm/requirements-arm-models-test.txt
9+
10+
# Install model gym repository
11+
git clone https://github.com/arm/neural-graphics-model-gym.git
12+
cd neural-graphics-model-gym
13+
# Remove model-converter installation from model-gym repository (to prevent overwriting executorch version)
14+
if [[ "$(uname)" == "Darwin" ]]; then
15+
sed -i '' 's/^model-converter = "ng_model_gym.bin.model_converter_launcher:main"/#&/' pyproject.toml
16+
else
17+
sed -i 's/^model-converter = "ng_model_gym.bin.model_converter_launcher:main"/#&/' pyproject.toml
18+
fi
19+
pip install . --no-deps
20+
cd ..
21+
rm -rf neural-graphics-model-gym

backends/arm/test/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
# This source code is licensed under the BSD-style license found in the
44
# LICENSE file in the root directory of this source tree.
55

6+
import logging
67
import os
78
import random
9+
import sys
810
from typing import Any
911

1012
import pytest
@@ -27,6 +29,8 @@ def pytest_configure(config):
2729
if config.option.arm_run_tosa_version:
2830
pytest._test_options["tosa_version"] = config.option.arm_run_tosa_version
2931

32+
logging.basicConfig(stream=sys.stdout)
33+
3034

3135
def pytest_collection_modifyitems(config, items):
3236
pass
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Copyright 2025 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from typing import Tuple
7+
8+
import pytest
9+
import torch
10+
11+
from executorch.backends.arm.test import common
12+
from executorch.backends.arm.test.tester.test_pipeline import (
13+
EthosU55PipelineINT,
14+
EthosU85PipelineINT,
15+
TosaPipelineFP,
16+
TosaPipelineINT,
17+
VgfPipeline,
18+
)
19+
20+
from huggingface_hub import hf_hub_download
21+
22+
from ng_model_gym.usecases.nss.model.model_blocks import AutoEncoderV1
23+
24+
input_t = Tuple[torch.Tensor] # Input x
25+
26+
27+
class NSS(torch.nn.Module):
28+
def __init__(self, *args, **kwargs):
29+
super().__init__(*args, **kwargs)
30+
self.auto_encoder = AutoEncoderV1()
31+
32+
33+
def nss() -> AutoEncoderV1:
34+
"""Get an instance of NSS with weights loaded."""
35+
36+
weights = hf_hub_download(
37+
repo_id="Arm/neural-super-sampling", filename="nss_v0.1.0_fp32.pt"
38+
)
39+
40+
nss_model = NSS()
41+
nss_model.load_state_dict(
42+
torch.load(weights, map_location=torch.device("cpu"), weights_only=True),
43+
strict=False,
44+
)
45+
return nss_model.auto_encoder
46+
47+
48+
def example_inputs():
49+
return (torch.randn((1, 12, 544, 960)),)
50+
51+
52+
def test_nss_tosa_FP():
53+
pipeline = TosaPipelineFP[input_t](
54+
nss().eval(),
55+
example_inputs(),
56+
aten_op=[],
57+
exir_op=[],
58+
use_to_edge_transform_and_lower=True,
59+
)
60+
pipeline.add_stage_after("export", pipeline.tester.dump_operator_distribution)
61+
pipeline.run()
62+
63+
64+
def test_nss_tosa_INT():
65+
pipeline = TosaPipelineINT[input_t](
66+
nss().eval(),
67+
example_inputs(),
68+
aten_op=[],
69+
exir_op=[],
70+
use_to_edge_transform_and_lower=True,
71+
)
72+
pipeline.run()
73+
74+
75+
@pytest.mark.skip(reason="No support for aten_upsample_nearest2d_vec on U55")
76+
@common.XfailIfNoCorstone300
77+
def test_nss_u55_INT():
78+
pipeline = EthosU55PipelineINT[input_t](
79+
nss().eval(),
80+
example_inputs(),
81+
aten_ops=[],
82+
exir_ops=[],
83+
run_on_fvp=True,
84+
use_to_edge_transform_and_lower=True,
85+
)
86+
pipeline.run()
87+
88+
89+
@pytest.mark.skip(
90+
reason="Fails at input memory allocation for input shape: [1, 12, 544, 960]"
91+
)
92+
@common.XfailIfNoCorstone320
93+
def test_nss_u85_INT():
94+
pipeline = EthosU85PipelineINT[input_t](
95+
nss().eval(),
96+
example_inputs(),
97+
aten_ops=[],
98+
exir_ops=[],
99+
run_on_fvp=True,
100+
use_to_edge_transform_and_lower=True,
101+
)
102+
pipeline.run()
103+
104+
105+
@pytest.mark.xfail(
106+
reason="[MLETORCH-1430]: Double types are not supported in buffers in MSL"
107+
)
108+
@common.SkipIfNoModelConverter
109+
def test_nss_vgf_FP():
110+
pipeline = VgfPipeline[input_t](
111+
nss().eval(),
112+
example_inputs(),
113+
aten_op=[],
114+
exir_op=[],
115+
tosa_version="TOSA-1.0+FP",
116+
use_to_edge_transform_and_lower=True,
117+
run_on_vulkan_runtime=True,
118+
)
119+
pipeline.run()
120+
121+
122+
@common.SkipIfNoModelConverter
123+
def test_nss_vgf_INT():
124+
pipeline = VgfPipeline[input_t](
125+
nss().eval(),
126+
example_inputs(),
127+
aten_op=[],
128+
exir_op=[],
129+
tosa_version="TOSA-1.0+INT",
130+
symmetric_io_quantization=True,
131+
use_to_edge_transform_and_lower=True,
132+
run_on_vulkan_runtime=True,
133+
)
134+
pipeline.run()
135+
136+
137+
ModelUnderTest = nss().eval()
138+
ModelInputs = example_inputs()

0 commit comments

Comments
 (0)