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 <algorithm>
10 #include <cmath>
11 #include <cstdint>
12 #include <limits>
13 #include <sstream>
14 #include <vector>
15
16 #include <executorch/runtime/core/exec_aten/exec_aten.h>
17 #include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h>
18 #include <executorch/runtime/core/exec_aten/testing_util/tensor_util.h>
19 #include <executorch/test/utils/DeathTest.h>
20
21 #include <gtest/gtest.h>
22
23 using namespace ::testing;
24 using exec_aten::ScalarType;
25 using exec_aten::Tensor;
26 using exec_aten::TensorImpl;
27 using exec_aten::TensorList;
28 using executorch::runtime::testing::IsCloseTo;
29 using executorch::runtime::testing::IsDataCloseTo;
30 using executorch::runtime::testing::IsDataEqualTo;
31 using executorch::runtime::testing::IsEqualTo;
32 using executorch::runtime::testing::IsListCloseTo;
33 using executorch::runtime::testing::IsListEqualTo;
34 using executorch::runtime::testing::tensor_data_is_close;
35 using executorch::runtime::testing::tensor_lists_are_close;
36 using executorch::runtime::testing::TensorFactory;
37 using executorch::runtime::testing::tensors_are_close;
38
39 // Exhaustively test all of our comparison functions every time. Also flip the
40 // params around to demonstrate that the underlying checks are commutative.
41
42 #define EXPECT_TENSORS_CLOSE_AND_EQUAL(t1__, t2__) \
43 EXPECT_TRUE(tensors_are_close((t1__), (t2__))); \
44 EXPECT_TRUE(tensors_are_close((t2__), (t1__))); \
45 EXPECT_THAT((t1__), IsCloseTo((t2__))); \
46 EXPECT_THAT((t2__), IsCloseTo((t1__))); \
47 EXPECT_TENSOR_CLOSE((t1__), (t2__)); \
48 EXPECT_TENSOR_CLOSE((t2__), (t1__)); \
49 ASSERT_TENSOR_CLOSE((t1__), (t2__)); \
50 ASSERT_TENSOR_CLOSE((t2__), (t1__)); \
51 EXPECT_THAT((t1__), IsEqualTo(t2__)); \
52 EXPECT_THAT((t2__), IsEqualTo(t1__)); \
53 EXPECT_TENSOR_EQ((t1__), (t2__)); \
54 EXPECT_TENSOR_EQ((t2__), (t1__)); \
55 ASSERT_TENSOR_EQ((t1__), (t2__)); \
56 ASSERT_TENSOR_EQ((t2__), (t1__))
57
58 #define EXPECT_TENSORS_CLOSE_BUT_NOT_EQUAL(t1__, t2__) \
59 EXPECT_TRUE(tensors_are_close((t1__), (t2__))); \
60 EXPECT_TRUE(tensors_are_close((t2__), (t1__))); \
61 EXPECT_THAT((t1__), IsCloseTo((t2__))); \
62 EXPECT_THAT((t2__), IsCloseTo((t1__))); \
63 EXPECT_TENSOR_CLOSE((t1__), (t2__)); \
64 EXPECT_TENSOR_CLOSE((t2__), (t1__)); \
65 ASSERT_TENSOR_CLOSE((t1__), (t2__)); \
66 ASSERT_TENSOR_CLOSE((t2__), (t1__)); \
67 EXPECT_THAT((t1__), Not(IsEqualTo(t2__))); \
68 EXPECT_THAT((t2__), Not(IsEqualTo(t1__))); \
69 EXPECT_TENSOR_NE((t1__), (t2__)); \
70 EXPECT_TENSOR_NE((t2__), (t1__)); \
71 ASSERT_TENSOR_NE((t1__), (t2__)); \
72 ASSERT_TENSOR_NE((t2__), (t1__))
73
74 #define EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(t1__, t2__) \
75 EXPECT_FALSE(tensors_are_close((t1__), (t2__))); \
76 EXPECT_FALSE(tensors_are_close((t2__), (t1__))); \
77 EXPECT_THAT((t1__), Not(IsCloseTo((t2__)))); \
78 EXPECT_THAT((t2__), Not(IsCloseTo((t1__)))); \
79 EXPECT_TENSOR_NOT_CLOSE((t1__), (t2__)); \
80 EXPECT_TENSOR_NOT_CLOSE((t2__), (t1__)); \
81 ASSERT_TENSOR_NOT_CLOSE((t1__), (t2__)); \
82 ASSERT_TENSOR_NOT_CLOSE((t2__), (t1__)); \
83 EXPECT_THAT((t1__), Not(IsEqualTo(t2__))); \
84 EXPECT_THAT((t2__), Not(IsEqualTo(t1__))); \
85 EXPECT_TENSOR_NE((t1__), (t2__)); \
86 EXPECT_TENSOR_NE((t2__), (t1__)); \
87 ASSERT_TENSOR_NE((t1__), (t2__)); \
88 ASSERT_TENSOR_NE((t2__), (t1__))
89
90 #define EXPECT_TENSORS_DATA_CLOSE_AND_EQUAL(t1__, t2__) \
91 EXPECT_TRUE(tensor_data_is_close((t1__), (t2__))); \
92 EXPECT_TRUE(tensor_data_is_close((t2__), (t1__))); \
93 EXPECT_THAT((t1__), IsDataCloseTo((t2__))); \
94 EXPECT_THAT((t2__), IsDataCloseTo((t1__))); \
95 EXPECT_TENSOR_DATA_CLOSE((t1__), (t2__)); \
96 EXPECT_TENSOR_DATA_CLOSE((t2__), (t1__)); \
97 ASSERT_TENSOR_DATA_CLOSE((t1__), (t2__)); \
98 ASSERT_TENSOR_DATA_CLOSE((t2__), (t1__)); \
99 EXPECT_THAT((t1__), IsDataEqualTo(t2__)); \
100 EXPECT_THAT((t2__), IsDataEqualTo(t1__)); \
101 EXPECT_TENSOR_DATA_EQ((t1__), (t2__)); \
102 EXPECT_TENSOR_DATA_EQ((t2__), (t1__)); \
103 ASSERT_TENSOR_DATA_EQ((t1__), (t2__)); \
104 ASSERT_TENSOR_DATA_EQ((t2__), (t1__))
105
106 #define EXPECT_TENSORS_DATA_CLOSE_BUT_NOT_EQUAL(t1__, t2__) \
107 EXPECT_TRUE(tensor_data_is_close((t1__), (t2__))); \
108 EXPECT_TRUE(tensor_data_is_close((t2__), (t1__))); \
109 EXPECT_THAT((t1__), IsDataCloseTo((t2__))); \
110 EXPECT_THAT((t2__), IsDataCloseTo((t1__))); \
111 EXPECT_TENSOR_DATA_CLOSE((t1__), (t2__)); \
112 EXPECT_TENSOR_DATA_CLOSE((t2__), (t1__)); \
113 ASSERT_TENSOR_DATA_CLOSE((t1__), (t2__)); \
114 ASSERT_TENSOR_DATA_CLOSE((t2__), (t1__)); \
115 EXPECT_THAT((t1__), Not(IsDataEqualTo(t2__))); \
116 EXPECT_THAT((t2__), Not(IsDataEqualTo(t1__))); \
117 EXPECT_TENSOR_DATA_NE((t1__), (t2__)); \
118 EXPECT_TENSOR_DATA_NE((t2__), (t1__)); \
119 ASSERT_TENSOR_DATA_NE((t1__), (t2__)); \
120 ASSERT_TENSOR_DATA_NE((t2__), (t1__))
121
122 #define EXPECT_TENSORS_DATA_NOT_CLOSE_OR_EQUAL(t1__, t2__) \
123 EXPECT_FALSE(tensor_data_is_close((t1__), (t2__))); \
124 EXPECT_FALSE(tensor_data_is_close((t2__), (t1__))); \
125 EXPECT_THAT((t1__), Not(IsDataCloseTo((t2__)))); \
126 EXPECT_THAT((t2__), Not(IsDataCloseTo((t1__)))); \
127 EXPECT_TENSOR_DATA_NOT_CLOSE((t1__), (t2__)); \
128 EXPECT_TENSOR_DATA_NOT_CLOSE((t2__), (t1__)); \
129 ASSERT_TENSOR_NOT_CLOSE((t1__), (t2__)); \
130 ASSERT_TENSOR_NOT_CLOSE((t2__), (t1__)); \
131 EXPECT_THAT((t1__), Not(IsDataEqualTo(t2__))); \
132 EXPECT_THAT((t2__), Not(IsDataEqualTo(t1__))); \
133 EXPECT_TENSOR_DATA_NE((t1__), (t2__)); \
134 EXPECT_TENSOR_DATA_NE((t2__), (t1__)); \
135 ASSERT_TENSOR_DATA_NE((t1__), (t2__)); \
136 ASSERT_TENSOR_DATA_NE((t2__), (t1__))
137
138 #define EXPECT_TENSOR_LISTS_CLOSE_AND_EQUAL(list1__, list2__) \
139 EXPECT_TRUE(tensor_lists_are_close( \
140 (list1__).data(), \
141 (list1__).size(), \
142 (list2__).data(), \
143 (list2__).size())); \
144 EXPECT_TRUE(tensor_lists_are_close( \
145 (list2__).data(), \
146 (list2__).size(), \
147 (list1__).data(), \
148 (list1__).size())); \
149 EXPECT_THAT((list1__), IsListCloseTo((list2__))); \
150 EXPECT_THAT((list2__), IsListCloseTo((list1__))); \
151 EXPECT_TENSOR_LISTS_CLOSE((list1__), (list2__)); \
152 EXPECT_TENSOR_LISTS_CLOSE((list2__), (list1__)); \
153 ASSERT_TENSOR_LISTS_CLOSE((list1__), (list2__)); \
154 ASSERT_TENSOR_LISTS_CLOSE((list2__), (list1__)); \
155 EXPECT_THAT((list1__), IsListEqualTo(list2__)); \
156 EXPECT_THAT((list2__), IsListEqualTo(list1__)); \
157 EXPECT_TENSOR_LISTS_EQ((list1__), (list2__)); \
158 EXPECT_TENSOR_LISTS_EQ((list2__), (list1__)); \
159 ASSERT_TENSOR_LISTS_EQ((list1__), (list2__)); \
160 ASSERT_TENSOR_LISTS_EQ((list2__), (list1__))
161
162 #define EXPECT_TENSOR_LISTS_CLOSE_BUT_NOT_EQUAL(list1__, list2__) \
163 EXPECT_TRUE(tensor_lists_are_close( \
164 (list1__).data(), \
165 (list1__).size(), \
166 (list2__).data(), \
167 (list2__).size())); \
168 EXPECT_TRUE(tensor_lists_are_close( \
169 (list2__).data(), \
170 (list2__).size(), \
171 (list1__).data(), \
172 (list1__).size())); \
173 EXPECT_THAT((list1__), IsListCloseTo((list2__))); \
174 EXPECT_THAT((list2__), IsListCloseTo((list1__))); \
175 EXPECT_TENSOR_LISTS_CLOSE((list1__), (list2__)); \
176 EXPECT_TENSOR_LISTS_CLOSE((list2__), (list1__)); \
177 ASSERT_TENSOR_LISTS_CLOSE((list1__), (list2__)); \
178 ASSERT_TENSOR_LISTS_CLOSE((list2__), (list1__)); \
179 EXPECT_THAT((list1__), Not(IsListEqualTo(list2__))); \
180 EXPECT_THAT((list2__), Not(IsListEqualTo(list1__))); \
181 EXPECT_TENSOR_LISTS_NE((list1__), (list2__)); \
182 EXPECT_TENSOR_LISTS_NE((list2__), (list1__)); \
183 ASSERT_TENSOR_LISTS_NE((list1__), (list2__)); \
184 ASSERT_TENSOR_LISTS_NE((list2__), (list1__))
185
186 #define EXPECT_TENSOR_LISTS_NOT_CLOSE_OR_EQUAL(list1__, list2__) \
187 EXPECT_FALSE(tensor_lists_are_close( \
188 (list1__).data(), \
189 (list1__).size(), \
190 (list2__).data(), \
191 (list2__).size())); \
192 EXPECT_FALSE(tensor_lists_are_close( \
193 (list2__).data(), \
194 (list2__).size(), \
195 (list1__).data(), \
196 (list1__).size())); \
197 EXPECT_THAT((list1__), Not(IsListCloseTo((list2__)))); \
198 EXPECT_THAT((list2__), Not(IsListCloseTo((list1__)))); \
199 EXPECT_TENSOR_LISTS_NOT_CLOSE((list1__), (list2__)); \
200 EXPECT_TENSOR_LISTS_NOT_CLOSE((list2__), (list1__)); \
201 ASSERT_TENSOR_LISTS_NOT_CLOSE((list1__), (list2__)); \
202 ASSERT_TENSOR_LISTS_NOT_CLOSE((list2__), (list1__)); \
203 EXPECT_THAT((list1__), Not(IsListEqualTo(list2__))); \
204 EXPECT_THAT((list2__), Not(IsListEqualTo(list1__))); \
205 EXPECT_TENSOR_LISTS_NE((list1__), (list2__)); \
206 EXPECT_TENSOR_LISTS_NE((list2__), (list1__)); \
207 ASSERT_TENSOR_LISTS_NE((list1__), (list2__)); \
208 ASSERT_TENSOR_LISTS_NE((list2__), (list1__))
209
210 namespace {
211 // calculate numel given size
size_to_numel(std::vector<int32_t> sizes)212 int32_t size_to_numel(std::vector<int32_t> sizes) {
213 int32_t numel = 1;
214 for (auto size : sizes) {
215 numel *= size;
216 }
217 return numel;
218 }
219
220 } // namespace
221
222 // Mismatched shapes/types/strides.
223
TEST(TensorUtilTest,DifferentDtypesAreNotCloseOrEqual)224 TEST(TensorUtilTest, DifferentDtypesAreNotCloseOrEqual) {
225 // Create two tensors with identical shape and data, but different dtypes.
226 TensorFactory<ScalarType::Int> tf_int;
227 Tensor a = tf_int.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
228
229 TensorFactory<ScalarType::Long> tf_long;
230 Tensor b = tf_long.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
231
232 EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(a, b);
233 }
234
TEST(TensorUtilTest,DifferentSizesAreNotCloseOrEqual)235 TEST(TensorUtilTest, DifferentSizesAreNotCloseOrEqual) {
236 TensorFactory<ScalarType::Int> tf;
237
238 // Create two tensors with identical dtype and data, but different shapes.
239 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
240 Tensor b = tf.make(/*sizes=*/{4}, /*data=*/{1, 2, 4, 8});
241
242 EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(a, b);
243 }
244
TEST(TensorUtilTest,DifferentLayoutsDies)245 TEST(TensorUtilTest, DifferentLayoutsDies) {
246 TensorFactory<ScalarType::Int> tf;
247
248 // Create two tensors with identical dtype, data and shapes, but different
249 // strides.
250 Tensor a = tf.make(
251 /*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8}, /*strided=*/{1, 2});
252 Tensor b = tf.make(
253 /*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8}, /*strided=*/{2, 1});
254
255 // Current `tensors_are_close` does not support comparing two tensors with
256 // different stride.
257 // TODO(T132992348): support comparison between tensors of different strides
258 ET_EXPECT_DEATH(EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(a, b), "");
259 ET_EXPECT_DEATH(EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b), "");
260 }
261
262 // Int tensors, as a proxy for all non-floating-point types.
263
TEST(TensorUtilTest,IntTensorIsCloseAndEqualToItself)264 TEST(TensorUtilTest, IntTensorIsCloseAndEqualToItself) {
265 TensorFactory<ScalarType::Int> tf;
266
267 Tensor t = tf.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
268
269 EXPECT_TENSORS_CLOSE_AND_EQUAL(t, t);
270 }
271
TEST(TensorUtilTest,IdenticalIntTensorsAreCloseAndEqual)272 TEST(TensorUtilTest, IdenticalIntTensorsAreCloseAndEqual) {
273 TensorFactory<ScalarType::Int> tf;
274
275 // Create two tensors with identical shape, dtype, and data.
276 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
277 Tensor b = tf.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
278
279 EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b);
280 }
281
TEST(TensorUtilTest,NonIdenticalIntTensorsAreNotCloseOrEqual)282 TEST(TensorUtilTest, NonIdenticalIntTensorsAreNotCloseOrEqual) {
283 TensorFactory<ScalarType::Int> tf;
284
285 // Create two tensors with identical shape and dtype, but different data.
286 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
287 Tensor b = tf.make(/*sizes=*/{2, 2}, /*data=*/{99, 2, 4, 8});
288
289 EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(a, b);
290 }
291
TEST(TensorUtilTest,EmptyTensorsAreCloseAndEqual)292 TEST(TensorUtilTest, EmptyTensorsAreCloseAndEqual) {
293 TensorFactory<ScalarType::Int> tf;
294
295 // Create two tensors with identical shapes but no data.
296 Tensor a = tf.make(/*sizes=*/{0, 2}, /*data=*/{});
297 EXPECT_EQ(a.numel(), 0);
298 EXPECT_EQ(a.nbytes(), 0);
299 Tensor b = tf.make(/*sizes=*/{0, 2}, /*data=*/{});
300 EXPECT_EQ(b.numel(), 0);
301 EXPECT_EQ(b.nbytes(), 0);
302
303 EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b);
304 }
305
306 // Float tensors, as a proxy for all floating-point types.
307
TEST(TensorUtilTest,FloatTensorIsCloseAndEqualToItself)308 TEST(TensorUtilTest, FloatTensorIsCloseAndEqualToItself) {
309 TensorFactory<ScalarType::Float> tf;
310
311 Tensor t = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
312
313 EXPECT_TENSORS_CLOSE_AND_EQUAL(t, t);
314 }
315
TEST(TensorUtilTest,IdenticalFloatTensorsAreCloseAndEqual)316 TEST(TensorUtilTest, IdenticalFloatTensorsAreCloseAndEqual) {
317 TensorFactory<ScalarType::Float> tf;
318
319 // Create two tensors with identical shape, dtype, and data.
320 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
321 Tensor b = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
322
323 EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b);
324 }
325
TEST(TensorUtilTest,NearlyIdenticalFloatTensorsAreCloseButNotEqual)326 TEST(TensorUtilTest, NearlyIdenticalFloatTensorsAreCloseButNotEqual) {
327 TensorFactory<ScalarType::Float> tf;
328
329 // Create two tensors with identical shape and dtype, but slightly different
330 // data.
331 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
332 Tensor b = tf.make(
333 /*sizes=*/{2, 2},
334 {// First data element is slightly larger.
335 std::nextafter(1.1f, 100.0f),
336 // Remaining data elements are the same.
337 2.2,
338 4.4,
339 8.8});
340
341 EXPECT_TENSORS_CLOSE_BUT_NOT_EQUAL(a, b);
342 }
343
TEST(TensorUtilTest,NonIdenticalFloatTensorsAreNotCloseOrEqual)344 TEST(TensorUtilTest, NonIdenticalFloatTensorsAreNotCloseOrEqual) {
345 TensorFactory<ScalarType::Float> tf;
346
347 // Create two tensors with identical shape and dtype, but different data.
348 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
349 Tensor b = tf.make(/*sizes=*/{2, 2}, /*data=*/{99.99, 2.2, 4.4, 8.8});
350
351 EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(a, b);
352 }
353
TEST(TensorUtilTest,FloatNanElementsAreCloseAndEqual)354 TEST(TensorUtilTest, FloatNanElementsAreCloseAndEqual) {
355 TensorFactory<ScalarType::Float> tf;
356
357 // Two identical tensors with NaN elements.
358 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, NAN, 2.2, NAN});
359 Tensor b = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, NAN, 2.2, NAN});
360
361 EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b);
362 }
363
TEST(TensorUtilTest,FloatNanElementsAreNotEqualToNonNan)364 TEST(TensorUtilTest, FloatNanElementsAreNotEqualToNonNan) {
365 TensorFactory<ScalarType::Float> tf;
366
367 // Regression test ensuring that NaN elements are not compared equal to
368 // non-NaN finite values.
369 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, NAN, 2.2, NAN});
370 Tensor b = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 0.0, 2.2, 0.0});
371
372 EXPECT_TENSORS_NOT_CLOSE_OR_EQUAL(a, b);
373 }
374
TEST(TensorUtilTest,FloatInfiniteElementsAreCloseAndEqual)375 TEST(TensorUtilTest, FloatInfiniteElementsAreCloseAndEqual) {
376 constexpr auto kInfinity = std::numeric_limits<float>::infinity();
377
378 TensorFactory<ScalarType::Float> tf;
379
380 // Two identical tensors with infinite elements.
381 Tensor a =
382 tf.make(/*sizes=*/{2, 2}, /*data=*/{-kInfinity, 1.1, 2.2, kInfinity});
383 Tensor b =
384 tf.make(/*sizes=*/{2, 2}, /*data=*/{-kInfinity, 1.1, 2.2, kInfinity});
385
386 EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b);
387 }
388
389 // Double: less test coverage since Float covers all of the branches, but
390 // demonstrate that it works.
391
TEST(TensorUtilTest,NearlyIdenticalDoubleTensorsAreCloseButNotEqual)392 TEST(TensorUtilTest, NearlyIdenticalDoubleTensorsAreCloseButNotEqual) {
393 TensorFactory<ScalarType::Float> tf;
394
395 // Create two tensors with identical shape and dtype, but slightly different
396 // data.
397 Tensor a = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
398 Tensor b = tf.make(
399 /*sizes=*/{2, 2},
400 {// First data element is slightly larger.
401 std::nextafter(1.1f, 100.0f),
402 // Remaining data elements are the same.
403 2.2,
404 4.4,
405 8.8});
406
407 EXPECT_TENSORS_CLOSE_BUT_NOT_EQUAL(a, b);
408 }
409
TEST(TensorUtilTest,DoubleAndInfinitNanElementsAreCloseAndEqual)410 TEST(TensorUtilTest, DoubleAndInfinitNanElementsAreCloseAndEqual) {
411 constexpr auto kInfinity = std::numeric_limits<double>::infinity();
412
413 TensorFactory<ScalarType::Double> tf;
414
415 // Two identical tensors with NaN and infinite elements.
416 Tensor a =
417 tf.make(/*sizes=*/{2, 2}, /*data=*/{-kInfinity, NAN, 1.1, kInfinity});
418 Tensor b =
419 tf.make(/*sizes=*/{2, 2}, /*data=*/{-kInfinity, NAN, 1.1, kInfinity});
420
421 EXPECT_TENSORS_CLOSE_AND_EQUAL(a, b);
422 }
423
424 // Testing closeness with tolerances
425
TEST(TensorUtilTest,TensorsAreCloseWithTol)426 TEST(TensorUtilTest, TensorsAreCloseWithTol) {
427 TensorFactory<ScalarType::Float> tf;
428 TensorFactory<ScalarType::Double> td;
429
430 // Create two tensors with identical shape and dtype, but different data.
431 Tensor af = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.0, 2.099999, 0.0, -0.05});
432 Tensor bf = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.099999, 2.0, 0.05, 0.0});
433
434 EXPECT_TENSOR_CLOSE_WITH_TOL(af, bf, 0.0, 0.1);
435
436 // Create two tensors with identical shape and dtype, but different data.
437 Tensor ad = td.make(/*sizes=*/{2, 2}, /*data=*/{1.099, 2.199, NAN, -9.0});
438 Tensor bd = td.make(/*sizes=*/{2, 2}, /*data=*/{1.0, 2.0, NAN, -10.0});
439
440 EXPECT_TENSOR_CLOSE_WITH_TOL(ad, bd, 0.1, 0.0);
441 }
442
TEST(TensorUtilTest,TensorsAreNotCloseWithTol)443 TEST(TensorUtilTest, TensorsAreNotCloseWithTol) {
444 TensorFactory<ScalarType::Float> tf;
445 TensorFactory<ScalarType::Double> td;
446
447 // Create two tensors with identical shape and dtype, but different data.
448 Tensor af = tf.make(/*sizes=*/{3}, /*data=*/{1.00, NAN, -10.0});
449 Tensor bf = tf.make(/*sizes=*/{3}, /*data=*/{1.11, NAN, -10.0});
450
451 EXPECT_TENSOR_NOT_CLOSE_WITH_TOL(af, bf, 0.0, 0.1);
452
453 // Create two tensors with identical shape and dtype, but different data.
454 Tensor ad = td.make(/*sizes=*/{3}, /*data=*/{1.0, 0.0, -10.0});
455 Tensor bd = td.make(/*sizes=*/{3}, /*data=*/{1.0, 0.0, -9.0});
456
457 EXPECT_TENSOR_NOT_CLOSE_WITH_TOL(ad, bd, 0.1, 0.0);
458
459 // Create two tensors with identical shape and dtype, but different data.
460 ad = tf.make(/*sizes=*/{3}, /*data=*/{1.0, 2.0, 0.00001});
461 bd = tf.make(/*sizes=*/{3}, /*data=*/{1.0, 2.0, 0.0});
462
463 EXPECT_TENSOR_NOT_CLOSE_WITH_TOL(ad, bd, 0.1, 0.0);
464 }
465
466 //
467 // Tests for shape-agnostic data equality.
468 //
469
470 // Common testing for EXPECT_TENSOR_DATA_EQ in different input sizes and
471 // dtypes.
472 template <ScalarType DTYPE>
test_data_equal(std::vector<int32_t> t1_sizes,std::vector<int32_t> t2_sizes)473 void test_data_equal(
474 std::vector<int32_t> t1_sizes,
475 std::vector<int32_t> t2_sizes) {
476 TensorFactory<DTYPE> tf;
477
478 // get corresponding ctype for input DTYPEs
479 using ctype = typename TensorFactory<DTYPE>::ctype;
480
481 // Get the size of data of t1 and t2.
482 // Make sure the two sizes are equal.
483 auto numel = size_to_numel(t1_sizes);
484 ASSERT_EQ(numel, size_to_numel(t2_sizes));
485
486 // Set up data vector for t1 and t2.
487 // Set them as a same random vector to test them generally.
488 std::vector<ctype> t1_data(numel);
489 std::generate(t1_data.begin(), t1_data.end(), std::rand);
490 std::vector<ctype> t2_data(numel);
491 t2_data = t1_data;
492
493 Tensor t1 = tf.make(t1_sizes, t1_data);
494 Tensor t2 = tf.make(t2_sizes, t2_data);
495
496 EXPECT_TENSORS_DATA_CLOSE_AND_EQUAL(t1, t2);
497 }
498
TEST(TensorUtilTest,TensorDataEqualSizeEqual)499 TEST(TensorUtilTest, TensorDataEqualSizeEqual) {
500 #define TEST_ENTRY(ctype, dtype) \
501 test_data_equal<ScalarType::dtype>({3, 4, 5}, {3, 4, 5});
502 ET_FORALL_REAL_TYPES_AND(Bool, TEST_ENTRY);
503 #undef TEST_ENTRY
504 }
505
TEST(TensorUtilTest,TensorDataEqualSizeUnequal)506 TEST(TensorUtilTest, TensorDataEqualSizeUnequal) {
507 #define TEST_ENTRY(ctype, dtype) \
508 test_data_equal<ScalarType::dtype>({3, 4, 5}, {3, 5, 4});
509 ET_FORALL_REAL_TYPES_AND(Bool, TEST_ENTRY);
510 #undef TEST_ENTRY
511 }
512
TEST(TensorUtilTest,EmptyTensorsSupported)513 TEST(TensorUtilTest, EmptyTensorsSupported) {
514 #define TEST_ENTRY(ctype, dtype) \
515 test_data_equal<ScalarType::dtype>({3, 4, 0, 5}, {3, 4, 0, 5});
516 ET_FORALL_REAL_TYPES_AND(Bool, TEST_ENTRY);
517 #undef TEST_ENTRY
518 }
519
TEST(TensorUtilTest,ZeroDimTensorsSupported)520 TEST(TensorUtilTest, ZeroDimTensorsSupported) {
521 #define TEST_ENTRY(ctype, dtype) test_data_equal<ScalarType::dtype>({}, {});
522 ET_FORALL_REAL_TYPES_AND(Bool, TEST_ENTRY);
523 #undef TEST_ENTRY
524 }
525
526 // Common testing for EXPECT_TENSOR_DATA_CLOSE in different input sizes and
527 // dtypes.
528 template <
529 ScalarType DTYPE,
530 std::enable_if_t<
531 DTYPE == ScalarType::Float || DTYPE == ScalarType::Double,
532 bool> = true>
test_data_close_but_not_equal(std::vector<int32_t> t1_sizes,std::vector<int32_t> t2_sizes)533 void test_data_close_but_not_equal(
534 std::vector<int32_t> t1_sizes,
535 std::vector<int32_t> t2_sizes) {
536 TensorFactory<DTYPE> tf;
537
538 // get corresponding ctype for input DTYPEs
539 using ctype = typename TensorFactory<DTYPE>::ctype;
540
541 // get the size of data of t1 and t2
542 // make sure the two sizes are equal
543 auto numel = size_to_numel(t1_sizes);
544 ASSERT_EQ(numel, size_to_numel(t2_sizes));
545
546 // set up data vector for t1 and t2
547 // set them as a almost same random vector (only the first element are
548 // different if the first element exists) to test them generally
549 std::vector<ctype> t1_data(numel);
550 std::generate(t1_data.begin(), t1_data.end(), std::rand);
551 std::vector<ctype> t2_data(numel);
552 t2_data = t1_data;
553
554 // Set the first element of t2 slightly larger than 0
555 // the "first element" only work if t2_numel > 0
556 // The checking with ctype.max() is to prevent overflow
557 if (numel > 0) {
558 if (t2_data[0] < std::numeric_limits<ctype>::max() - 100) {
559 t2_data[0] = std::nextafter(t2_data[0], t2_data[0] + 100.0f);
560 } else {
561 t2_data[0] = std::nextafter(t2_data[0], t2_data[0] - 100.f);
562 }
563 }
564
565 Tensor t1 = tf.make(t1_sizes, t1_data);
566 Tensor t2 = tf.make(t2_sizes, t2_data);
567
568 EXPECT_TENSORS_DATA_CLOSE_AND_EQUAL(t1, t2);
569 }
570
TEST(TensorUtilTest,TensorDataCloseNotEqualSizeEqual)571 TEST(TensorUtilTest, TensorDataCloseNotEqualSizeEqual) {
572 #define TEST_ENTRY(ctype, dtype) \
573 test_data_equal<ScalarType::dtype>({3, 4, 5}, {3, 4, 5});
574 ET_FORALL_FLOAT_TYPES(TEST_ENTRY);
575 #undef TEST_ENTRY
576 }
577
TEST(TensorUtilTest,TensorDataCloseNotEqualSizeUnequal)578 TEST(TensorUtilTest, TensorDataCloseNotEqualSizeUnequal) {
579 #define TEST_ENTRY(ctype, dtype) \
580 test_data_equal<ScalarType::dtype>({3, 4, 5}, {3, 5, 4});
581 ET_FORALL_FLOAT_TYPES(TEST_ENTRY);
582 #undef TEST_ENTRY
583 }
584
585 // Common testing for EXPECT_TENSOR_DATA_EQ in different input sizes and
586 // dtypes.
587 template <ScalarType DTYPE_T1, ScalarType DTYPE_T2>
test_data_equal_but_size_or_dtype_mismatch(std::vector<int32_t> t1_sizes,std::vector<int32_t> t2_sizes)588 void test_data_equal_but_size_or_dtype_mismatch(
589 std::vector<int32_t> t1_sizes,
590 std::vector<int32_t> t2_sizes) {
591 TensorFactory<DTYPE_T1> tf_t1;
592 TensorFactory<DTYPE_T2> tf_t2;
593
594 Tensor t1 = tf_t1.zeros(t1_sizes);
595 Tensor t2 = tf_t2.zeros(t2_sizes);
596
597 EXPECT_TENSORS_DATA_NOT_CLOSE_OR_EQUAL(t1, t2);
598 }
599
TEST(TensorUtilTest,TensorDataTypeMismatched)600 TEST(TensorUtilTest, TensorDataTypeMismatched) {
601 std::vector<int32_t> sizes = {3, 4, 5, 6};
602 test_data_equal_but_size_or_dtype_mismatch<
603 ScalarType::Float,
604 ScalarType::Double>(sizes, sizes);
605 test_data_equal_but_size_or_dtype_mismatch<
606 ScalarType::Int,
607 ScalarType::Double>(sizes, sizes);
608 }
609
TEST(TensorUtilTest,TensorSizeMismatched)610 TEST(TensorUtilTest, TensorSizeMismatched) {
611 std::vector<int32_t> sizes_t1 = {3, 4, 5, 6};
612 std::vector<int32_t> sizes_t2 = {3, 4, 5, 7};
613 test_data_equal_but_size_or_dtype_mismatch<
614 ScalarType::Float,
615 ScalarType::Float>(sizes_t1, sizes_t2);
616 }
617
TEST(TensorUtilTest,TensorDataMismatched)618 TEST(TensorUtilTest, TensorDataMismatched) {
619 TensorFactory<ScalarType::Int> tf;
620 Tensor t1 = tf.make(/*size=*/{3, 2}, /*data=*/{1, 2, 3, 4, 5, 6});
621 Tensor t2 = tf.make(/*size=*/{3, 2}, /*data=*/{1, 2, 3, 1, 5, 6});
622 Tensor t3 = tf.make(/*size=*/{2, 3}, /*data=*/{1, 2, 3, 1, 5, 6});
623 EXPECT_TENSORS_DATA_NOT_CLOSE_OR_EQUAL(t1, t2);
624 EXPECT_TENSORS_DATA_NOT_CLOSE_OR_EQUAL(t1, t3);
625
626 Tensor t_zero_dim = tf.make(/*size=*/{}, /*data=*/{0});
627 Tensor t_empty = tf.make(/*size=*/{0}, /*data=*/{});
628 EXPECT_TENSORS_DATA_NOT_CLOSE_OR_EQUAL(t_zero_dim, t_empty);
629 }
630
631 // Testing data closeness with tolerances
632
TEST(TensorUtilTest,TensorDataCloseWithTol)633 TEST(TensorUtilTest, TensorDataCloseWithTol) {
634 TensorFactory<ScalarType::Float> tf;
635 TensorFactory<ScalarType::Double> td;
636
637 // Create two tensors with identical shape and dtype, but different data.
638 Tensor af = tf.make(/*sizes=*/{4, 1}, /*data=*/{1.0, 2.099, 0.0, -0.05});
639 Tensor bf = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.099, 2.0, 0.05, 0.0});
640
641 EXPECT_TENSOR_DATA_CLOSE_WITH_TOL(af, bf, 0.0, 0.1);
642
643 // Create two tensors with identical shape and dtype, but different data.
644 Tensor ad = td.make(/*sizes=*/{2, 2}, /*data=*/{1.099, 2.199, NAN, -9.0});
645 Tensor bd = td.make(/*sizes=*/{4}, /*data=*/{1.0, 2.0, NAN, -10.0});
646
647 EXPECT_TENSOR_DATA_CLOSE_WITH_TOL(ad, bd, 0.1, 0.0);
648 }
649
TEST(TensorUtilTest,TensorDataNotCloseWithTol)650 TEST(TensorUtilTest, TensorDataNotCloseWithTol) {
651 TensorFactory<ScalarType::Float> tf;
652 TensorFactory<ScalarType::Double> td;
653
654 // Create two tensors with identical shape and dtype, but different data.
655 Tensor af = tf.make(/*sizes=*/{3}, /*data=*/{1.00, 0.0, -10.0});
656 Tensor bf = tf.make(/*sizes=*/{3, 1}, /*data=*/{1.11, 0.0, -10.0});
657
658 EXPECT_TENSOR_DATA_NOT_CLOSE_WITH_TOL(af, bf, 0.0, 0.1);
659
660 // Create two tensors with identical shape and dtype, but different data.
661 Tensor ad = td.make(/*sizes=*/{2, 2}, /*data=*/{1.0, 0.0, -10.0, 0.0});
662 Tensor bd = td.make(/*sizes=*/{4}, /*data=*/{1.0, 0.0, -9.0, 0.0});
663
664 EXPECT_TENSOR_DATA_NOT_CLOSE_WITH_TOL(ad, bd, 0.1, 0.0);
665
666 // Create two tensors with identical shape and dtype, but different data.
667 ad = tf.make(/*sizes=*/{1, 4}, /*data=*/{1.0, 2.0, NAN, 0.00001});
668 bd = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.0, 2.0, NAN, 0.0});
669
670 EXPECT_TENSOR_DATA_NOT_CLOSE_WITH_TOL(ad, bd, 0.1, 0.0);
671 }
672
673 //
674 // Tests for TensorList helpers.
675 //
676
TEST(TensorUtilTest,TensorListsCloseAndEqual)677 TEST(TensorUtilTest, TensorListsCloseAndEqual) {
678 TensorFactory<ScalarType::Int> tf_int;
679 TensorFactory<ScalarType::Float> tf_float;
680
681 // Two lists of tensors that should be close and equal. Elements have
682 // different shapes and dtypes.
683 std::vector<Tensor> vec1 = {
684 tf_int.zeros(/*sizes=*/{1, 2}),
685 tf_float.ones(/*sizes=*/{2, 1}),
686 };
687 TensorList list1(vec1.data(), vec1.size());
688 std::vector<Tensor> vec2 = {
689 tf_int.zeros(/*sizes=*/{1, 2}),
690 tf_float.ones(/*sizes=*/{2, 1}),
691 };
692 TensorList list2(vec2.data(), vec2.size());
693
694 // Show that we can compare a mix of vectors and TensorLists.
695 EXPECT_TENSOR_LISTS_CLOSE_AND_EQUAL(list1, list2);
696 EXPECT_TENSOR_LISTS_CLOSE_AND_EQUAL(vec1, list2);
697 EXPECT_TENSOR_LISTS_CLOSE_AND_EQUAL(list1, vec2);
698 EXPECT_TENSOR_LISTS_CLOSE_AND_EQUAL(vec1, vec2);
699 }
700
TEST(TensorUtilTest,EmptyTensorListsAreCloseAndEqual)701 TEST(TensorUtilTest, EmptyTensorListsAreCloseAndEqual) {
702 // Two empty lists.
703 TensorList list1;
704 EXPECT_EQ(list1.size(), 0);
705 TensorList list2;
706 EXPECT_EQ(list2.size(), 0);
707
708 EXPECT_TENSOR_LISTS_CLOSE_AND_EQUAL(list1, list2);
709 }
710
TEST(TensorUtilTest,TensorListsCloseButNotEqual)711 TEST(TensorUtilTest, TensorListsCloseButNotEqual) {
712 TensorFactory<ScalarType::Int> tf_int;
713 TensorFactory<ScalarType::Float> tf_float;
714
715 // Two lists of tensors that should be close and equal. Elements have
716 // different shapes and dtypes.
717 std::vector<Tensor> vec1 = {
718 tf_int.zeros(/*sizes=*/{1, 2}),
719 tf_float.ones(/*sizes=*/{2, 1}),
720 };
721 TensorList list1(vec1.data(), vec1.size());
722 std::vector<Tensor> vec2 = {
723 tf_int.zeros(/*sizes=*/{1, 2}),
724 tf_float.ones(/*sizes=*/{2, 1}),
725 };
726 TensorList list2(vec2.data(), vec2.size());
727
728 // Tweak a float value slightly.
729 vec1[1].mutable_data_ptr<float>()[0] = std::nextafter(1.0f, 100.0f);
730
731 // Show that we can compare a mix of vectors and TensorLists.
732 EXPECT_TENSOR_LISTS_CLOSE_BUT_NOT_EQUAL(list1, list2);
733 }
734
TEST(TensorUtilTest,TensorListsWithDifferentDataAreNotCloseOrEqual)735 TEST(TensorUtilTest, TensorListsWithDifferentDataAreNotCloseOrEqual) {
736 TensorFactory<ScalarType::Int> tf_int;
737 TensorFactory<ScalarType::Float> tf_float;
738
739 std::vector<Tensor> vec1 = {
740 tf_int.zeros(/*sizes=*/{1, 2}),
741 tf_float.ones(/*sizes=*/{2, 1}),
742 };
743 TensorList list1(vec1.data(), vec1.size());
744
745 std::vector<Tensor> vec2 = {
746 tf_int.zeros(/*sizes=*/{1, 2}),
747 tf_float.zeros(/*sizes=*/{2, 1}), // vs. ones() in the first list.
748 };
749 TensorList list2(vec2.data(), vec2.size());
750
751 EXPECT_TENSOR_LISTS_NOT_CLOSE_OR_EQUAL(list1, list2);
752 }
753
TEST(TensorUtilTest,TensorListsWithDifferentLengthsAreNotCloseOrEqual)754 TEST(TensorUtilTest, TensorListsWithDifferentLengthsAreNotCloseOrEqual) {
755 TensorFactory<ScalarType::Int> tf_int;
756 TensorFactory<ScalarType::Float> tf_float;
757
758 std::vector<Tensor> vec1 = {
759 tf_int.zeros(/*sizes=*/{1, 2}),
760 tf_float.ones(/*sizes=*/{2, 1}),
761 };
762 TensorList list1(vec1.data(), vec1.size());
763
764 std::vector<Tensor> vec2 = {
765 tf_int.zeros(/*sizes=*/{1, 2}),
766 // Missing second element.
767 };
768 TensorList list2(vec2.data(), vec2.size());
769
770 EXPECT_TENSOR_LISTS_NOT_CLOSE_OR_EQUAL(list1, list2);
771 }
772
773 // We don't need to test the ATen operator<<() implementations since they're
774 // tested elsewhere.
775 #ifndef USE_ATEN_LIB
776
777 // Printing/formatting helpers.
778
TEST(TensorUtilTest,ScalarTypeStreamSmokeTest)779 TEST(TensorUtilTest, ScalarTypeStreamSmokeTest) {
780 // Don't test everything, since operator<<(ScalarType) is just a wrapper
781 // around a separately-tested function. Just demonstrate that the stream
782 // wrapper works, and gives us a little more information for unknown types.
783 {
784 std::stringstream out;
785 out << ScalarType::Byte;
786 EXPECT_STREQ(out.str().c_str(), "Byte");
787 }
788 {
789 std::stringstream out;
790 out << static_cast<ScalarType>(127);
791 EXPECT_STREQ(out.str().c_str(), "Unknown(127)");
792 }
793 }
794
TEST(TensorUtilTest,TensorStreamInt)795 TEST(TensorUtilTest, TensorStreamInt) {
796 TensorFactory<ScalarType::Int> tf;
797
798 Tensor t = tf.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 4, 8});
799
800 std::stringstream out;
801 out << t;
802 EXPECT_STREQ(
803 out.str().c_str(), "ETensor(sizes={2, 2}, dtype=Int, data={1, 2, 4, 8})");
804 }
805
TEST(TensorUtilTest,TensorStreamDouble)806 TEST(TensorUtilTest, TensorStreamDouble) {
807 TensorFactory<ScalarType::Double> tf;
808
809 Tensor t = tf.make(/*sizes=*/{2, 2}, /*data=*/{1.1, 2.2, 4.4, 8.8});
810
811 std::stringstream out;
812 out << t;
813 EXPECT_STREQ(
814 out.str().c_str(),
815 "ETensor(sizes={2, 2}, dtype=Double, data={1.1, 2.2, 4.4, 8.8})");
816 }
817
TEST(TensorUtilTest,TensorStreamBool)818 TEST(TensorUtilTest, TensorStreamBool) {
819 TensorFactory<ScalarType::Bool> tf;
820
821 Tensor t = tf.make(/*sizes=*/{2, 2}, /*data=*/{true, false, true, false});
822
823 std::stringstream out;
824 out << t;
825 EXPECT_STREQ(
826 out.str().c_str(),
827 "ETensor(sizes={2, 2}, dtype=Bool, data={1, 0, 1, 0})");
828 }
829
TEST(TensorTest,TestZeroShapeTensorEquality)830 TEST(TensorTest, TestZeroShapeTensorEquality) {
831 TensorImpl::SizesType sizes[2] = {2, 2};
832 TensorImpl::StridesType strides[2] = {2, 1};
833 TensorImpl::DimOrderType dim_order[2] = {0, 1};
834
835 TensorImpl t1(ScalarType::Float, 2, sizes, nullptr, dim_order, strides);
836 TensorImpl t2(ScalarType::Float, 2, sizes, nullptr, dim_order, strides);
837
838 ET_EXPECT_DEATH({ EXPECT_TENSOR_EQ(Tensor(&t1), Tensor(&t2)); }, "");
839
840 float data[] = {1.0, 2.0, 3.0, 4.0};
841
842 t1.set_data(data);
843 t2.set_data(data);
844
845 EXPECT_TENSOR_EQ(Tensor(&t1), Tensor(&t2));
846 }
847
848 #endif // !USE_ATEN_LIB
849