xref: /aosp_15_r20/external/executorch/kernels/test/op_rsub_test.cpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <executorch/kernels/test/FunctionHeaderWrapper.h> // Declares the operator
10 #include <executorch/kernels/test/TestUtil.h>
11 #include <executorch/kernels/test/supported_features.h>
12 #include <executorch/runtime/core/exec_aten/exec_aten.h>
13 #include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h>
14 #include <executorch/runtime/core/exec_aten/testing_util/tensor_util.h>
15 
16 #include <gtest/gtest.h>
17 
18 using namespace ::testing;
19 using exec_aten::Scalar;
20 using exec_aten::ScalarType;
21 using exec_aten::Tensor;
22 using torch::executor::testing::SupportedFeatures;
23 using torch::executor::testing::TensorFactory;
24 
25 class OpRSubScalarOutTest : public OperatorTest {
26  protected:
op_rsub_scalar_out(const Tensor & self,const Scalar & other,const Scalar & alpha,Tensor & out)27   Tensor& op_rsub_scalar_out(
28       const Tensor& self,
29       const Scalar& other,
30       const Scalar& alpha,
31       Tensor& out) {
32     return torch::executor::aten::rsub_outf(context_, self, other, alpha, out);
33   }
34 
35   // Common testing for substraction of scalar for integer Tensor.
36   template <ScalarType DTYPE>
test_integer_rsub_scalar_out()37   void test_integer_rsub_scalar_out() {
38     TensorFactory<DTYPE> tf;
39 
40     const std::vector<int32_t> sizes = {2, 2};
41 
42     // Destination for the rsub.
43     Tensor out = tf.zeros(sizes);
44 
45     // Performs substraction of tensor from scalar.
46     op_rsub_scalar_out(
47         tf.make(sizes, /*data=*/{1, 2, 4, 5}),
48         10,
49         /*alpha=*/2,
50         out);
51 
52     // Check that it matches the expected output.
53     EXPECT_TENSOR_EQ(out, tf.make(sizes, /*data=*/{8, 6, 2, 0}));
54   }
55 
56   // Common testing for substraction between floating point tensor and scalar.
57   template <ScalarType DTYPE>
test_floating_point_rsub_scalar_out()58   void test_floating_point_rsub_scalar_out() {
59     TensorFactory<DTYPE> tf;
60 
61     const std::vector<int32_t> sizes = {2, 2};
62 
63     // Destination for the rsub.
64     Tensor out = tf.zeros(sizes);
65 
66     // Performs substraction of tensor from scalar.
67     op_rsub_scalar_out(
68         tf.make(sizes, /*data=*/{1.1, 2.2, 4.4, 8.8}),
69         1.1,
70         /*alpha=*/1,
71         out);
72 
73     // Check that it matches the expected output.
74     EXPECT_TENSOR_CLOSE(out, tf.make(sizes, /*data=*/{0.0, -1.1, -3.3, -7.7}));
75   }
76 
77   /* %python
78   import torch
79   torch.manual_seed(0)
80   x = torch.rand(2, 3)
81   other = 10
82   alpha = 2
83   res = other - alpha * x
84   op = "op_rsub_scalar_out"
85   opt_setup_params = f"""
86     Scalar other = {other};
87     Scalar alpha = {alpha};
88   """
89   opt_extra_params = "other, alpha,"
90   out_args = "out_shape, dynamism"
91   dtype = "ScalarType::Float"
92   check = "EXPECT_TENSOR_CLOSE" */
93 
test_dynamic_shape(const std::vector<int32_t> & out_shape,enum torch::executor::TensorShapeDynamism dynamism)94   void test_dynamic_shape(
95       const std::vector<int32_t>& out_shape,
96       enum torch::executor::TensorShapeDynamism dynamism) {
97     /* %python
98     %rewrite(unary_op) */
99 
100     TensorFactory<ScalarType::Float> tf;
101 
102     Tensor x = tf.make(
103         {2, 3},
104         {0.49625658988952637,
105          0.7682217955589294,
106          0.08847743272781372,
107          0.13203048706054688,
108          0.30742281675338745,
109          0.6340786814689636});
110     Tensor expected = tf.make(
111         {2, 3},
112         {9.007486343383789,
113          8.463556289672852,
114          9.823044776916504,
115          9.735939025878906,
116          9.385154724121094,
117          8.731842994689941});
118 
119     Scalar other = 10;
120     Scalar alpha = 2;
121 
122     Tensor out = tf.zeros(out_shape, dynamism);
123     op_rsub_scalar_out(x, other, alpha, out);
124     EXPECT_TENSOR_CLOSE(out, expected);
125   }
126 };
127 
TEST_F(OpRSubScalarOutTest,ByteTensors)128 TEST_F(OpRSubScalarOutTest, ByteTensors) {
129   test_integer_rsub_scalar_out<ScalarType::Byte>();
130 }
131 
TEST_F(OpRSubScalarOutTest,CharTensors)132 TEST_F(OpRSubScalarOutTest, CharTensors) {
133   test_integer_rsub_scalar_out<ScalarType::Char>();
134 }
135 
TEST_F(OpRSubScalarOutTest,ShortTensors)136 TEST_F(OpRSubScalarOutTest, ShortTensors) {
137   test_integer_rsub_scalar_out<ScalarType::Short>();
138 }
139 
TEST_F(OpRSubScalarOutTest,IntTensors)140 TEST_F(OpRSubScalarOutTest, IntTensors) {
141   test_integer_rsub_scalar_out<ScalarType::Int>();
142 }
143 
TEST_F(OpRSubScalarOutTest,LongTensors)144 TEST_F(OpRSubScalarOutTest, LongTensors) {
145   test_integer_rsub_scalar_out<ScalarType::Long>();
146 }
147 
TEST_F(OpRSubScalarOutTest,IntTensorFloatAlphaDies)148 TEST_F(OpRSubScalarOutTest, IntTensorFloatAlphaDies) {
149   // op_rsub_scalar_out() doesn't handle floating alpha for intergal inputs
150   TensorFactory<ScalarType::Int> tf;
151 
152   const std::vector<int32_t> sizes = {2, 2};
153 
154   // Destination for the op.
155   Tensor out = tf.zeros(sizes);
156 
157   // Subtraction operation on integral tensor with floating alpha
158   // should cause an assertion and kill the test process.
159   ET_EXPECT_KERNEL_FAILURE(
160       context_, op_rsub_scalar_out(tf.ones(sizes), 0, /*alpha=*/.7, out));
161 }
162 
TEST_F(OpRSubScalarOutTest,FloatTensors)163 TEST_F(OpRSubScalarOutTest, FloatTensors) {
164   test_floating_point_rsub_scalar_out<ScalarType::Float>();
165 }
166 
TEST_F(OpRSubScalarOutTest,DoubleTensors)167 TEST_F(OpRSubScalarOutTest, DoubleTensors) {
168   test_floating_point_rsub_scalar_out<ScalarType::Double>();
169 }
170 
TEST_F(OpRSubScalarOutTest,UnhandledDtypeDies)171 TEST_F(OpRSubScalarOutTest, UnhandledDtypeDies) {
172   // op_rsub_scalar_out() doesn't handle Bool.
173   TensorFactory<ScalarType::Bool> tf;
174 
175   const std::vector<int32_t> sizes = {2, 2};
176 
177   // Subtrahend
178   Tensor a = tf.make(sizes, /*data=*/{false, true, false, true});
179 
180   // Destination for the subtraction.
181   Tensor out = tf.zeros(sizes);
182 
183   // Subtraction operation on boolean tensor should cause an assertion and
184   // kill the test process.
185   ET_EXPECT_KERNEL_FAILURE(
186       context_, op_rsub_scalar_out(a, false, /*alpha=*/0, out));
187 }
188 
189 // The output tensor may not have a dtype different from the input even if it
190 // has the same shape.
TEST_F(OpRSubScalarOutTest,MismatchedOutputDtypeDies)191 TEST_F(OpRSubScalarOutTest, MismatchedOutputDtypeDies) {
192   // Two different dtypes. This test uses two types with the same size to
193   // demonstrate that the ScalarType itself matters, not the size of the
194   // tensor elements.
195   TensorFactory<ScalarType::Byte> tf_byte;
196   TensorFactory<ScalarType::Char> tf_char;
197 
198   const std::vector<int32_t> sizes = {2, 2};
199 
200   // Minuend and subtrahend of the same dtype.
201   Tensor a = tf_byte.ones(sizes);
202 
203   // Destination with a dtype different from the inputs.
204   Tensor out = tf_char.zeros(sizes);
205 
206   // Performing substraction of scalar from tesnor and write into a mismatched
207   // output should cause an assertion and kill the test process.
208   ET_EXPECT_KERNEL_FAILURE(
209       context_, op_rsub_scalar_out(a, 1, /*alpha=*/0, out));
210 }
211 
212 // Mismatched shape tests.
213 
TEST_F(OpRSubScalarOutTest,MismatchedOutputShapesDies)214 TEST_F(OpRSubScalarOutTest, MismatchedOutputShapesDies) {
215   if (SupportedFeatures::get()->is_aten) {
216     GTEST_SKIP() << "ATen kernel can handle output shapes";
217   }
218 
219   TensorFactory<ScalarType::Int> tf;
220 
221   const std::vector<int32_t> sizes = {2, 2};
222 
223   Tensor a = tf.ones(sizes);
224 
225   // Destination with a different shape.
226   Tensor out = tf.zeros(/*sizes=*/{4});
227 
228   // Performing substraction of scalar from tensor into a mismatched output
229   // should cause an assertion and kill the test process.
230   ET_EXPECT_KERNEL_FAILURE(
231       context_, op_rsub_scalar_out(a, 1, /*alpha=*/0, out));
232 }
233 
TEST_F(OpRSubScalarOutTest,DynamicShapeUpperBoundSameAsExpected)234 TEST_F(OpRSubScalarOutTest, DynamicShapeUpperBoundSameAsExpected) {
235   test_dynamic_shape(
236       {2, 3}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
237 }
238 
TEST_F(OpRSubScalarOutTest,DynamicShapeUpperBoundLargerThanExpected)239 TEST_F(OpRSubScalarOutTest, DynamicShapeUpperBoundLargerThanExpected) {
240   if (!torch::executor::testing::SupportedFeatures::get()->output_resize) {
241     GTEST_SKIP() << "Dynamic shape not supported";
242   }
243   test_dynamic_shape(
244       {10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
245 }
246 
TEST_F(OpRSubScalarOutTest,DynamicShapeUnbound)247 TEST_F(OpRSubScalarOutTest, DynamicShapeUnbound) {
248   if (!torch::executor::testing::SupportedFeatures::get()->output_resize) {
249     GTEST_SKIP() << "Dynamic shape not supported";
250   }
251   test_dynamic_shape(
252       {1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
253 }
254