1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2014 Benoit Steiner <[email protected]>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
10 #include <numeric>
11
12 #include "main.h"
13
14 #include <Eigen/CXX11/Tensor>
15
16 using Eigen::Tensor;
17 using Eigen::RowMajor;
18
test_1d()19 static void test_1d()
20 {
21 Tensor<float, 1> vec1(6);
22 Tensor<float, 1, RowMajor> vec2(6);
23
24 vec1(0) = 4.0; vec2(0) = 0.0;
25 vec1(1) = 8.0; vec2(1) = 1.0;
26 vec1(2) = 15.0; vec2(2) = 2.0;
27 vec1(3) = 16.0; vec2(3) = 3.0;
28 vec1(4) = 23.0; vec2(4) = 4.0;
29 vec1(5) = 42.0; vec2(5) = 5.0;
30
31 float data3[6];
32 TensorMap<Tensor<float, 1>> vec3(data3, 6);
33 vec3 = vec1.sqrt();
34 float data4[6];
35 TensorMap<Tensor<float, 1, RowMajor>> vec4(data4, 6);
36 vec4 = vec2.square();
37 float data5[6];
38 TensorMap<Tensor<float, 1, RowMajor>> vec5(data5, 6);
39 vec5 = vec2.cube();
40
41 VERIFY_IS_APPROX(vec3(0), sqrtf(4.0));
42 VERIFY_IS_APPROX(vec3(1), sqrtf(8.0));
43 VERIFY_IS_APPROX(vec3(2), sqrtf(15.0));
44 VERIFY_IS_APPROX(vec3(3), sqrtf(16.0));
45 VERIFY_IS_APPROX(vec3(4), sqrtf(23.0));
46 VERIFY_IS_APPROX(vec3(5), sqrtf(42.0));
47
48 VERIFY_IS_APPROX(vec4(0), 0.0f);
49 VERIFY_IS_APPROX(vec4(1), 1.0f);
50 VERIFY_IS_APPROX(vec4(2), 2.0f * 2.0f);
51 VERIFY_IS_APPROX(vec4(3), 3.0f * 3.0f);
52 VERIFY_IS_APPROX(vec4(4), 4.0f * 4.0f);
53 VERIFY_IS_APPROX(vec4(5), 5.0f * 5.0f);
54
55 VERIFY_IS_APPROX(vec5(0), 0.0f);
56 VERIFY_IS_APPROX(vec5(1), 1.0f);
57 VERIFY_IS_APPROX(vec5(2), 2.0f * 2.0f * 2.0f);
58 VERIFY_IS_APPROX(vec5(3), 3.0f * 3.0f * 3.0f);
59 VERIFY_IS_APPROX(vec5(4), 4.0f * 4.0f * 4.0f);
60 VERIFY_IS_APPROX(vec5(5), 5.0f * 5.0f * 5.0f);
61
62 vec3 = vec1 + vec2;
63 VERIFY_IS_APPROX(vec3(0), 4.0f + 0.0f);
64 VERIFY_IS_APPROX(vec3(1), 8.0f + 1.0f);
65 VERIFY_IS_APPROX(vec3(2), 15.0f + 2.0f);
66 VERIFY_IS_APPROX(vec3(3), 16.0f + 3.0f);
67 VERIFY_IS_APPROX(vec3(4), 23.0f + 4.0f);
68 VERIFY_IS_APPROX(vec3(5), 42.0f + 5.0f);
69 }
70
test_2d()71 static void test_2d()
72 {
73 float data1[6];
74 TensorMap<Tensor<float, 2>> mat1(data1, 2, 3);
75 float data2[6];
76 TensorMap<Tensor<float, 2, RowMajor>> mat2(data2, 2, 3);
77
78 mat1(0,0) = 0.0;
79 mat1(0,1) = 1.0;
80 mat1(0,2) = 2.0;
81 mat1(1,0) = 3.0;
82 mat1(1,1) = 4.0;
83 mat1(1,2) = 5.0;
84
85 mat2(0,0) = -0.0;
86 mat2(0,1) = -1.0;
87 mat2(0,2) = -2.0;
88 mat2(1,0) = -3.0;
89 mat2(1,1) = -4.0;
90 mat2(1,2) = -5.0;
91
92 Tensor<float, 2> mat3(2,3);
93 Tensor<float, 2, RowMajor> mat4(2,3);
94 mat3 = mat1.abs();
95 mat4 = mat2.abs();
96
97 VERIFY_IS_APPROX(mat3(0,0), 0.0f);
98 VERIFY_IS_APPROX(mat3(0,1), 1.0f);
99 VERIFY_IS_APPROX(mat3(0,2), 2.0f);
100 VERIFY_IS_APPROX(mat3(1,0), 3.0f);
101 VERIFY_IS_APPROX(mat3(1,1), 4.0f);
102 VERIFY_IS_APPROX(mat3(1,2), 5.0f);
103
104 VERIFY_IS_APPROX(mat4(0,0), 0.0f);
105 VERIFY_IS_APPROX(mat4(0,1), 1.0f);
106 VERIFY_IS_APPROX(mat4(0,2), 2.0f);
107 VERIFY_IS_APPROX(mat4(1,0), 3.0f);
108 VERIFY_IS_APPROX(mat4(1,1), 4.0f);
109 VERIFY_IS_APPROX(mat4(1,2), 5.0f);
110 }
111
test_3d()112 static void test_3d()
113 {
114 Tensor<float, 3> mat1(2,3,7);
115 Tensor<float, 3, RowMajor> mat2(2,3,7);
116
117 float val = 1.0f;
118 for (int i = 0; i < 2; ++i) {
119 for (int j = 0; j < 3; ++j) {
120 for (int k = 0; k < 7; ++k) {
121 mat1(i,j,k) = val;
122 mat2(i,j,k) = val;
123 val += 1.0f;
124 }
125 }
126 }
127
128 Tensor<float, 3> mat3(2,3,7);
129 mat3 = mat1 + mat1;
130 Tensor<float, 3, RowMajor> mat4(2,3,7);
131 mat4 = mat2 * 3.14f;
132 Tensor<float, 3> mat5(2,3,7);
133 mat5 = mat1.inverse().log();
134 Tensor<float, 3, RowMajor> mat6(2,3,7);
135 mat6 = mat2.pow(0.5f) * 3.14f;
136 Tensor<float, 3> mat7(2,3,7);
137 mat7 = mat1.cwiseMax(mat5 * 2.0f).exp();
138 Tensor<float, 3, RowMajor> mat8(2,3,7);
139 mat8 = (-mat2).exp() * 3.14f;
140 Tensor<float, 3, RowMajor> mat9(2,3,7);
141 mat9 = mat2 + 3.14f;
142 Tensor<float, 3, RowMajor> mat10(2,3,7);
143 mat10 = mat2 - 3.14f;
144 Tensor<float, 3, RowMajor> mat11(2,3,7);
145 mat11 = mat2 / 3.14f;
146
147 val = 1.0f;
148 for (int i = 0; i < 2; ++i) {
149 for (int j = 0; j < 3; ++j) {
150 for (int k = 0; k < 7; ++k) {
151 VERIFY_IS_APPROX(mat3(i,j,k), val + val);
152 VERIFY_IS_APPROX(mat4(i,j,k), val * 3.14f);
153 VERIFY_IS_APPROX(mat5(i,j,k), logf(1.0f/val));
154 VERIFY_IS_APPROX(mat6(i,j,k), sqrtf(val) * 3.14f);
155 VERIFY_IS_APPROX(mat7(i,j,k), expf((std::max)(val, mat5(i,j,k) * 2.0f)));
156 VERIFY_IS_APPROX(mat8(i,j,k), expf(-val) * 3.14f);
157 VERIFY_IS_APPROX(mat9(i,j,k), val + 3.14f);
158 VERIFY_IS_APPROX(mat10(i,j,k), val - 3.14f);
159 VERIFY_IS_APPROX(mat11(i,j,k), val / 3.14f);
160 val += 1.0f;
161 }
162 }
163 }
164 }
165
test_constants()166 static void test_constants()
167 {
168 Tensor<float, 3> mat1(2,3,7);
169 Tensor<float, 3> mat2(2,3,7);
170 Tensor<float, 3> mat3(2,3,7);
171
172 float val = 1.0f;
173 for (int i = 0; i < 2; ++i) {
174 for (int j = 0; j < 3; ++j) {
175 for (int k = 0; k < 7; ++k) {
176 mat1(i,j,k) = val;
177 val += 1.0f;
178 }
179 }
180 }
181 mat2 = mat1.constant(3.14f);
182 mat3 = mat1.cwiseMax(7.3f).exp();
183
184 val = 1.0f;
185 for (int i = 0; i < 2; ++i) {
186 for (int j = 0; j < 3; ++j) {
187 for (int k = 0; k < 7; ++k) {
188 VERIFY_IS_APPROX(mat2(i,j,k), 3.14f);
189 VERIFY_IS_APPROX(mat3(i,j,k), expf((std::max)(val, 7.3f)));
190 val += 1.0f;
191 }
192 }
193 }
194 }
195
test_boolean()196 static void test_boolean()
197 {
198 const int kSize = 31;
199 Tensor<int, 1> vec(kSize);
200 std::iota(vec.data(), vec.data() + kSize, 0);
201
202 // Test ||.
203 Tensor<bool, 1> bool1 = vec < vec.constant(1) || vec > vec.constant(4);
204 for (int i = 0; i < kSize; ++i) {
205 bool expected = i < 1 || i > 4;
206 VERIFY_IS_EQUAL(bool1[i], expected);
207 }
208
209 // Test &&, including cast of operand vec.
210 Tensor<bool, 1> bool2 = vec.cast<bool>() && vec < vec.constant(4);
211 for (int i = 0; i < kSize; ++i) {
212 bool expected = bool(i) && i < 4;
213 VERIFY_IS_EQUAL(bool2[i], expected);
214 }
215
216 // Compilation tests:
217 // Test Tensor<bool> against results of cast or comparison; verifies that
218 // CoeffReturnType is set to match Op return type of bool for Unary and Binary
219 // Ops.
220 Tensor<bool, 1> bool3 = vec.cast<bool>() && bool2;
221 bool3 = vec < vec.constant(4) && bool2;
222 }
223
test_functors()224 static void test_functors()
225 {
226 Tensor<float, 3> mat1(2,3,7);
227 Tensor<float, 3> mat2(2,3,7);
228 Tensor<float, 3> mat3(2,3,7);
229
230 float val = 1.0f;
231 for (int i = 0; i < 2; ++i) {
232 for (int j = 0; j < 3; ++j) {
233 for (int k = 0; k < 7; ++k) {
234 mat1(i,j,k) = val;
235 val += 1.0f;
236 }
237 }
238 }
239 mat2 = mat1.inverse().unaryExpr(&asinf);
240 mat3 = mat1.unaryExpr(&tanhf);
241
242 val = 1.0f;
243 for (int i = 0; i < 2; ++i) {
244 for (int j = 0; j < 3; ++j) {
245 for (int k = 0; k < 7; ++k) {
246 VERIFY_IS_APPROX(mat2(i,j,k), asinf(1.0f / mat1(i,j,k)));
247 VERIFY_IS_APPROX(mat3(i,j,k), tanhf(mat1(i,j,k)));
248 val += 1.0f;
249 }
250 }
251 }
252 }
253
test_type_casting()254 static void test_type_casting()
255 {
256 Tensor<bool, 3> mat1(2,3,7);
257 Tensor<float, 3> mat2(2,3,7);
258 Tensor<double, 3> mat3(2,3,7);
259 mat1.setRandom();
260 mat2.setRandom();
261
262 mat3 = mat1.cast<double>();
263 for (int i = 0; i < 2; ++i) {
264 for (int j = 0; j < 3; ++j) {
265 for (int k = 0; k < 7; ++k) {
266 VERIFY_IS_APPROX(mat3(i,j,k), mat1(i,j,k) ? 1.0 : 0.0);
267 }
268 }
269 }
270
271 mat3 = mat2.cast<double>();
272 for (int i = 0; i < 2; ++i) {
273 for (int j = 0; j < 3; ++j) {
274 for (int k = 0; k < 7; ++k) {
275 VERIFY_IS_APPROX(mat3(i,j,k), static_cast<double>(mat2(i,j,k)));
276 }
277 }
278 }
279 }
280
test_select()281 static void test_select()
282 {
283 Tensor<float, 3> selector(2,3,7);
284 Tensor<float, 3> mat1(2,3,7);
285 Tensor<float, 3> mat2(2,3,7);
286 Tensor<float, 3> result(2,3,7);
287
288 selector.setRandom();
289 mat1.setRandom();
290 mat2.setRandom();
291 result = (selector > selector.constant(0.5f)).select(mat1, mat2);
292
293 for (int i = 0; i < 2; ++i) {
294 for (int j = 0; j < 3; ++j) {
295 for (int k = 0; k < 7; ++k) {
296 VERIFY_IS_APPROX(result(i,j,k), (selector(i,j,k) > 0.5f) ? mat1(i,j,k) : mat2(i,j,k));
297 }
298 }
299 }
300 }
301
302 template <typename Scalar>
test_minmax_nan_propagation_templ()303 void test_minmax_nan_propagation_templ() {
304 for (int size = 1; size < 17; ++size) {
305 const Scalar kNaN = std::numeric_limits<Scalar>::quiet_NaN();
306 const Scalar kInf = std::numeric_limits<Scalar>::infinity();
307 const Scalar kZero(0);
308 Tensor<Scalar, 1> vec_all_nan(size);
309 Tensor<Scalar, 1> vec_one_nan(size);
310 Tensor<Scalar, 1> vec_zero(size);
311 vec_all_nan.setConstant(kNaN);
312 vec_zero.setZero();
313 vec_one_nan.setZero();
314 vec_one_nan(size/2) = kNaN;
315
316 auto verify_all_nan = [&](const Tensor<Scalar, 1>& v) {
317 for (int i = 0; i < size; ++i) {
318 VERIFY((numext::isnan)(v(i)));
319 }
320 };
321
322 auto verify_all_zero = [&](const Tensor<Scalar, 1>& v) {
323 for (int i = 0; i < size; ++i) {
324 VERIFY_IS_EQUAL(v(i), Scalar(0));
325 }
326 };
327
328 // Test NaN propagating max.
329 // max(nan, nan) = nan
330 // max(nan, 0) = nan
331 // max(0, nan) = nan
332 // max(0, 0) = 0
333 verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(kNaN));
334 verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(vec_all_nan));
335 verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(kZero));
336 verify_all_nan(vec_all_nan.template cwiseMax<PropagateNaN>(vec_zero));
337 verify_all_nan(vec_zero.template cwiseMax<PropagateNaN>(kNaN));
338 verify_all_nan(vec_zero.template cwiseMax<PropagateNaN>(vec_all_nan));
339 verify_all_zero(vec_zero.template cwiseMax<PropagateNaN>(kZero));
340 verify_all_zero(vec_zero.template cwiseMax<PropagateNaN>(vec_zero));
341
342 // Test number propagating max.
343 // max(nan, nan) = nan
344 // max(nan, 0) = 0
345 // max(0, nan) = 0
346 // max(0, 0) = 0
347 verify_all_nan(vec_all_nan.template cwiseMax<PropagateNumbers>(kNaN));
348 verify_all_nan(vec_all_nan.template cwiseMax<PropagateNumbers>(vec_all_nan));
349 verify_all_zero(vec_all_nan.template cwiseMax<PropagateNumbers>(kZero));
350 verify_all_zero(vec_all_nan.template cwiseMax<PropagateNumbers>(vec_zero));
351 verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(kNaN));
352 verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(vec_all_nan));
353 verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(kZero));
354 verify_all_zero(vec_zero.template cwiseMax<PropagateNumbers>(vec_zero));
355
356 // Test NaN propagating min.
357 // min(nan, nan) = nan
358 // min(nan, 0) = nan
359 // min(0, nan) = nan
360 // min(0, 0) = 0
361 verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(kNaN));
362 verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(vec_all_nan));
363 verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(kZero));
364 verify_all_nan(vec_all_nan.template cwiseMin<PropagateNaN>(vec_zero));
365 verify_all_nan(vec_zero.template cwiseMin<PropagateNaN>(kNaN));
366 verify_all_nan(vec_zero.template cwiseMin<PropagateNaN>(vec_all_nan));
367 verify_all_zero(vec_zero.template cwiseMin<PropagateNaN>(kZero));
368 verify_all_zero(vec_zero.template cwiseMin<PropagateNaN>(vec_zero));
369
370 // Test number propagating min.
371 // min(nan, nan) = nan
372 // min(nan, 0) = 0
373 // min(0, nan) = 0
374 // min(0, 0) = 0
375 verify_all_nan(vec_all_nan.template cwiseMin<PropagateNumbers>(kNaN));
376 verify_all_nan(vec_all_nan.template cwiseMin<PropagateNumbers>(vec_all_nan));
377 verify_all_zero(vec_all_nan.template cwiseMin<PropagateNumbers>(kZero));
378 verify_all_zero(vec_all_nan.template cwiseMin<PropagateNumbers>(vec_zero));
379 verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(kNaN));
380 verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(vec_all_nan));
381 verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(kZero));
382 verify_all_zero(vec_zero.template cwiseMin<PropagateNumbers>(vec_zero));
383
384 // Test min and max reduction
385 Tensor<Scalar, 0> val;
386 val = vec_zero.minimum();
387 VERIFY_IS_EQUAL(val(), kZero);
388 val = vec_zero.template minimum<PropagateNaN>();
389 VERIFY_IS_EQUAL(val(), kZero);
390 val = vec_zero.template minimum<PropagateNumbers>();
391 VERIFY_IS_EQUAL(val(), kZero);
392 val = vec_zero.maximum();
393 VERIFY_IS_EQUAL(val(), kZero);
394 val = vec_zero.template maximum<PropagateNaN>();
395 VERIFY_IS_EQUAL(val(), kZero);
396 val = vec_zero.template maximum<PropagateNumbers>();
397 VERIFY_IS_EQUAL(val(), kZero);
398
399 // Test NaN propagation for tensor of all NaNs.
400 val = vec_all_nan.template minimum<PropagateNaN>();
401 VERIFY((numext::isnan)(val()));
402 val = vec_all_nan.template minimum<PropagateNumbers>();
403 VERIFY_IS_EQUAL(val(), kInf);
404 val = vec_all_nan.template maximum<PropagateNaN>();
405 VERIFY((numext::isnan)(val()));
406 val = vec_all_nan.template maximum<PropagateNumbers>();
407 VERIFY_IS_EQUAL(val(), -kInf);
408
409 // Test NaN propagation for tensor with a single NaN.
410 val = vec_one_nan.template minimum<PropagateNaN>();
411 VERIFY((numext::isnan)(val()));
412 val = vec_one_nan.template minimum<PropagateNumbers>();
413 VERIFY_IS_EQUAL(val(), (size == 1 ? kInf : kZero));
414 val = vec_one_nan.template maximum<PropagateNaN>();
415 VERIFY((numext::isnan)(val()));
416 val = vec_one_nan.template maximum<PropagateNumbers>();
417 VERIFY_IS_EQUAL(val(), (size == 1 ? -kInf : kZero));
418 }
419 }
420
test_clip()421 static void test_clip()
422 {
423 Tensor<float, 1> vec(6);
424 vec(0) = 4.0;
425 vec(1) = 8.0;
426 vec(2) = 15.0;
427 vec(3) = 16.0;
428 vec(4) = 23.0;
429 vec(5) = 42.0;
430
431 float kMin = 20;
432 float kMax = 30;
433
434 Tensor<float, 1> vec_clipped(6);
435 vec_clipped = vec.clip(kMin, kMax);
436 for (int i = 0; i < 6; ++i) {
437 VERIFY_IS_EQUAL(vec_clipped(i), numext::mini(numext::maxi(vec(i), kMin), kMax));
438 }
439 }
440
test_minmax_nan_propagation()441 static void test_minmax_nan_propagation()
442 {
443 test_minmax_nan_propagation_templ<float>();
444 test_minmax_nan_propagation_templ<double>();
445 }
446
EIGEN_DECLARE_TEST(cxx11_tensor_expr)447 EIGEN_DECLARE_TEST(cxx11_tensor_expr)
448 {
449 CALL_SUBTEST(test_1d());
450 CALL_SUBTEST(test_2d());
451 CALL_SUBTEST(test_3d());
452 CALL_SUBTEST(test_constants());
453 CALL_SUBTEST(test_boolean());
454 CALL_SUBTEST(test_functors());
455 CALL_SUBTEST(test_type_casting());
456 CALL_SUBTEST(test_select());
457 CALL_SUBTEST(test_clip());
458
459 // Nan propagation does currently not work like one would expect from std::max/std::min,
460 // so we disable it for now
461 #if !EIGEN_ARCH_ARM_OR_ARM64
462 CALL_SUBTEST(test_minmax_nan_propagation());
463 #endif
464 }
465