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 class OpDetachCopyOutTest : public OperatorTest {
26 protected:
op_detach_copy_out(const Tensor & self,Tensor & out)27 Tensor& op_detach_copy_out(const Tensor& self, Tensor& out) {
28 return torch::executor::aten::detach_copy_outf(context_, self, out);
29 }
30
31 // Common testing for eq operator
32 template <ScalarType DTYPE>
test_detach_copy_out()33 void test_detach_copy_out() {
34 TensorFactory<DTYPE> tf;
35 const std::vector<int32_t> sizes = {2, 2};
36
37 Tensor in = tf.make(sizes, {1, 2, 3, 4});
38 Tensor out = tf.zeros(sizes);
39
40 // Valid input should give the expected output
41 op_detach_copy_out(in, out);
42 EXPECT_TENSOR_EQ(out, tf.make(sizes, {1, 2, 3, 4}));
43 }
44
45 template <ScalarType DTYPE>
test_detach_copy_out_invalid_shape()46 void test_detach_copy_out_invalid_shape() {
47 TensorFactory<DTYPE> tf;
48
49 const std::vector<int32_t> in_sizes = {2, 2};
50 const std::vector<int32_t> out_sizes = {4};
51
52 Tensor in = tf.ones(in_sizes);
53 Tensor out = tf.zeros(out_sizes);
54
55 ET_EXPECT_KERNEL_FAILURE(context_, op_detach_copy_out(in, out));
56 }
57 };
58
59 template <>
test_detach_copy_out()60 void OpDetachCopyOutTest::test_detach_copy_out<ScalarType::Bool>() {
61 TensorFactory<ScalarType::Bool> tf;
62 const std::vector<int32_t> sizes = {2, 2};
63 Tensor out = tf.zeros(sizes);
64
65 // Valid input should give the expected output
66 op_detach_copy_out(tf.make(sizes, /*data=*/{true, false, true, false}), out);
67 EXPECT_TENSOR_EQ(out, tf.make(sizes, /*data=*/{true, false, true, false}));
68 }
69
70 template <>
test_detach_copy_out()71 void OpDetachCopyOutTest::test_detach_copy_out<ScalarType::Float>() {
72 TensorFactory<ScalarType::Float> tf;
73 const std::vector<int32_t> sizes = {2, 2};
74 Tensor out = tf.zeros(sizes);
75
76 // Valid input should give the expected output
77 op_detach_copy_out(
78 tf.make(sizes, /*data=*/{3.14, INFINITY, -INFINITY, NAN}), out);
79 EXPECT_TENSOR_EQ(
80 out, tf.make(sizes, /*data=*/{3.14, INFINITY, -INFINITY, NAN}));
81 }
82
TEST_F(OpDetachCopyOutTest,AllScalarInputOutputSupport)83 TEST_F(OpDetachCopyOutTest, AllScalarInputOutputSupport) {
84 #define TEST_ENTRY(ctype, dtype) test_detach_copy_out<ScalarType::dtype>();
85 ET_FORALL_REAL_TYPES_AND(Bool, TEST_ENTRY);
86 #undef TEST_ENTRY
87 }
88
89 // Mismatched shape tests.
TEST_F(OpDetachCopyOutTest,MismatchedShapesDies)90 TEST_F(OpDetachCopyOutTest, MismatchedShapesDies) {
91 if (torch::executor::testing::SupportedFeatures::get()->is_aten) {
92 GTEST_SKIP() << "ATen kernel can handle mismatched shapes";
93 }
94 #define TEST_ENTRY(ctype, dtype) \
95 test_detach_copy_out_invalid_shape<ScalarType::dtype>();
96 ET_FORALL_REAL_TYPES_AND(Bool, TEST_ENTRY);
97 #undef TEST_ENTRY
98 }
99
TEST_F(OpDetachCopyOutTest,MismatchedInputDtypesDies)100 TEST_F(OpDetachCopyOutTest, MismatchedInputDtypesDies) {
101 TensorFactory<ScalarType::Byte> tf_byte;
102 TensorFactory<ScalarType::Char> tf_char;
103
104 const std::vector<int32_t> sizes = {2, 2};
105
106 Tensor in = tf_byte.ones(sizes);
107 Tensor out = tf_char.ones(sizes);
108
109 ET_EXPECT_KERNEL_FAILURE(context_, op_detach_copy_out(in, out));
110 }
111
TEST_F(OpDetachCopyOutTest,SimpleGeneratedCase)112 TEST_F(OpDetachCopyOutTest, SimpleGeneratedCase) {
113 TensorFactory<ScalarType::Float> tf;
114
115 Tensor x = tf.make(
116 {10, 10},
117 {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,
118 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,
119 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,
120 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,
121 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,
122 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,
123 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,
124 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
125 Tensor expected_result = tf.make(
126 {10, 10},
127 {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,
128 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,
129 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,
130 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,
131 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,
132 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,
133 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,
134 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
135
136 Tensor out = tf.zeros({10, 10});
137 Tensor ret = op_detach_copy_out(x, out);
138 EXPECT_TENSOR_CLOSE(out, expected_result);
139 }
140
TEST_F(OpDetachCopyOutTest,DynamicShapeUpperBoundSameAsExpected)141 TEST_F(OpDetachCopyOutTest, DynamicShapeUpperBoundSameAsExpected) {
142 TensorFactory<ScalarType::Float> tf;
143
144 Tensor x = tf.make(
145 {3, 2},
146 {0.18719732761383057,
147 0.03402292728424072,
148 0.944246232509613,
149 0.8801798820495605,
150 0.0012360215187072754,
151 0.5935860276222229});
152 Tensor expected_result = tf.make(
153 {3, 2},
154 {0.18719732761383057,
155 0.03402292728424072,
156 0.944246232509613,
157 0.8801798820495605,
158 0.0012360215187072754,
159 0.5935860276222229});
160
161 Tensor out =
162 tf.zeros({3, 2}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
163 Tensor ret = op_detach_copy_out(x, out);
164 EXPECT_TENSOR_CLOSE(out, expected_result);
165 }
166
TEST_F(OpDetachCopyOutTest,DynamicShapeUpperBoundLargerThanExpected)167 TEST_F(OpDetachCopyOutTest, DynamicShapeUpperBoundLargerThanExpected) {
168 TensorFactory<ScalarType::Float> tf;
169
170 Tensor x = tf.make(
171 {3, 2},
172 {0.18719732761383057,
173 0.03402292728424072,
174 0.944246232509613,
175 0.8801798820495605,
176 0.0012360215187072754,
177 0.5935860276222229});
178 Tensor expected_result = tf.make(
179 {3, 2},
180 {0.18719732761383057,
181 0.03402292728424072,
182 0.944246232509613,
183 0.8801798820495605,
184 0.0012360215187072754,
185 0.5935860276222229});
186
187 Tensor out =
188 tf.zeros({10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
189 Tensor ret = op_detach_copy_out(x, out);
190 EXPECT_TENSOR_CLOSE(out, expected_result);
191 }
192
TEST_F(OpDetachCopyOutTest,DynamicShapeUnbound)193 TEST_F(OpDetachCopyOutTest, DynamicShapeUnbound) {
194 GTEST_SKIP() << "Dynamic shape unbound not supported";
195 TensorFactory<ScalarType::Float> tf;
196
197 Tensor x = tf.make(
198 {3, 2},
199 {0.18719732761383057,
200 0.03402292728424072,
201 0.944246232509613,
202 0.8801798820495605,
203 0.0012360215187072754,
204 0.5935860276222229});
205 Tensor expected_result = tf.make(
206 {3, 2},
207 {0.18719732761383057,
208 0.03402292728424072,
209 0.944246232509613,
210 0.8801798820495605,
211 0.0012360215187072754,
212 0.5935860276222229});
213
214 Tensor out =
215 tf.zeros({1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
216 Tensor ret = op_detach_copy_out(x, out);
217 EXPECT_TENSOR_CLOSE(out, expected_result);
218 }
219