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 using namespace ::testing;
19 using exec_aten::ArrayRef;
20 using exec_aten::ScalarType;
21 using exec_aten::Tensor;
22 using torch::executor::testing::TensorFactory;
23
24 class OpTCopyTest : public OperatorTest {
25 protected:
op_t_copy_out(const Tensor & self,Tensor & out)26 Tensor& op_t_copy_out(const Tensor& self, Tensor& out) {
27 return torch::executor::aten::t_copy_outf(context_, self, out);
28 }
29 };
30
31 TEST_F(OpTCopyTest, 1DTranspose) {
32 TensorFactory<ScalarType::Int> tf;
33
34 Tensor t_in = tf.make({4}, {1, 2, 3, 4});
35 Tensor t_out = tf.make({4}, {0, 0, 0, 0});
36
37 op_t_copy_out(t_in, t_out);
38 EXPECT_TENSOR_EQ(t_in, t_out);
39 }
40
41 TEST_F(OpTCopyTest, 1DTransposeMismatchShapeDie) {
42 if (torch::executor::testing::SupportedFeatures::get()->is_aten) {
43 GTEST_SKIP() << "ATen kernel can handle mismatched shapes";
44 }
45 TensorFactory<ScalarType::Int> tf;
46
47 Tensor t_in = tf.make({4}, {1, 2, 3, 4});
48 Tensor t_out = tf.make({2}, {0, 0});
49
50 ET_EXPECT_KERNEL_FAILURE(context_, op_t_copy_out(t_in, t_out));
51 }
52
53 TEST_F(OpTCopyTest, 2DTranspose) {
54 TensorFactory<ScalarType::Int> tf;
55
56 Tensor t_in = tf.make({2, 3}, {1, 2, 3, 4, 5, 6});
57 Tensor t_out = tf.make({3, 2}, {0, 0, 0, 0, 0, 0});
58 Tensor t_expected = tf.make({3, 2}, {1, 4, 2, 5, 3, 6});
59
60 op_t_copy_out(t_in, t_out);
61 EXPECT_TENSOR_EQ(t_out, t_expected);
62 }
63
64 TEST_F(OpTCopyTest, 2DTransposeMismatchShapeDie) {
65 if (torch::executor::testing::SupportedFeatures::get()->is_aten) {
66 GTEST_SKIP() << "ATen kernel can handle mismatched shapes";
67 }
68 TensorFactory<ScalarType::Int> tf;
69
70 Tensor t_in = tf.make({2, 3}, {1, 2, 3, 4, 5, 6});
71 Tensor t_out = tf.make({2, 2}, {0, 0, 0, 0});
72
73 ET_EXPECT_KERNEL_FAILURE(context_, op_t_copy_out(t_in, t_out));
74 }
75
76 TEST_F(OpTCopyTest, 3DTransposeDie) {
77 TensorFactory<ScalarType::Int> tf;
78
79 Tensor t_in = tf.make({2, 3, 1}, {1, 2, 3, 4, 5, 6});
80 Tensor t_out = tf.make({3, 2, 1}, {0, 0, 0, 0, 0, 0});
81
82 ET_EXPECT_KERNEL_FAILURE(context_, op_t_copy_out(t_in, t_out));
83 }
84
85 /* %python
86 import torch
87 torch.manual_seed(0)
88 x = torch.rand(3, 2)
89 res = torch.t(x)
90 op = "op_t_copy_out"
91 dtype = "ScalarType::Float"
92 check = "EXPECT_TENSOR_EQ" */
93
TEST_F(OpTCopyTest,DynamicShapeUpperBoundSameAsExpected)94 TEST_F(OpTCopyTest, DynamicShapeUpperBoundSameAsExpected) {
95 /* %python
96 out_args = "{2, 3}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND"
97 %rewrite(unary_op) */
98
99 TensorFactory<ScalarType::Float> tf;
100
101 Tensor x = tf.make(
102 {3, 2},
103 {0.49625658988952637,
104 0.7682217955589294,
105 0.08847743272781372,
106 0.13203048706054688,
107 0.30742281675338745,
108 0.6340786814689636});
109 Tensor expected = tf.make(
110 {2, 3},
111 {0.49625658988952637,
112 0.08847743272781372,
113 0.30742281675338745,
114 0.7682217955589294,
115 0.13203048706054688,
116 0.6340786814689636});
117
118 Tensor out =
119 tf.zeros({2, 3}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
120 op_t_copy_out(x, out);
121 EXPECT_TENSOR_EQ(out, expected);
122 }
123
TEST_F(OpTCopyTest,DynamicShapeUpperBoundLargerThanExpected)124 TEST_F(OpTCopyTest, DynamicShapeUpperBoundLargerThanExpected) {
125 if (!torch::executor::testing::SupportedFeatures::get()->output_resize) {
126 GTEST_SKIP() << "Dynamic shape not supported";
127 }
128 /* %python
129 out_args = "{10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND"
130 %rewrite(unary_op) */
131
132 TensorFactory<ScalarType::Float> tf;
133
134 Tensor x = tf.make(
135 {3, 2},
136 {0.49625658988952637,
137 0.7682217955589294,
138 0.08847743272781372,
139 0.13203048706054688,
140 0.30742281675338745,
141 0.6340786814689636});
142 Tensor expected = tf.make(
143 {2, 3},
144 {0.49625658988952637,
145 0.08847743272781372,
146 0.30742281675338745,
147 0.7682217955589294,
148 0.13203048706054688,
149 0.6340786814689636});
150
151 Tensor out =
152 tf.zeros({10, 10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
153 op_t_copy_out(x, out);
154 EXPECT_TENSOR_EQ(out, expected);
155 }
156
TEST_F(OpTCopyTest,DynamicShapeUnbound)157 TEST_F(OpTCopyTest, DynamicShapeUnbound) {
158 if (!torch::executor::testing::SupportedFeatures::get()->output_resize) {
159 GTEST_SKIP() << "Dynamic shape not supported";
160 }
161 /* %python
162 out_args = "{1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND"
163 %rewrite(unary_op) */
164
165 TensorFactory<ScalarType::Float> tf;
166
167 Tensor x = tf.make(
168 {3, 2},
169 {0.49625658988952637,
170 0.7682217955589294,
171 0.08847743272781372,
172 0.13203048706054688,
173 0.30742281675338745,
174 0.6340786814689636});
175 Tensor expected = tf.make(
176 {2, 3},
177 {0.49625658988952637,
178 0.08847743272781372,
179 0.30742281675338745,
180 0.7682217955589294,
181 0.13203048706054688,
182 0.6340786814689636});
183
184 Tensor out =
185 tf.zeros({1, 1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
186 op_t_copy_out(x, out);
187 EXPECT_TENSOR_EQ(out, expected);
188 }
189