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 #include <cmath>
18
19 using namespace ::testing;
20 using exec_aten::Scalar;
21 using exec_aten::ScalarType;
22 using exec_aten::Tensor;
23 using torch::executor::testing::TensorFactory;
24
25 namespace {
26
27 class OpDivOutTest : public OperatorTest {
28 protected:
op_div_out(const Tensor & a,const Tensor & b,Tensor & out)29 Tensor& op_div_out(const Tensor& a, const Tensor& b, Tensor& out) {
30 return torch::executor::aten::div_outf(context_, a, b, out);
31 }
32
33 template <ScalarType DTYPE_A, ScalarType DTYPE_B, ScalarType DTYPE_OUT>
test_div()34 void test_div() {
35 TensorFactory<DTYPE_A> tf_a;
36 TensorFactory<DTYPE_B> tf_b;
37 TensorFactory<DTYPE_OUT> tf_out;
38
39 const std::vector<int32_t> sizes = {2, 2};
40
41 Tensor out = tf_out.zeros(sizes);
42
43 // Valid input should give the expected output
44 op_div_out(
45 tf_a.make(sizes, /*data=*/{1, 2, 4, 8}),
46 tf_b.make(sizes, /*data=*/{8, 4, 2, 1}),
47 out);
48
49 EXPECT_TENSOR_CLOSE(out, tf_out.make(sizes, /*data=*/{0.125, 0.5, 2, 8}));
50 }
51
52 template <ScalarType DTYPE_A, ScalarType DTYPE_B>
test_div_enumerate_out_types()53 void test_div_enumerate_out_types() {
54 #define ENUMERATE_TEST_ENTRY(ctype, dtype) \
55 test_div<DTYPE_A, DTYPE_B, ScalarType::dtype>();
56
57 ET_FORALL_FLOAT_TYPES(ENUMERATE_TEST_ENTRY)
58
59 #undef ENUMERATE_TEST_ENTRY
60 }
61
62 template <ScalarType DTYPE_A>
test_div_enumerate_b_types()63 void test_div_enumerate_b_types() {
64 #define ENUMERATE_TEST_ENTRY(ctype, dtype) \
65 test_div_enumerate_out_types<DTYPE_A, ScalarType::dtype>();
66
67 ET_FORALL_REAL_TYPES(ENUMERATE_TEST_ENTRY)
68
69 #undef ENUMERATE_TEST_ENTRY
70 }
71
72 template <ScalarType OUTPUT_DTYPE>
test_div_invalid_output_dtype_dies()73 void test_div_invalid_output_dtype_dies() {
74 TensorFactory<ScalarType::Float> tf_float;
75 TensorFactory<OUTPUT_DTYPE> tf_out;
76
77 const std::vector<int32_t> sizes = {2, 5};
78
79 Tensor a = tf_float.ones(sizes);
80 Tensor b = tf_float.ones(sizes);
81 Tensor out = tf_out.zeros(sizes);
82
83 ET_EXPECT_KERNEL_FAILURE(context_, op_div_out(a, b, out));
84 }
85
86 /**
87 * Common testing for div operator, for float output types
88 */
89 void test_div_enumerate_a_types();
90 };
91
92 template <>
93 void OpDivOutTest::
test_div()94 test_div<ScalarType::Float, ScalarType::Float, ScalarType::Float>() {
95 TensorFactory<ScalarType::Float> tf;
96
97 const std::vector<int32_t> sizes = {2, 5};
98
99 // Invalid divisor input zero should die
100 Tensor out = tf.zeros(sizes);
101
102 // Valid input should give the expected output
103 op_div_out(
104 tf.make(sizes, /*data=*/{1, 2, 4, 8, INFINITY, -INFINITY, NAN, 1, 1, 1}),
105 tf.make(
106 sizes,
107 /*data=*/
108 {8, 0, 2, 1, INFINITY, -INFINITY, NAN, INFINITY, -INFINITY, NAN}),
109 out);
110 EXPECT_TENSOR_CLOSE(
111 out,
112 tf.make(
113 sizes, /*data=*/{0.125, INFINITY, 2, 8, NAN, NAN, NAN, 0, 0, NAN}));
114 }
115
116 template <>
117 void OpDivOutTest::
test_div()118 test_div<ScalarType::Bool, ScalarType::Float, ScalarType::Float>() {
119 TensorFactory<ScalarType::Bool> tf_b;
120 TensorFactory<ScalarType::Float> tf;
121
122 const std::vector<int32_t> sizes = {2, 2};
123
124 // Invalid divisor input zero should die
125 Tensor out = tf.zeros(sizes);
126
127 // Valid input should give the expected output
128 op_div_out(
129 tf_b.make(sizes, /*data=*/{1, 1, 1, 1}),
130 tf.make(sizes, /*data=*/{4, 4, 2, 1}),
131 out);
132
133 EXPECT_TENSOR_CLOSE(out, tf.make(sizes, /*data=*/{0.25, 0.25, 0.5, 1.0}));
134 }
135
test_div_enumerate_a_types()136 void OpDivOutTest::test_div_enumerate_a_types() {
137 #define ENUMERATE_TEST_ENTRY(ctype, dtype) \
138 test_div_enumerate_b_types<ScalarType::dtype>();
139
140 ET_FORALL_REAL_TYPES(ENUMERATE_TEST_ENTRY)
141
142 test_div<ScalarType::Bool, ScalarType::Float, ScalarType::Float>();
143
144 #undef ENUMERATE_TEST_ENTRY
145 }
146
147 class OpDivScalarOutTest : public OperatorTest {
148 protected:
op_div_scalar_out(const Tensor & a,const Scalar & b,Tensor & out)149 Tensor& op_div_scalar_out(const Tensor& a, const Scalar& b, Tensor& out) {
150 return torch::executor::aten::div_outf(context_, a, b, out);
151 }
152 };
153
154 }; // namespace
155
156 //
157 // Correctness Tests
158 //
159
160 /**
161 * Uses the function templates above to test all valid combinations of inputs
162 * and output dtypes
163 */
TEST_F(OpDivOutTest,AllRealDtypesSupported)164 TEST_F(OpDivOutTest, AllRealDtypesSupported) {
165 test_div_enumerate_a_types();
166 }
167
TEST_F(OpDivOutTest,BroadcastSupported1)168 TEST_F(OpDivOutTest, BroadcastSupported1) {
169 TensorFactory<ScalarType::Float> tf;
170
171 Tensor a = tf.make({2, 1, 2, 1}, {4, 8, 12, 16});
172 Tensor b = tf.make({2, 1, 4}, {1, 1, 1, 1, 2, 2, 2, 2});
173
174 // Destination for the broadcasting div. Follow the broadcasting rules in
175 // https://fburl.com/n9wl4d0o
176 Tensor out = tf.zeros({2, 2, 2, 4});
177
178 op_div_out(a, b, out);
179
180 Tensor ret = tf.make(
181 {2, 2, 2, 4}, {4, 4, 4, 4, 8, 8, 8, 8, 2, 2, 2, 2, 4, 4, 4, 4,
182 12, 12, 12, 12, 16, 16, 16, 16, 6, 6, 6, 6, 8, 8, 8, 8});
183 EXPECT_TENSOR_EQ(out, ret);
184 }
185
TEST_F(OpDivOutTest,BroadcastSupported2)186 TEST_F(OpDivOutTest, BroadcastSupported2) {
187 TensorFactory<ScalarType::Float> tf;
188
189 Tensor a = tf.make({3, 2, 1}, {2, 3, 4, 5, 6, 7});
190 Tensor b = tf.make({1, 2, 1}, {2, 2});
191
192 // Destination for the broadcasting div. Follow the broadcasting rules in
193 // https://fburl.com/n9wl4d0o
194 Tensor out = tf.zeros({3, 2, 1});
195
196 op_div_out(a, b, out);
197
198 Tensor ret = tf.make({3, 2, 1}, {1, 1.5, 2, 2.5, 3, 3.5});
199 EXPECT_TENSOR_EQ(out, ret);
200 }
201
TEST_F(OpDivOutTest,BroadcastScalarSupported1)202 TEST_F(OpDivOutTest, BroadcastScalarSupported1) {
203 TensorFactory<ScalarType::Float> tf;
204
205 Tensor a = tf.make({2, 1, 3}, {2, 3, 4, 5, 6, 7});
206 Tensor b = tf.make({1}, {2});
207
208 // Destination for the broadcasting div. Follow the broadcasting rules in
209 // https://fburl.com/n9wl4d0o
210 Tensor out = tf.zeros({2, 1, 3});
211
212 op_div_out(a, b, out);
213
214 Tensor ret = tf.make({2, 1, 3}, {1, 1.5, 2, 2.5, 3, 3.5});
215 EXPECT_TENSOR_EQ(out, ret);
216 }
217
TEST_F(OpDivOutTest,BroadcastScalarSupported2)218 TEST_F(OpDivOutTest, BroadcastScalarSupported2) {
219 TensorFactory<ScalarType::Float> tf;
220
221 Tensor a = tf.make({1, 1, 1}, {8});
222 Tensor b = tf.make({3, 1, 1}, {2, 4, 8});
223
224 // Destination for the broadcasting div. Follow the broadcasting rules in
225 // https://fburl.com/n9wl4d0o
226 Tensor out = tf.zeros({3, 1, 1});
227
228 op_div_out(a, b, out);
229
230 Tensor ret = tf.make({3, 1, 1}, {4, 2, 1});
231 EXPECT_TENSOR_EQ(out, ret);
232
233 std::swap(a, b);
234 out = tf.zeros({3, 1, 1});
235 op_div_out(a, b, out);
236 ret = tf.make({3, 1, 1}, {0.25, 0.5, 1});
237 EXPECT_TENSOR_EQ(out, ret);
238 }
239
TEST_F(OpDivOutTest,BroadcastScalarRank0Supported)240 TEST_F(OpDivOutTest, BroadcastScalarRank0Supported) {
241 TensorFactory<ScalarType::Float> tf;
242
243 Tensor a = tf.make({1}, {8});
244 Tensor b = tf.make({}, {2});
245
246 Tensor out = tf.zeros({1});
247
248 op_div_out(a, b, out);
249
250 Tensor ret = tf.make({1}, {4});
251 EXPECT_TENSOR_EQ(out, ret);
252
253 op_div_out(b, a, out);
254
255 ret = tf.make({1}, {0.25});
256 EXPECT_TENSOR_EQ(out, ret);
257 }
258
TEST_F(OpDivOutTest,BroadcastDimSizeIsOneAB)259 TEST_F(OpDivOutTest, BroadcastDimSizeIsOneAB) {
260 TensorFactory<ScalarType::Float> tf;
261
262 Tensor x = tf.make(
263 {3, 2},
264 {0.9403896331787109,
265 0.33918434381484985,
266 0.6973152756690979,
267 0.7128887176513672,
268 0.9746139049530029,
269 0.3507251739501953});
270 Tensor y = tf.make({1, 2}, {0.942541241645813, 0.0298004150390625});
271 Tensor expected_result = tf.make(
272 {3, 2},
273 {0.9977172017097473,
274 11.381866455078125,
275 0.7398247122764587,
276 23.922107696533203,
277 1.0340278148651123,
278 11.769137382507324});
279
280 Tensor out = tf.zeros({3, 2});
281 Tensor ret = op_div_out(x, y, out);
282 EXPECT_TENSOR_CLOSE(out, expected_result);
283 }
284
TEST_F(OpDivOutTest,BroadcastDimSizeMissingAB)285 TEST_F(OpDivOutTest, BroadcastDimSizeMissingAB) {
286 TensorFactory<ScalarType::Float> tf;
287
288 Tensor x = tf.make(
289 {3, 2},
290 {0.9403896331787109,
291 0.33918434381484985,
292 0.6973152756690979,
293 0.7128887176513672,
294 0.9746139049530029,
295 0.3507251739501953});
296 Tensor y = tf.make({2}, {0.942541241645813, 0.0298004150390625});
297 Tensor expected_result = tf.make(
298 {3, 2},
299 {0.9977172017097473,
300 11.381866455078125,
301 0.7398247122764587,
302 23.922107696533203,
303 1.0340278148651123,
304 11.769137382507324});
305
306 Tensor out = tf.zeros({3, 2});
307 Tensor ret = op_div_out(x, y, out);
308 EXPECT_TENSOR_CLOSE(out, expected_result);
309 }
310
TEST_F(OpDivOutTest,BroadcastDimSizeIsOneBA)311 TEST_F(OpDivOutTest, BroadcastDimSizeIsOneBA) {
312 TensorFactory<ScalarType::Float> tf;
313
314 Tensor x = tf.make({1, 2}, {0.942541241645813, 0.0298004150390625});
315 Tensor y = tf.make(
316 {3, 2},
317 {0.9403896331787109,
318 0.33918434381484985,
319 0.6973152756690979,
320 0.7128887176513672,
321 0.9746139049530029,
322 0.3507251739501953});
323 Tensor expected_result = tf.make(
324 {3, 2},
325 {1.0022879838943481,
326 0.08785904943943024,
327 1.351671576499939,
328 0.041802339255809784,
329 0.9670919179916382,
330 0.08496799319982529});
331
332 Tensor out = tf.zeros({3, 2});
333 Tensor ret = op_div_out(x, y, out);
334 EXPECT_TENSOR_CLOSE(out, expected_result);
335 }
336
TEST_F(OpDivOutTest,BroadcastDimSizeMissingBA)337 TEST_F(OpDivOutTest, BroadcastDimSizeMissingBA) {
338 TensorFactory<ScalarType::Float> tf;
339
340 Tensor x = tf.make({1, 2}, {0.942541241645813, 0.0298004150390625});
341 Tensor y = tf.make(
342 {3, 2},
343 {0.9403896331787109,
344 0.33918434381484985,
345 0.6973152756690979,
346 0.7128887176513672,
347 0.9746139049530029,
348 0.3507251739501953});
349 Tensor expected_result = tf.make(
350 {3, 2},
351 {1.0022879838943481,
352 0.08785904943943024,
353 1.351671576499939,
354 0.041802339255809784,
355 0.9670919179916382,
356 0.08496799319982529});
357
358 Tensor out = tf.zeros({3, 2});
359 Tensor ret = op_div_out(x, y, out);
360 EXPECT_TENSOR_CLOSE(out, expected_result);
361 }
362
363 //
364 // Death Tests
365 //
366
TEST_F(OpDivOutTest,MismatchedShapesDies)367 TEST_F(OpDivOutTest, MismatchedShapesDies) {
368 if (torch::executor::testing::SupportedFeatures::get()->is_aten) {
369 GTEST_SKIP() << "ATen kernel can handle mismatched shapes";
370 }
371 TensorFactory<ScalarType::Int> tf_int;
372 TensorFactory<ScalarType::Float> tf_float;
373
374 Tensor a = tf_int.ones(/*sizes=*/{2});
375 Tensor b = tf_int.ones(/*sizes=*/{4});
376 Tensor out = tf_float.ones(/*sizes=*/{2, 2});
377
378 ET_EXPECT_KERNEL_FAILURE(context_, op_div_out(a, b, out));
379 }
380
TEST_F(OpDivOutTest,AllNonFloatOutputDTypeDies)381 TEST_F(OpDivOutTest, AllNonFloatOutputDTypeDies) {
382 #define TEST_ENTRY(ctype, dtype) \
383 test_div_invalid_output_dtype_dies<ScalarType::dtype>();
384 ET_FORALL_INT_TYPES(TEST_ENTRY);
385 #undef TEST_ENTRY
386 }
387
388 //
389 // Dynamic Shape Tests
390 //
391
TEST_F(OpDivOutTest,DynamicShapeUpperBoundSameAsExpected)392 TEST_F(OpDivOutTest, DynamicShapeUpperBoundSameAsExpected) {
393 TensorFactory<ScalarType::Float> tf;
394
395 Tensor x = tf.make(
396 {3, 2},
397 {0.9321315288543701,
398 0.013347446918487549,
399 0.42016714811325073,
400 0.059867143630981445,
401 0.951939046382904,
402 0.8632845878601074});
403 Tensor y = tf.make(
404 {3, 2},
405 {0.714946985244751,
406 0.39985191822052,
407 0.9640239477157593,
408 0.06885606050491333,
409 0.008897960186004639,
410 0.468650221824646});
411 Tensor expected_result = tf.make(
412 {3, 2},
413 {1.3037770986557007,
414 0.03338097408413887,
415 0.4358472228050232,
416 0.869453489780426,
417 106.98396301269531,
418 1.8420659303665161});
419
420 Tensor out =
421 tf.zeros({3, 2}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
422 Tensor ret = op_div_out(x, y, out);
423 EXPECT_TENSOR_CLOSE(out, expected_result);
424 }
425
TEST_F(OpDivOutTest,DynamicShapeUpperBoundLargerThanExpected)426 TEST_F(OpDivOutTest, DynamicShapeUpperBoundLargerThanExpected) {
427 TensorFactory<ScalarType::Float> tf;
428
429 Tensor x = tf.make(
430 {3, 2},
431 {0.9321315288543701,
432 0.013347446918487549,
433 0.42016714811325073,
434 0.059867143630981445,
435 0.951939046382904,
436 0.8632845878601074});
437 Tensor y = tf.make(
438 {3, 2},
439 {0.714946985244751,
440 0.39985191822052,
441 0.9640239477157593,
442 0.06885606050491333,
443 0.008897960186004639,
444 0.468650221824646});
445 Tensor expected_result = tf.make(
446 {3, 2},
447 {1.3037770986557007,
448 0.03338097408413887,
449 0.4358472228050232,
450 0.869453489780426,
451 106.98396301269531,
452 1.8420659303665161});
453
454 Tensor out =
455 tf.zeros({10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
456 Tensor ret = op_div_out(x, y, out);
457 EXPECT_TENSOR_CLOSE(out, expected_result);
458 }
459
TEST_F(OpDivOutTest,DynamicShapeUnbound)460 TEST_F(OpDivOutTest, DynamicShapeUnbound) {
461 GTEST_SKIP() << "Dynamic shape not supported";
462 TensorFactory<ScalarType::Float> tf;
463
464 Tensor x = tf.make(
465 {3, 2},
466 {0.9321315288543701,
467 0.013347446918487549,
468 0.42016714811325073,
469 0.059867143630981445,
470 0.951939046382904,
471 0.8632845878601074});
472 Tensor y = tf.make(
473 {3, 2},
474 {0.714946985244751,
475 0.39985191822052,
476 0.9640239477157593,
477 0.06885606050491333,
478 0.008897960186004639,
479 0.468650221824646});
480 Tensor expected_result = tf.make(
481 {3, 2},
482 {1.3037770986557007,
483 0.03338097408413887,
484 0.4358472228050232,
485 0.869453489780426,
486 106.98396301269531,
487 1.8420659303665161});
488
489 Tensor out =
490 tf.zeros({1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
491 Tensor ret = op_div_out(x, y, out);
492 EXPECT_TENSOR_CLOSE(out, expected_result);
493 }
494
TEST_F(OpDivScalarOutTest,SanityCheckIntScalar)495 TEST_F(OpDivScalarOutTest, SanityCheckIntScalar) {
496 TensorFactory<ScalarType::Int> tf_a;
497 TensorFactory<ScalarType::Float> tf_out;
498
499 const std::vector<int32_t> sizes = {2, 2};
500
501 Tensor out = tf_out.zeros(sizes);
502
503 op_div_scalar_out(tf_a.make(sizes, {1, 2, 4, -9}), 2, out);
504
505 // Check that it matches the expected output.
506 EXPECT_TENSOR_EQ(out, tf_out.make(sizes, {0.5, 1.0, 2.0, -4.5}));
507 }
508
TEST_F(OpDivScalarOutTest,SanityCheckFloatScalar)509 TEST_F(OpDivScalarOutTest, SanityCheckFloatScalar) {
510 TensorFactory<ScalarType::Int> tf_a;
511 TensorFactory<ScalarType::Float> tf_out;
512
513 const std::vector<int32_t> sizes = {2, 2};
514
515 Tensor out = tf_out.zeros(sizes);
516
517 op_div_scalar_out(tf_a.make(sizes, {1, 2, 4, -9}), 2.0, out);
518
519 // Check that it matches the expected output.
520 EXPECT_TENSOR_EQ(out, tf_out.make(sizes, {0.5, 1.0, 2.0, -4.5}));
521 }
522
TEST_F(OpDivScalarOutTest,OptimizedSanityCheck)523 TEST_F(OpDivScalarOutTest, OptimizedSanityCheck) {
524 TensorFactory<ScalarType::Float> tf;
525
526 const std::vector<int32_t> sizes = {2, 2};
527
528 Tensor out = tf.zeros(sizes);
529
530 op_div_scalar_out(tf.make(sizes, {1.3, 2.1, 4.6, 8.2}), 2.0, out);
531
532 // Check that it matches the expected output.
533 EXPECT_TENSOR_CLOSE(out, tf.make(sizes, {0.65, 1.05, 2.3, 4.1}));
534 }
535