Skip to content

Commit 2cd1d41

Browse files
committed
Arm Backend: Add tests for rsub, transpose_copy and t_copy
Signed-off-by: Agrima Khare <[email protected]> Change-Id: I8aea84b9a83bbe1b429eeb474676854f41e09ee3
1 parent 418c584 commit 2cd1d41

File tree

5 files changed

+365
-4
lines changed

5 files changed

+365
-4
lines changed

backends/arm/quantizer/quantization_annotator.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ def _match_pattern(
333333
torch.ops.aten.transpose.Dimname,
334334
torch.ops.aten.transpose.int,
335335
torch.ops.aten.transpose_copy.int,
336+
torch.ops.aten.t_copy.default,
336337
torch.ops.aten.tile.default,
337338
torch.ops.aten.flip.default,
338339
torch.ops.aten.chunk.default,
@@ -512,7 +513,8 @@ def any_or_hardtanh_min_zero(n: Node):
512513
quant_properties.quant_inputs = [
513514
_QuantProperty(0, input_act_qspec),
514515
_QuantProperty(
515-
1, input_act_qspec if node.args[0] == node.args[1] else shared_qspec # type: ignore[arg-type]
516+
1,
517+
input_act_qspec if node.args[0] == node.args[1] else shared_qspec, # type: ignore[arg-type]
516518
),
517519
]
518520
quant_properties.quant_output = _QuantProperty(0, shared_qspec) # type: ignore[arg-type]
@@ -531,7 +533,8 @@ def any_or_hardtanh_min_zero(n: Node):
531533
)
532534
quant_properties.quant_inputs = [_QuantProperty(0, input_qspec)] # type: ignore[arg-type]
533535
quant_properties.quant_output = _QuantProperty(
534-
0, SharedQuantizationSpec((node.args[0], node)) # type: ignore[arg-type]
536+
0,
537+
SharedQuantizationSpec((node.args[0], node)), # type: ignore[arg-type]
535538
)
536539
elif node.target in (
537540
torch.ops.aten.cat.default,
@@ -564,7 +567,8 @@ def any_or_hardtanh_min_zero(n: Node):
564567
elif node.target in _one_to_one_shared_input_qspec:
565568
quant_properties.quant_inputs = [_QuantProperty(0, input_act_qspec)]
566569
quant_properties.quant_output = _QuantProperty(
567-
0, SharedQuantizationSpec((node.args[0], node)) # type: ignore[arg-type]
570+
0,
571+
SharedQuantizationSpec((node.args[0], node)), # type: ignore[arg-type]
568572
)
569573
elif node.target in [
570574
torch.ops.aten.eq.Tensor,
@@ -577,7 +581,8 @@ def any_or_hardtanh_min_zero(n: Node):
577581
quant_properties.quant_inputs = [
578582
_QuantProperty(0, input_act_qspec),
579583
_QuantProperty(
580-
1, input_act_qspec if node.args[0] == node.args[1] else shared_qspec # type: ignore[arg-type]
584+
1,
585+
input_act_qspec if node.args[0] == node.args[1] else shared_qspec, # type: ignore[arg-type]
581586
),
582587
]
583588
quant_properties.quant_output = None

backends/arm/test/ops/test_rsub.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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 torch
9+
10+
from executorch.backends.arm.test import common
11+
from executorch.backends.arm.test.tester.test_pipeline import (
12+
EthosU55PipelineINT,
13+
EthosU85PipelineINT,
14+
TosaPipelineFP,
15+
TosaPipelineINT,
16+
VgfPipeline,
17+
)
18+
19+
rsub_test_data = {
20+
"rand_2D_4x4": lambda: (torch.rand(4, 4), 2),
21+
"rand_3D_4x4x4": lambda: (torch.rand(4, 2, 2), 1.5),
22+
"rand_4D_2x2x4x4": lambda: (torch.rand(2, 2, 4, 4), -1.1),
23+
"rand_4D_big_small": lambda: (
24+
(10e30) * torch.randn(1, 20, 30, 40),
25+
-0.25,
26+
),
27+
"zero": lambda: (torch.rand(4, 4), 0),
28+
# "swapped": lambda: (2, torch.rand(4, 4)), # torch.rsub(Scalar, Tensor) is not supported as it is not supported in eager mode.
29+
}
30+
31+
32+
class Rsub(torch.nn.Module):
33+
aten_op = "torch.ops.aten.rsub.Scalar"
34+
exir_op = "executorch_exir_dialects_edge__ops_aten_sub_Tensor"
35+
36+
def forward(self, x: torch.Tensor, y: int):
37+
return torch.rsub(x, y)
38+
39+
40+
input_t1 = Tuple[torch.Tensor, torch.Tensor]
41+
42+
43+
@common.parametrize("test_data", rsub_test_data)
44+
def test_rsub_scalar_tosa_FP(test_data):
45+
pipeline = TosaPipelineFP[input_t1](
46+
Rsub(),
47+
test_data(),
48+
aten_op=Rsub.aten_op,
49+
exir_op=Rsub.exir_op,
50+
use_to_edge_transform_and_lower=False,
51+
)
52+
pipeline.run()
53+
54+
55+
@common.parametrize("test_data", rsub_test_data)
56+
def test_rsub_scalar_tosa_INT(test_data):
57+
"""Test Subtraction (TOSA INT)"""
58+
pipeline = TosaPipelineINT[input_t1](
59+
Rsub(),
60+
test_data(),
61+
aten_op="torch.ops.aten.sub.Tensor",
62+
exir_op=Rsub.exir_op,
63+
use_to_edge_transform_and_lower=False,
64+
qtol=0,
65+
)
66+
pipeline.run()
67+
68+
69+
@common.parametrize("test_data", rsub_test_data)
70+
@common.XfailIfNoCorstone300
71+
def test_rsub_scalar_u55_INT(test_data):
72+
"""Test Subtraction on Ethos-U55 (FVP Mode)"""
73+
pipeline = EthosU55PipelineINT[input_t1](
74+
Rsub(),
75+
test_data(),
76+
aten_ops="torch.ops.aten.sub.Tensor",
77+
exir_ops=Rsub.exir_op,
78+
run_on_fvp=True,
79+
use_to_edge_transform_and_lower=False,
80+
)
81+
pipeline.run()
82+
83+
84+
@common.parametrize("test_data", rsub_test_data)
85+
@common.XfailIfNoCorstone320
86+
def test_rsub_scalar_u85_INT(test_data):
87+
"""Test Subtraction on Ethos-U85 (FVP Mode)"""
88+
pipeline = EthosU85PipelineINT[input_t1](
89+
Rsub(),
90+
test_data(),
91+
aten_ops="torch.ops.aten.sub.Tensor",
92+
exir_ops=Rsub.exir_op,
93+
run_on_fvp=True,
94+
use_to_edge_transform_and_lower=False,
95+
)
96+
pipeline.run()
97+
98+
99+
@common.parametrize("test_data", rsub_test_data)
100+
@common.SkipIfNoModelConverter
101+
def test_rsub_scalar_vgf_FP(test_data: Tuple[torch.Tensor]):
102+
"""Test Subtraction (VGF FP)"""
103+
pipeline = VgfPipeline[input_t1](
104+
Rsub(),
105+
test_data(),
106+
Rsub.aten_op,
107+
Rsub.exir_op,
108+
tosa_version="TOSA-1.0+FP",
109+
use_to_edge_transform_and_lower=False,
110+
)
111+
pipeline.run()
112+
113+
114+
@common.parametrize("test_data", rsub_test_data)
115+
@common.SkipIfNoModelConverter
116+
def test_rsub_scalar_vgf_INT(test_data: Tuple[torch.Tensor]):
117+
"""Test Subtraction (VGF INT)"""
118+
pipeline = VgfPipeline[input_t1](
119+
Rsub(),
120+
test_data(),
121+
aten_op="torch.ops.aten.sub.Tensor",
122+
exir_op=Rsub.exir_op,
123+
tosa_version="TOSA-1.0+INT",
124+
use_to_edge_transform_and_lower=False,
125+
)
126+
pipeline.run()

backends/arm/test/ops/test_sub.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def test_sub_tensor_tosa_INT_2(test_data: Tuple[torch.Tensor, torch.Tensor]):
132132
@common.parametrize("test_data", sub_tan_test_data)
133133
def test_sub_tensor_tosa_INT_3(test_data: Tuple[torch.Tensor, torch.Tensor]):
134134
"""Test Two-Operand Subtraction (TOSA INT)"""
135+
# This test has only been added to the tosa INT profile in order to catch quantization-induced errors.
135136
pipeline = TosaPipelineINT[input_t2](
136137
SubTan(), test_data(), aten_op, exir_op, qtol=0
137138
)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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 torch
9+
10+
from executorch.backends.arm.test import common
11+
from executorch.backends.arm.test.tester.test_pipeline import (
12+
EthosU55PipelineINT,
13+
EthosU85PipelineINT,
14+
TosaPipelineFP,
15+
TosaPipelineINT,
16+
VgfPipeline,
17+
)
18+
19+
test_data_suite = {
20+
# test_name: (test_data, min, max)
21+
"rand": lambda: (torch.rand(2, 3),),
22+
"rand_multiplied": lambda: (torch.rand(3, 4) * 10,),
23+
"ones": lambda: (torch.ones(5, 10),),
24+
"randn": lambda: (torch.randn(1, 10) * 2,),
25+
}
26+
27+
28+
class TCopy(torch.nn.Module):
29+
aten_op = "torch.ops.aten.t_copy.default"
30+
exir_op = "executorch_exir_dialects_edge__ops_aten_permute_copy_default"
31+
32+
def forward(self, x: torch.Tensor):
33+
return torch.t_copy(x)
34+
35+
36+
input_t1 = Tuple[torch.Tensor]
37+
38+
39+
@common.parametrize("test_data", test_data_suite)
40+
def test_t_tosa_FP(test_data: Tuple):
41+
pipeline = TosaPipelineFP[input_t1](
42+
TCopy(),
43+
test_data(),
44+
aten_op=TCopy.aten_op,
45+
exir_op=TCopy.exir_op,
46+
use_to_edge_transform_and_lower=False,
47+
)
48+
49+
pipeline.run()
50+
51+
52+
@common.parametrize("test_data", test_data_suite)
53+
def test_t_tosa_INT(test_data: Tuple):
54+
pipeline = TosaPipelineINT[input_t1](
55+
TCopy(),
56+
test_data(),
57+
aten_op=TCopy.aten_op,
58+
exir_op=TCopy.exir_op,
59+
use_to_edge_transform_and_lower=False,
60+
)
61+
pipeline.run()
62+
63+
64+
@common.XfailIfNoCorstone300
65+
@common.parametrize("test_data", test_data_suite)
66+
def test_t_u55_INT(test_data: Tuple):
67+
pipeline = EthosU55PipelineINT[input_t1](
68+
TCopy(),
69+
test_data(),
70+
aten_ops=TCopy.aten_op,
71+
exir_ops=[],
72+
use_to_edge_transform_and_lower=True,
73+
)
74+
pipeline.run()
75+
76+
77+
@common.XfailIfNoCorstone320
78+
@common.parametrize("test_data", test_data_suite)
79+
def test_t_u85_INT(test_data: Tuple):
80+
pipeline = EthosU85PipelineINT[input_t1](
81+
TCopy(),
82+
test_data(),
83+
aten_ops=TCopy.aten_op,
84+
exir_ops=TCopy.exir_op,
85+
use_to_edge_transform_and_lower=False,
86+
)
87+
pipeline.run()
88+
89+
90+
@common.parametrize("test_data", test_data_suite)
91+
@common.SkipIfNoModelConverter
92+
def test_t_vgf_FP(test_data: Tuple):
93+
pipeline = VgfPipeline[input_t1](
94+
TCopy(),
95+
test_data(),
96+
aten_op=TCopy.aten_op,
97+
exir_op=TCopy.exir_op,
98+
tosa_version="TOSA-1.0+FP",
99+
use_to_edge_transform_and_lower=False,
100+
)
101+
pipeline.run()
102+
103+
104+
@common.parametrize("test_data", test_data_suite)
105+
@common.SkipIfNoModelConverter
106+
def test_t_vgf_INT(test_data: Tuple):
107+
pipeline = VgfPipeline[input_t1](
108+
TCopy(),
109+
test_data(),
110+
aten_op=TCopy.aten_op,
111+
exir_op=TCopy.exir_op,
112+
tosa_version="TOSA-1.0+INT",
113+
use_to_edge_transform_and_lower=False,
114+
)
115+
pipeline.run()

0 commit comments

Comments
 (0)