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 #include <iostream>
19
20 using namespace ::testing;
21 using executorch::aten::Scalar;
22 using executorch::aten::ScalarType;
23 using executorch::aten::Tensor;
24 using executorch::runtime::testing::TensorFactory;
25 using torch::executor::testing::SupportedFeatures;
26 namespace etrt = executorch::runtime;
27
28 class OpAddOutKernelTest : public OperatorTest {
29 protected:
op_add_out(const Tensor & self,const Tensor & other,const Scalar & alpha,Tensor & out)30 Tensor& op_add_out(
31 const Tensor& self,
32 const Tensor& other,
33 const Scalar& alpha,
34 Tensor& out) {
35 return torch::executor::aten::add_outf(context_, self, other, alpha, out);
36 }
37
38 template <ScalarType DTYPE_A, ScalarType DTYPE_B, ScalarType DTYPE_OUT>
test_add()39 void test_add() {
40 TensorFactory<DTYPE_A> tf_a;
41 TensorFactory<DTYPE_B> tf_b;
42 TensorFactory<DTYPE_OUT> tf_out;
43
44 const std::vector<int32_t> sizes = {2, 2};
45
46 // Destination for the sum.
47 Tensor out = tf_out.zeros(sizes);
48
49 // Add two tensors.
50 op_add_out(
51 tf_a.make(sizes, /*data=*/{1, 2, 4, 8}),
52 tf_b.ones(sizes),
53 /*alpha=*/1,
54 out);
55
56 // Check that it matches the expected output.
57 EXPECT_TENSOR_EQ(out, tf_out.make(sizes, /*data=*/{2, 3, 5, 9}));
58 }
59
60 template <ScalarType DTYPE_A, ScalarType DTYPE_B>
test_add_enumerate_out_types()61 void test_add_enumerate_out_types() {
62 test_add<DTYPE_A, DTYPE_B, ScalarType::BFloat16>();
63 test_add<DTYPE_A, DTYPE_B, ScalarType::Half>();
64 test_add<DTYPE_A, DTYPE_B, ScalarType::Float>();
65 test_add<DTYPE_A, DTYPE_B, ScalarType::Double>();
66 // Integral out type is only allowed if both inputs are integral types
67 if (etrt::isIntegralType(DTYPE_A, false) &&
68 etrt::isIntegralType(DTYPE_B, false)) {
69 test_add<DTYPE_A, DTYPE_B, ScalarType::Int>();
70 test_add<DTYPE_A, DTYPE_B, ScalarType::Long>();
71 }
72 }
73
74 template <ScalarType DTYPE_A>
test_add_enumerate_b_types()75 void test_add_enumerate_b_types() {
76 #define ENUMERATE_TEST_ENTRY(ctype, dtype) \
77 test_add_enumerate_out_types<DTYPE_A, ScalarType::dtype>();
78
79 ET_FORALL_REALHBF16_TYPES(ENUMERATE_TEST_ENTRY)
80
81 #undef ENUMERATE_TEST_ENTRY
82 }
83
test_add_enumerate_a_types()84 void test_add_enumerate_a_types() {
85 #define ENUMERATE_TEST_ENTRY(ctype, dtype) \
86 test_add_enumerate_b_types<ScalarType::dtype>();
87
88 ET_FORALL_REALHBF16_TYPES(ENUMERATE_TEST_ENTRY)
89
90 #undef ENUMERATE_TEST_ENTRY
91 }
92
93 // Common testing for adding two floating point Tensors.
94 template <ScalarType DTYPE>
test_floating_point_add_out()95 void test_floating_point_add_out() {
96 TensorFactory<DTYPE> tf;
97
98 const std::vector<int32_t> sizes = {2, 2};
99
100 // Destination for the sum.
101 Tensor out = tf.zeros(sizes);
102
103 // Add two tensors.
104 op_add_out(
105 tf.make(sizes, /*data=*/{1.25, 2.25, 4.5, 8.875}),
106 tf.ones(sizes),
107 /*alpha=*/1.25,
108 out);
109
110 // Check that it matches the expected output. Values selected to
111 // be exactly representable to avoid throwing off half/bfloat16
112 // tests.
113 EXPECT_TENSOR_CLOSE(out, tf.make(sizes, /*data=*/{2.5, 3.5, 5.75, 10.125}));
114 }
115 };
116
117 class OpAddScalarOutKernelTest : public OperatorTest {
118 protected:
op_add_scalar_out(const Tensor & self,const Scalar & other,const Scalar & alpha,Tensor & out)119 Tensor& op_add_scalar_out(
120 const Tensor& self,
121 const Scalar& other,
122 const Scalar& alpha,
123 Tensor& out) {
124 return torch::executor::aten::add_outf(context_, self, other, alpha, out);
125 }
126 };
127
128 /**
129 * Uses the function templates above to test all valid combinations of inputs
130 * and output dtypes
131 */
TEST_F(OpAddOutKernelTest,AllRealDtypesSupported)132 TEST_F(OpAddOutKernelTest, AllRealDtypesSupported) {
133 test_add_enumerate_a_types();
134 }
135
TEST_F(OpAddOutKernelTest,FloatTensors)136 TEST_F(OpAddOutKernelTest, FloatTensors) {
137 test_floating_point_add_out<ScalarType::Float>();
138 }
139
TEST_F(OpAddOutKernelTest,DoubleTensors)140 TEST_F(OpAddOutKernelTest, DoubleTensors) {
141 test_floating_point_add_out<ScalarType::Double>();
142 }
143
TEST_F(OpAddOutKernelTest,HalfTensors)144 TEST_F(OpAddOutKernelTest, HalfTensors) {
145 test_floating_point_add_out<ScalarType::Half>();
146 }
147
TEST_F(OpAddOutKernelTest,BFloat16Tensors)148 TEST_F(OpAddOutKernelTest, BFloat16Tensors) {
149 test_floating_point_add_out<ScalarType::BFloat16>();
150 }
151
TEST_F(OpAddOutKernelTest,BoolAndIntInputTensor)152 TEST_F(OpAddOutKernelTest, BoolAndIntInputTensor) {
153 TensorFactory<ScalarType::Bool> tf;
154 TensorFactory<ScalarType::Int> tfi;
155
156 const std::vector<int32_t> sizes = {2, 2};
157
158 Tensor a = tf.make(sizes, /*data=*/{false, true, false, true});
159 Tensor b = tfi.make(sizes, /*data=*/{2, 4, 3, 3});
160
161 Tensor out = tfi.zeros(sizes);
162
163 op_add_out(a, b, /*alpha=*/1, out);
164 EXPECT_TENSOR_EQ(out, tfi.make(sizes, {2, 5, 3, 4}));
165 }
166
TEST_F(OpAddOutKernelTest,BoolAndBoolInputTensor)167 TEST_F(OpAddOutKernelTest, BoolAndBoolInputTensor) {
168 et_pal_init();
169 TensorFactory<ScalarType::Bool> tf;
170
171 const std::vector<int32_t> sizes = {2, 2};
172
173 Tensor a = tf.make(sizes, /*data=*/{false, true, false, true});
174 Tensor b = tf.make(sizes, /*data=*/{false, true, true, true});
175
176 Tensor out = tf.zeros(sizes);
177
178 op_add_out(a, b, /*alpha=*/1, out);
179 EXPECT_TENSOR_EQ(out, tf.make(sizes, {false, true, true, true}));
180 }
181
TEST_F(OpAddOutKernelTest,BroadcastDimSizeIsOneAB)182 TEST_F(OpAddOutKernelTest, BroadcastDimSizeIsOneAB) {
183 TensorFactory<ScalarType::Float> tf;
184
185 Tensor x = tf.make(
186 {3, 2},
187 {0.5721208453178406,
188 0.9629082083702087,
189 0.19517338275909424,
190 0.4107270836830139,
191 0.945562481880188,
192 0.8788509368896484});
193 Tensor y = tf.make({1, 2}, {0.7453382015228271, 0.3131374716758728});
194 Tensor expected_result = tf.make(
195 {3, 2},
196 {1.3174591064453125,
197 1.2760456800460815,
198 0.9405115842819214,
199 0.7238645553588867,
200 1.6909006834030151,
201 1.191988468170166});
202
203 Tensor out = tf.zeros({3, 2});
204 Tensor ret = op_add_out(x, y, 1, out);
205 EXPECT_TENSOR_CLOSE(out, expected_result);
206 }
207
TEST_F(OpAddOutKernelTest,BroadcastDimSizeMissingAB)208 TEST_F(OpAddOutKernelTest, BroadcastDimSizeMissingAB) {
209 TensorFactory<ScalarType::Float> tf;
210
211 Tensor x = tf.make(
212 {3, 2},
213 {0.5721208453178406,
214 0.9629082083702087,
215 0.19517338275909424,
216 0.4107270836830139,
217 0.945562481880188,
218 0.8788509368896484});
219 Tensor y = tf.make({2}, {0.7453382015228271, 0.3131374716758728});
220 Tensor expected_result = tf.make(
221 {3, 2},
222 {1.3174591064453125,
223 1.2760456800460815,
224 0.9405115842819214,
225 0.7238645553588867,
226 1.6909006834030151,
227 1.191988468170166});
228
229 Tensor out = tf.zeros({3, 2});
230 Tensor ret = op_add_out(x, y, 1, out);
231 EXPECT_TENSOR_CLOSE(out, expected_result);
232 }
233
TEST_F(OpAddOutKernelTest,BroadcastDimSizeIsOneBA)234 TEST_F(OpAddOutKernelTest, BroadcastDimSizeIsOneBA) {
235 TensorFactory<ScalarType::Float> tf;
236
237 Tensor x = tf.make({1, 2}, {0.7453382015228271, 0.3131374716758728});
238 Tensor y = tf.make(
239 {3, 2},
240 {0.5721208453178406,
241 0.9629082083702087,
242 0.19517338275909424,
243 0.4107270836830139,
244 0.945562481880188,
245 0.8788509368896484});
246 Tensor expected_result = tf.make(
247 {3, 2},
248 {1.3174591064453125,
249 1.2760456800460815,
250 0.9405115842819214,
251 0.7238645553588867,
252 1.6909006834030151,
253 1.191988468170166});
254
255 Tensor out = tf.zeros({3, 2});
256 Tensor ret = op_add_out(x, y, 1, out);
257 EXPECT_TENSOR_CLOSE(out, expected_result);
258 }
259
TEST_F(OpAddOutKernelTest,BroadcastDimSizeMissingBA)260 TEST_F(OpAddOutKernelTest, BroadcastDimSizeMissingBA) {
261 TensorFactory<ScalarType::Float> tf;
262
263 Tensor x = tf.make({1, 2}, {0.7453382015228271, 0.3131374716758728});
264 Tensor y = tf.make(
265 {3, 2},
266 {0.5721208453178406,
267 0.9629082083702087,
268 0.19517338275909424,
269 0.4107270836830139,
270 0.945562481880188,
271 0.8788509368896484});
272 Tensor expected_result = tf.make(
273 {3, 2},
274 {1.3174591064453125,
275 1.2760456800460815,
276 0.9405115842819214,
277 0.7238645553588867,
278 1.6909006834030151,
279 1.191988468170166});
280
281 Tensor out = tf.zeros({3, 2});
282 Tensor ret = op_add_out(x, y, 1, out);
283 EXPECT_TENSOR_CLOSE(out, expected_result);
284 }
285
TEST_F(OpAddOutKernelTest,BroadcastSupported)286 TEST_F(OpAddOutKernelTest, BroadcastSupported) {
287 TensorFactory<ScalarType::Float> tf;
288
289 const std::vector<int32_t> sizes = {2, 2};
290
291 Tensor a = tf.zeros({5, 1, 3, 1});
292 Tensor b = tf.ones({2, 1, 4});
293
294 // Destination for the broadcasting sum. Follow the broadcasting rules in
295 // https://fburl.com/n9wl4d0o
296 Tensor out = tf.zeros({5, 2, 3, 4});
297
298 Tensor ret = op_add_out(a, b, 1, out);
299
300 EXPECT_TENSOR_EQ(out, ret);
301 EXPECT_TENSOR_EQ(out, tf.ones({5, 2, 3, 4}));
302 }
303
TEST_F(OpAddOutKernelTest,BroadcastOneElementTensor)304 TEST_F(OpAddOutKernelTest, BroadcastOneElementTensor) {
305 TensorFactory<ScalarType::Float> tf;
306 Tensor x = tf.make({1}, {1.75});
307 Tensor y = tf.make({3, 2}, {-1.5, -1, -0.5, 0, 0.5, 1.5});
308
309 Tensor out = tf.zeros({3, 2});
310
311 Tensor ret = op_add_out(x, y, 1, out);
312
313 Tensor expected = tf.make(
314 {3, 2},
315 {
316 0.25,
317 0.75,
318 1.25,
319 1.75,
320 2.25,
321 3.25,
322 });
323
324 EXPECT_TENSOR_EQ(out, expected);
325
326 out = op_add_out(y, x, 1, out);
327 EXPECT_TENSOR_EQ(out, expected);
328 }
329
TEST_F(OpAddOutKernelTest,BroadcastOneElementTensorTypePromotion)330 TEST_F(OpAddOutKernelTest, BroadcastOneElementTensorTypePromotion) {
331 TensorFactory<ScalarType::Float> tf;
332 TensorFactory<ScalarType::Double> tfDouble;
333 Tensor x = tfDouble.make({1}, {1.75});
334 Tensor y = tf.make({3, 2}, {-1.5, -1, -0.5, 0, 0.5, 1.5});
335
336 Tensor out = tfDouble.zeros({3, 2});
337
338 Tensor ret = op_add_out(x, y, 1, out);
339
340 Tensor expected = tfDouble.make(
341 {3, 2},
342 {
343 0.25,
344 0.75,
345 1.25,
346 1.75,
347 2.25,
348 3.25,
349 });
350
351 EXPECT_TENSOR_EQ(out, expected);
352
353 out = op_add_out(y, x, 1, out);
354 EXPECT_TENSOR_EQ(out, expected);
355 }
356
TEST_F(OpAddOutKernelTest,BroadcastOneElementRank0Tensor)357 TEST_F(OpAddOutKernelTest, BroadcastOneElementRank0Tensor) {
358 TensorFactory<ScalarType::Float> tf;
359
360 Tensor a = tf.make({1}, {5});
361 Tensor b = tf.make({}, {2});
362
363 Tensor out = tf.zeros({1});
364
365 op_add_out(a, b, 1, out);
366
367 Tensor ret = tf.make({1}, {7});
368 EXPECT_TENSOR_EQ(out, ret);
369
370 op_add_out(b, a, 1, out);
371 EXPECT_TENSOR_EQ(out, ret);
372 }
373
374 //
375 // Death Tests
376 //
377
TEST_F(OpAddOutKernelTest,IntInputsFloatAlphaDies)378 TEST_F(OpAddOutKernelTest, IntInputsFloatAlphaDies) {
379 // op_add_out() doesn't handle floating alpha for intergal inputs
380 TensorFactory<ScalarType::Int> tf;
381
382 const std::vector<int32_t> sizes = {2, 2};
383
384 // Destination for the op.
385 Tensor out = tf.zeros(sizes);
386
387 // Elementwise add operation on two integral tensor with floating alpha
388 // should cause an assertion and kill the test process.
389 ET_EXPECT_KERNEL_FAILURE(
390 context_, op_add_out(tf.ones(sizes), tf.ones(sizes), /*alpha=*/.7, out));
391 }
392
TEST_F(OpAddOutKernelTest,BoolInputsFloatAlphaDies)393 TEST_F(OpAddOutKernelTest, BoolInputsFloatAlphaDies) {
394 // op_add_out() doesn't handle floating alpha for intergal inputs
395 TensorFactory<ScalarType::Bool> tf;
396
397 const std::vector<int32_t> sizes = {2, 2};
398
399 // Destination for the op.
400 Tensor out = tf.zeros(sizes);
401
402 // Elementwise add operation on two integral tensor with floating alpha
403 // should cause an assertion and kill the test process.
404 ET_EXPECT_KERNEL_FAILURE(
405 context_, op_add_out(tf.ones(sizes), tf.ones(sizes), /*alpha=*/.7, out));
406 }
407
TEST_F(OpAddOutKernelTest,IntOutputWithFloatInputDies)408 TEST_F(OpAddOutKernelTest, IntOutputWithFloatInputDies) {
409 TensorFactory<ScalarType::Int> tfi;
410 TensorFactory<ScalarType::Float> tff;
411
412 const std::vector<int32_t> sizes = {2, 2};
413
414 // Addends.
415 Tensor a = tfi.make(sizes, /*data=*/{2, 4, 3, 3});
416 Tensor b = tff.make(sizes, /*data=*/{2, 4, 3, 3});
417
418 // Destination for the sum.
419 Tensor out = tfi.zeros(sizes);
420
421 ET_EXPECT_KERNEL_FAILURE(context_, op_add_out(a, b, /*alpha=*/1, out));
422 }
423
TEST_F(OpAddOutKernelTest,BoolOutputWithIntegralInput)424 TEST_F(OpAddOutKernelTest, BoolOutputWithIntegralInput) {
425 // op_add_out() doesn't handle Bool.
426 TensorFactory<ScalarType::Bool> tf;
427 TensorFactory<ScalarType::Int> tfi;
428
429 const std::vector<int32_t> sizes = {2, 2};
430
431 // Addends.
432 Tensor a = tfi.make(sizes, /*data=*/{false, true, true, false});
433 Tensor b = tfi.make(sizes, /*data=*/{2, 3, 4, 3});
434
435 // Destination for the sum.
436 Tensor out = tf.zeros(sizes);
437
438 ET_EXPECT_KERNEL_FAILURE(context_, op_add_out(a, b, /*alpha=*/1, out));
439 }
440
TEST_F(OpAddOutKernelTest,MismatchedNonBroadcastableInputShapesDies)441 TEST_F(OpAddOutKernelTest, MismatchedNonBroadcastableInputShapesDies) {
442 TensorFactory<ScalarType::Int> tf;
443
444 // Addends with different shapes.
445 Tensor a = tf.ones(/*sizes=*/{4, 2});
446 Tensor b = tf.ones(/*sizes=*/{2, 2});
447
448 // Destination for the sum; matches the shape of one of the inputs.
449 Tensor out = tf.zeros(/*sizes=*/{8});
450
451 // Adding the two mismatched tensors should cause an assertion and kill the
452 // test process.
453 ET_EXPECT_KERNEL_FAILURE(context_, op_add_out(a, b, /*unused=*/0, out));
454 }
455
TEST_F(OpAddOutKernelTest,MismatchedOutputShapesDies)456 TEST_F(OpAddOutKernelTest, MismatchedOutputShapesDies) {
457 if (SupportedFeatures::get()->output_resize) {
458 GTEST_SKIP()
459 << "The current kernel supports implicitly resizing output tensor";
460 }
461
462 TensorFactory<ScalarType::Int> tf;
463
464 const std::vector<int32_t> sizes = {2, 2};
465
466 // Addends with the same shapes.
467 Tensor a = tf.ones(sizes);
468 Tensor b = tf.ones(sizes);
469
470 // Destination with a different shape.
471 Tensor out = tf.zeros(/*sizes=*/{4});
472
473 // Adding the tensors into a mismatched output should cause an assertion and
474 // kill the test process.
475 ET_EXPECT_KERNEL_FAILURE(context_, op_add_out(a, b, /*unused=*/0, out));
476 }
477
TEST_F(OpAddOutKernelTest,SimpleGeneratedCase)478 TEST_F(OpAddOutKernelTest, SimpleGeneratedCase) {
479 et_pal_init();
480
481 TensorFactory<ScalarType::Float> tf;
482
483 Tensor x = tf.make(
484 {10, 10},
485 {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,
486 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,
487 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,
488 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,
489 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,
490 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,
491 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,
492 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
493 Tensor y = tf.make(
494 {10, 10},
495 {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,
496 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,
497 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,
498 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,
499 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,
500 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,
501 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,
502 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
503 Tensor expected_result = tf.make(
504 {10, 10},
505 {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
506 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
507 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
508 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
509 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
510 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
511 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
512 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0});
513
514 Tensor out = tf.zeros({10, 10});
515 Tensor ret = op_add_out(x, y, 1, out);
516 EXPECT_TENSOR_CLOSE(out, expected_result);
517 }
518
TEST_F(OpAddOutKernelTest,DynamicShapeUpperBoundSameAsExpected)519 TEST_F(OpAddOutKernelTest, DynamicShapeUpperBoundSameAsExpected) {
520 TensorFactory<ScalarType::Float> tf;
521
522 Tensor x = tf.make(
523 {3, 2},
524 {0.04024535417556763,
525 0.6475827097892761,
526 0.9623860716819763,
527 0.6206040978431702,
528 0.47623592615127563,
529 0.4509747624397278});
530 Tensor y = tf.make(
531 {3, 2},
532 {0.7232733964920044,
533 0.3614498972892761,
534 0.15757757425308228,
535 0.9975225925445557,
536 0.09227871894836426,
537 0.3320664167404175});
538 Tensor expected_result = tf.make(
539 {3, 2},
540 {0.763518750667572,
541 1.0090326070785522,
542 1.1199636459350586,
543 1.618126630783081,
544 0.5685146450996399,
545 0.7830411791801453});
546
547 Tensor out =
548 tf.zeros({3, 2}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
549 Tensor ret = op_add_out(x, y, 1, out);
550 EXPECT_TENSOR_CLOSE(out, expected_result);
551 }
552
TEST_F(OpAddOutKernelTest,DynamicShapeUpperBoundLargerThanExpected)553 TEST_F(OpAddOutKernelTest, DynamicShapeUpperBoundLargerThanExpected) {
554 TensorFactory<ScalarType::Float> tf;
555
556 Tensor x = tf.make(
557 {3, 2},
558 {0.04024535417556763,
559 0.6475827097892761,
560 0.9623860716819763,
561 0.6206040978431702,
562 0.47623592615127563,
563 0.4509747624397278});
564 Tensor y = tf.make(
565 {3, 2},
566 {0.7232733964920044,
567 0.3614498972892761,
568 0.15757757425308228,
569 0.9975225925445557,
570 0.09227871894836426,
571 0.3320664167404175});
572 Tensor expected_result = tf.make(
573 {3, 2},
574 {0.763518750667572,
575 1.0090326070785522,
576 1.1199636459350586,
577 1.618126630783081,
578 0.5685146450996399,
579 0.7830411791801453});
580
581 Tensor out =
582 tf.zeros({10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
583 Tensor ret = op_add_out(x, y, 1, out);
584 EXPECT_TENSOR_CLOSE(out, expected_result);
585 }
586
TEST_F(OpAddOutKernelTest,DynamicShapeUnbound)587 TEST_F(OpAddOutKernelTest, DynamicShapeUnbound) {
588 GTEST_SKIP() << "Dynamic shape not supported";
589 TensorFactory<ScalarType::Float> tf;
590
591 Tensor x = tf.make(
592 {3, 2},
593 {0.04024535417556763,
594 0.6475827097892761,
595 0.9623860716819763,
596 0.6206040978431702,
597 0.47623592615127563,
598 0.4509747624397278});
599 Tensor y = tf.make(
600 {3, 2},
601 {0.7232733964920044,
602 0.3614498972892761,
603 0.15757757425308228,
604 0.9975225925445557,
605 0.09227871894836426,
606 0.3320664167404175});
607 Tensor expected_result = tf.make(
608 {3, 2},
609 {0.763518750667572,
610 1.0090326070785522,
611 1.1199636459350586,
612 1.618126630783081,
613 0.5685146450996399,
614 0.7830411791801453});
615
616 Tensor out =
617 tf.zeros({1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
618 Tensor ret = op_add_out(x, y, 1, out);
619 EXPECT_TENSOR_CLOSE(out, expected_result);
620 }
621
TEST_F(OpAddScalarOutKernelTest,SanityCheck)622 TEST_F(OpAddScalarOutKernelTest, SanityCheck) {
623 TensorFactory<ScalarType::Int> tf;
624
625 const std::vector<int32_t> sizes = {2, 2};
626
627 Tensor out = tf.zeros(sizes);
628
629 op_add_scalar_out(tf.make(sizes, {1, 2, 4, 8}), true, /*alpha=*/2, out);
630
631 // Check that it matches the expected output.
632 EXPECT_TENSOR_EQ(out, tf.make(sizes, {3, 4, 6, 10}));
633 }
634
TEST_F(OpAddScalarOutKernelTest,OptimizedSanityCheck)635 TEST_F(OpAddScalarOutKernelTest, OptimizedSanityCheck) {
636 TensorFactory<ScalarType::Float> tf;
637
638 const std::vector<int32_t> sizes = {2, 2};
639
640 Tensor out = tf.zeros(sizes);
641
642 op_add_scalar_out(
643 tf.make(sizes, {1.3, 2.1, 4.6, 8.2}), 1.9, /*alpha=*/2.8, out);
644
645 // Check that it matches the expected output.
646 EXPECT_TENSOR_CLOSE(out, tf.make(sizes, {6.62, 7.42, 9.92, 13.52}));
647 }
648
TEST_F(OpAddScalarOutKernelTest,DtypeTest_float16_bool_int_float16)649 TEST_F(OpAddScalarOutKernelTest, DtypeTest_float16_bool_int_float16) {
650 torch::executor::testing::TensorFactory<exec_aten::ScalarType::Half> tfHalf;
651
652 exec_aten::Tensor self = tfHalf.ones({2, 2});
653 exec_aten::Scalar other = exec_aten::Scalar(true);
654 exec_aten::Scalar alpha = exec_aten::Scalar(1);
655 exec_aten::Tensor out = tfHalf.zeros({2, 2});
656 exec_aten::Tensor out_expected = tfHalf.full({2, 2}, 2.0);
657 op_add_scalar_out(self, other, alpha, out);
658 EXPECT_TENSOR_CLOSE(out, out_expected);
659 }
660