xref: /aosp_15_r20/external/executorch/kernels/test/op_div_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 #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