xref: /aosp_15_r20/external/executorch/kernels/test/op_round_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::ScalarType;
20 using exec_aten::Tensor;
21 using torch::executor::testing::TensorFactory;
22 
23 class OpRoundTest : public OperatorTest {
24  protected:
op_round_out(const Tensor & self,Tensor & out)25   Tensor& op_round_out(const Tensor& self, Tensor& out) {
26     return torch::executor::aten::round_outf(context_, self, out);
27   }
28 
29   // Common testing for round on two floating point Tensors.
30   template <ScalarType DTYPE>
test_round_execution_floats()31   void test_round_execution_floats() {
32     TensorFactory<DTYPE> tf;
33 
34     const std::vector<int32_t> sizes = {11};
35 
36     Tensor in = tf.make(
37         sizes,
38         /*data=*/{1.5, -1.5, 0, 1.5, 2.5, 3.5, 4.5, 1.4, -1.4, 1.7, -1.7});
39 
40     // Destination for the round.
41     Tensor out = tf.zeros(sizes);
42 
43     // Run round.
44     op_round_out(in, out);
45 
46     // Check that it matches the expected output.
47     EXPECT_TENSOR_EQ(
48         out,
49         tf.make(
50             sizes,
51             /*data=*/
52             {2.0, -2.0, 0.0, 2.0, 2.0, 4.0, 4.0, 1.0, -1.0, 2.0, -2.0}));
53   }
54 
55   template <ScalarType DTYPE>
test_round_execution_ints()56   void test_round_execution_ints() {
57     TensorFactory<DTYPE> tf;
58 
59     const std::vector<int32_t> sizes = {6};
60 
61     Tensor in = tf.make(sizes, /*data=*/{-1, 2, 0, 3, 0, -5});
62 
63     // Destination for the round.
64     Tensor out = tf.zeros(sizes);
65 
66     // Run round.
67     op_round_out(in, out);
68 
69     // Check that it matches the expected output.
70     EXPECT_TENSOR_EQ(
71         out,
72         tf.make(
73             sizes,
74             /*data=*/
75             {-1, 2, 0, 3, 0, -5}));
76   }
77 };
78 
TEST_F(OpRoundTest,FloatTensors)79 TEST_F(OpRoundTest, FloatTensors) {
80   test_round_execution_floats<ScalarType::Float>();
81 }
82 
TEST_F(OpRoundTest,DoubleTensors)83 TEST_F(OpRoundTest, DoubleTensors) {
84   test_round_execution_floats<ScalarType::Double>();
85 }
86 
TEST_F(OpRoundTest,ByteTensors)87 TEST_F(OpRoundTest, ByteTensors) {
88   TensorFactory<ScalarType::Byte> tf;
89 
90   const std::vector<int32_t> sizes = {6};
91 
92   Tensor in = tf.make(sizes, /*data=*/{1, 2, 0, 3, 0, 5});
93 
94   // Destination for the round.
95   Tensor out = tf.zeros(sizes);
96 
97   // Run round.
98   op_round_out(in, out);
99 
100   // Check that it matches the expected output.
101   EXPECT_TENSOR_EQ(
102       out,
103       tf.make(
104           sizes,
105           /*data=*/
106           {1, 2, 0, 3, 0, 5}));
107 }
108 
TEST_F(OpRoundTest,CharTensors)109 TEST_F(OpRoundTest, CharTensors) {
110   test_round_execution_ints<ScalarType::Char>();
111 }
112 
TEST_F(OpRoundTest,ShortTensors)113 TEST_F(OpRoundTest, ShortTensors) {
114   test_round_execution_ints<ScalarType::Short>();
115 }
116 
TEST_F(OpRoundTest,IntTensors)117 TEST_F(OpRoundTest, IntTensors) {
118   test_round_execution_ints<ScalarType::Int>();
119 }
120 
TEST_F(OpRoundTest,LongTensors)121 TEST_F(OpRoundTest, LongTensors) {
122   test_round_execution_ints<ScalarType::Long>();
123 }
124 
TEST_F(OpRoundTest,InfAndNanPreserved)125 TEST_F(OpRoundTest, InfAndNanPreserved) {
126   TensorFactory<ScalarType::Float> tf;
127 
128   const std::vector<int32_t> sizes = {7};
129 
130   Tensor in = tf.make(
131       sizes,
132       /*data=*/
133       {1.7, 1.4, NAN, std::numeric_limits<float>::infinity(), 1.5, -1.5, 0});
134 
135   // Destination for the round.
136   Tensor out = tf.zeros(sizes);
137 
138   // Run full round.
139   op_round_out(in, out);
140 
141   // Check that it matches the expected output.
142   EXPECT_TENSOR_EQ(
143       out,
144       tf.make(
145           sizes,
146           /*data=*/
147           {2.0,
148            1.0,
149            NAN,
150            std::numeric_limits<float>::infinity(),
151            2.0,
152            -2.0,
153            0.0}));
154 }
155 
TEST_F(OpRoundTest,UnhandledDtypeDies)156 TEST_F(OpRoundTest, UnhandledDtypeDies) {
157   // round() doesn't handle Bool.
158   TensorFactory<ScalarType::Bool> tf;
159 
160   const std::vector<int32_t> sizes = {2, 2};
161 
162   Tensor a = tf.make(sizes, /*data=*/{false, true, false, true});
163 
164   // Destination for the round.
165   Tensor out = tf.zeros(sizes);
166 
167   ET_EXPECT_KERNEL_FAILURE(context_, op_round_out(a, out));
168 }
169 
170 /* %python
171 import torch
172 torch.manual_seed(0)
173 x = torch.rand(3, 2) * 10.0 - 5.0
174 res = torch.round(x)
175 op = "op_round_out"
176 dtype = "ScalarType::Float"
177 check = "EXPECT_TENSOR_EQ" */
178 
TEST_F(OpRoundTest,DynamicShapeUpperBoundSameAsExpected)179 TEST_F(OpRoundTest, DynamicShapeUpperBoundSameAsExpected) {
180   /* %python
181   out_args = "{3, 2}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND"
182   %rewrite(unary_op) */
183 
184   TensorFactory<ScalarType::Float> tf;
185 
186   Tensor x = tf.make(
187       {3, 2},
188       {-0.03743410110473633,
189        2.682218074798584,
190        -4.115225791931152,
191        -3.6796951293945312,
192        -1.925771713256836,
193        1.3407869338989258});
194   Tensor expected = tf.make({3, 2}, {-0.0, 3.0, -4.0, -4.0, -2.0, 1.0});
195 
196   Tensor out =
197       tf.zeros({3, 2}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
198   op_round_out(x, out);
199   EXPECT_TENSOR_EQ(out, expected);
200 }
201 
TEST_F(OpRoundTest,DynamicShapeUpperBoundLargerThanExpected)202 TEST_F(OpRoundTest, DynamicShapeUpperBoundLargerThanExpected) {
203   /* %python
204   out_args = "{10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND"
205   %rewrite(unary_op) */
206 
207   TensorFactory<ScalarType::Float> tf;
208 
209   Tensor x = tf.make(
210       {3, 2},
211       {-0.03743410110473633,
212        2.682218074798584,
213        -4.115225791931152,
214        -3.6796951293945312,
215        -1.925771713256836,
216        1.3407869338989258});
217   Tensor expected = tf.make({3, 2}, {-0.0, 3.0, -4.0, -4.0, -2.0, 1.0});
218 
219   Tensor out =
220       tf.zeros({10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
221   op_round_out(x, out);
222   EXPECT_TENSOR_EQ(out, expected);
223 }
224 
TEST_F(OpRoundTest,DynamicShapeUnbound)225 TEST_F(OpRoundTest, DynamicShapeUnbound) {
226   GTEST_SKIP() << "Dynamic shape unbound not supported";
227   /* %python
228   out_args = "{1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND"
229   %rewrite(unary_op) */
230 
231   TensorFactory<ScalarType::Float> tf;
232 
233   Tensor x = tf.make(
234       {3, 2},
235       {-0.03743410110473633,
236        2.682218074798584,
237        -4.115225791931152,
238        -3.6796951293945312,
239        -1.925771713256836,
240        1.3407869338989258});
241   Tensor expected = tf.make({3, 2}, {-0.0, 3.0, -4.0, -4.0, -2.0, 1.0});
242 
243   Tensor out =
244       tf.zeros({1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
245   op_round_out(x, out);
246   EXPECT_TENSOR_EQ(out, expected);
247 }
248