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/runtime/core/exec_aten/exec_aten.h>
12 #include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h>
13 #include <executorch/runtime/core/exec_aten/testing_util/tensor_util.h>
14
15 #include <gtest/gtest.h>
16
17 using namespace ::testing;
18 using exec_aten::ScalarType;
19 using exec_aten::Tensor;
20 using torch::executor::testing::TensorFactory;
21
22 class OpReluTest : public OperatorTest {
23 protected:
op_relu_out(const Tensor & self,Tensor & out)24 Tensor& op_relu_out(const Tensor& self, Tensor& out) {
25 return torch::executor::aten::relu_outf(context_, self, out);
26 }
27
28 // Common testing for relu on two floating point Tensors.
29 template <ScalarType DTYPE>
test_relu_execution_floats()30 void test_relu_execution_floats() {
31 TensorFactory<DTYPE> tf;
32
33 const std::vector<int32_t> sizes = {3, 2};
34
35 Tensor in = tf.make(
36 sizes, /*data=*/{-0.4775, 0.2948, -0.3984, 1.8690, -0.4048, 0.0});
37
38 // Destination for the relu.
39 Tensor out = tf.zeros(sizes);
40
41 // Run relu.
42 op_relu_out(in, out);
43
44 // Check that it matches the expected output.
45 EXPECT_TENSOR_EQ(
46 out,
47 tf.make(
48 sizes,
49 /*data=*/
50 {0.0, 0.2948, 0.0, 1.8690, 0.0, 0.0}));
51 }
52
53 template <ScalarType DTYPE>
test_relu_execution_ints()54 void test_relu_execution_ints() {
55 TensorFactory<DTYPE> tf;
56
57 const std::vector<int32_t> sizes = {3, 2};
58
59 Tensor in = tf.make(sizes, /*data=*/{-1, 2, 0, 3, 0, -5});
60
61 // Destination for the relu.
62 Tensor out = tf.zeros(sizes);
63
64 // Run relu.
65 op_relu_out(in, out);
66
67 // Check that it matches the expected output.
68 EXPECT_TENSOR_EQ(
69 out,
70 tf.make(
71 sizes,
72 /*data=*/
73 {0, 2, 0, 3, 0, 0}));
74 }
75 };
76
TEST_F(OpReluTest,FloatTensors)77 TEST_F(OpReluTest, FloatTensors) {
78 test_relu_execution_floats<ScalarType::Float>();
79 }
80
TEST_F(OpReluTest,DoubleTensors)81 TEST_F(OpReluTest, DoubleTensors) {
82 test_relu_execution_floats<ScalarType::Double>();
83 }
84
TEST_F(OpReluTest,ByteTensors)85 TEST_F(OpReluTest, ByteTensors) {
86 TensorFactory<ScalarType::Byte> tf;
87
88 const std::vector<int32_t> sizes = {3, 2};
89
90 Tensor in = tf.make(sizes, /*data=*/{1, 2, 0, 3, 0, 5});
91
92 // Destination for the relu.
93 Tensor out = tf.zeros(sizes);
94
95 // Run relu.
96 op_relu_out(in, out);
97
98 // Check that it matches the expected output.
99 EXPECT_TENSOR_EQ(
100 out,
101 tf.make(
102 sizes,
103 /*data=*/
104 {1, 2, 0, 3, 0, 5}));
105 }
106
TEST_F(OpReluTest,CharTensors)107 TEST_F(OpReluTest, CharTensors) {
108 test_relu_execution_ints<ScalarType::Char>();
109 }
110
TEST_F(OpReluTest,ShortTensors)111 TEST_F(OpReluTest, ShortTensors) {
112 test_relu_execution_ints<ScalarType::Short>();
113 }
114
TEST_F(OpReluTest,IntTensors)115 TEST_F(OpReluTest, IntTensors) {
116 test_relu_execution_ints<ScalarType::Int>();
117 }
118
TEST_F(OpReluTest,LongTensors)119 TEST_F(OpReluTest, LongTensors) {
120 test_relu_execution_ints<ScalarType::Long>();
121 }
122
TEST_F(OpReluTest,InfAndNanPreserved)123 TEST_F(OpReluTest, InfAndNanPreserved) {
124 TensorFactory<ScalarType::Float> tf;
125
126 const std::vector<int32_t> sizes = {4, 2};
127
128 Tensor in = tf.make(
129 sizes,
130 /*data=*/
131 {-0.4775,
132 0.2948,
133 -0.3984,
134 NAN,
135 std::numeric_limits<float>::infinity(),
136 -1 * std::numeric_limits<float>::infinity(),
137 0.3,
138 -0.4848});
139
140 // Destination for the relu.
141 Tensor out = tf.zeros(sizes);
142
143 // Run full relu.
144 op_relu_out(in, out);
145
146 // Check that it matches the expected output.
147 EXPECT_TENSOR_EQ(
148 out,
149 tf.make(
150 sizes,
151 /*data=*/
152 {0.0,
153 0.2948,
154 0.0,
155 NAN,
156 std::numeric_limits<float>::infinity(),
157 0.0,
158 0.3,
159 0.0}));
160 }
161
TEST_F(OpReluTest,UnhandledDtypeDies)162 TEST_F(OpReluTest, UnhandledDtypeDies) {
163 // relu() doesn't handle Bool.
164 TensorFactory<ScalarType::Bool> tf;
165
166 const std::vector<int32_t> sizes = {2, 2};
167
168 Tensor a = tf.make(sizes, /*data=*/{false, true, false, true});
169
170 // Destination for the relu.
171 Tensor out = tf.zeros(sizes);
172
173 ET_EXPECT_KERNEL_FAILURE(context_, op_relu_out(a, out));
174 }
175
176 #if !defined(USE_ATEN_LIB)
TEST_F(OpReluTest,UpperBoundOutTensor)177 TEST_F(OpReluTest, UpperBoundOutTensor) {
178 TensorFactory<ScalarType::Float> tf;
179
180 const std::vector<int32_t> sizes = {3, 2};
181
182 Tensor in =
183 tf.make(sizes, /*data=*/{-0.4775, 0.2948, -0.3984, 1.8690, -0.4048, 0.0});
184
185 // Destination for the relu.
186 Tensor out =
187 tf.zeros({5, 7}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
188
189 // Run relu.
190 op_relu_out(in, out);
191
192 // Check that it matches the expected output.
193 EXPECT_TENSOR_EQ(
194 out,
195 tf.make(
196 sizes,
197 /*data=*/
198 {0.0, 0.2948, 0.0, 1.8690, 0.0, 0.0}));
199 }
200 #endif
201
TEST_F(OpReluTest,SimpleGeneratedCase)202 TEST_F(OpReluTest, SimpleGeneratedCase) {
203 TensorFactory<ScalarType::Float> tf;
204
205 Tensor x = tf.make(
206 {10, 10},
207 {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
208 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
209 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
210 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
211 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
212 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
213 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
214 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
215 Tensor expected_result = tf.make(
216 {10, 10},
217 {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
218 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
219 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
220 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
221 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
222 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
223 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
224 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
225
226 Tensor out = tf.zeros({10, 10});
227 Tensor ret = op_relu_out(x, out);
228 EXPECT_TENSOR_CLOSE(out, expected_result);
229 }
230
TEST_F(OpReluTest,DynamicShapeUpperBoundSameAsExpected)231 TEST_F(OpReluTest, DynamicShapeUpperBoundSameAsExpected) {
232 TensorFactory<ScalarType::Float> tf;
233
234 Tensor x = tf.make(
235 {3, 2},
236 {0.676039457321167,
237 0.06196027994155884,
238 0.36154472827911377,
239 0.7953161001205444,
240 0.7633233070373535,
241 0.5809110999107361});
242 Tensor expected_result = tf.make(
243 {3, 2},
244 {0.676039457321167,
245 0.06196027994155884,
246 0.36154472827911377,
247 0.7953161001205444,
248 0.7633233070373535,
249 0.5809110999107361});
250
251 Tensor out =
252 tf.zeros({3, 2}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
253 Tensor ret = op_relu_out(x, out);
254 EXPECT_TENSOR_CLOSE(out, expected_result);
255 }
256
TEST_F(OpReluTest,DynamicShapeUpperBoundLargerThanExpected)257 TEST_F(OpReluTest, DynamicShapeUpperBoundLargerThanExpected) {
258 TensorFactory<ScalarType::Float> tf;
259
260 Tensor x = tf.make(
261 {3, 2},
262 {0.676039457321167,
263 0.06196027994155884,
264 0.36154472827911377,
265 0.7953161001205444,
266 0.7633233070373535,
267 0.5809110999107361});
268 Tensor expected_result = tf.make(
269 {3, 2},
270 {0.676039457321167,
271 0.06196027994155884,
272 0.36154472827911377,
273 0.7953161001205444,
274 0.7633233070373535,
275 0.5809110999107361});
276
277 Tensor out =
278 tf.zeros({10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
279 Tensor ret = op_relu_out(x, out);
280 EXPECT_TENSOR_CLOSE(out, expected_result);
281 }
282
TEST_F(OpReluTest,DynamicShapeUnbound)283 TEST_F(OpReluTest, DynamicShapeUnbound) {
284 GTEST_SKIP() << "Unbound dynamic shape not supported";
285 TensorFactory<ScalarType::Float> tf;
286
287 Tensor x = tf.make(
288 {3, 2},
289 {0.676039457321167,
290 0.06196027994155884,
291 0.36154472827911377,
292 0.7953161001205444,
293 0.7633233070373535,
294 0.5809110999107361});
295 Tensor expected_result = tf.make(
296 {3, 2},
297 {0.676039457321167,
298 0.06196027994155884,
299 0.36154472827911377,
300 0.7953161001205444,
301 0.7633233070373535,
302 0.5809110999107361});
303
304 Tensor out =
305 tf.zeros({1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
306 Tensor ret = op_relu_out(x, out);
307 EXPECT_TENSOR_CLOSE(out, expected_result);
308 }
309