1 // This file is part of Eigen, a lightweight C++ template library 2 // for linear algebra. 3 // 4 // Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog (at) gmail.com> 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 "main.h" 11 12 #include <Eigen/CXX11/Tensor> 13 14 using Eigen::Tensor; 15 16 template<typename> 17 static void test_simple_reshape() 18 { 19 Tensor<float, 5> tensor1(2,3,1,7,1); 20 tensor1.setRandom(); 21 22 Tensor<float, 3> tensor2(2,3,7); 23 Tensor<float, 2> tensor3(6,7); 24 Tensor<float, 2> tensor4(2,21); 25 26 Tensor<float, 3>::Dimensions dim1(2,3,7); 27 tensor2 = tensor1.reshape(dim1); 28 Tensor<float, 2>::Dimensions dim2(6,7); 29 tensor3 = tensor1.reshape(dim2); 30 Tensor<float, 2>::Dimensions dim3(2,21); 31 tensor4 = tensor1.reshape(dim1).reshape(dim3); 32 33 for (int i = 0; i < 2; ++i) { 34 for (int j = 0; j < 3; ++j) { 35 for (int k = 0; k < 7; ++k) { 36 VERIFY_IS_EQUAL(tensor1(i,j,0,k,0), tensor2(i,j,k)); 37 VERIFY_IS_EQUAL(tensor1(i,j,0,k,0), tensor3(i+2*j,k)); 38 VERIFY_IS_EQUAL(tensor1(i,j,0,k,0), tensor4(i,j+3*k)); 39 } 40 } 41 } 42 } 43 44 template<typename> 45 static void test_reshape_in_expr() { 46 MatrixXf m1(2,3*5*7*11); 47 MatrixXf m2(3*5*7*11,13); 48 m1.setRandom(); 49 m2.setRandom(); 50 MatrixXf m3 = m1 * m2; 51 52 TensorMap<Tensor<float, 5>> tensor1(m1.data(), 2,3,5,7,11); 53 TensorMap<Tensor<float, 5>> tensor2(m2.data(), 3,5,7,11,13); 54 Tensor<float, 2>::Dimensions newDims1(2,3*5*7*11); 55 Tensor<float, 2>::Dimensions newDims2(3*5*7*11,13); 56 typedef Tensor<float, 1>::DimensionPair DimPair; 57 array<DimPair, 1> contract_along{{DimPair(1, 0)}}; 58 Tensor<float, 2> tensor3(2,13); 59 tensor3 = tensor1.reshape(newDims1).contract(tensor2.reshape(newDims2), contract_along); 60 61 Map<MatrixXf> res(tensor3.data(), 2, 13); 62 for (int i = 0; i < 2; ++i) { 63 for (int j = 0; j < 13; ++j) { 64 VERIFY_IS_APPROX(res(i,j), m3(i,j)); 65 } 66 } 67 } 68 69 template<typename> 70 static void test_reshape_as_lvalue() 71 { 72 Tensor<float, 3> tensor(2,3,7); 73 tensor.setRandom(); 74 75 Tensor<float, 2> tensor2d(6,7); 76 Tensor<float, 3>::Dimensions dim(2,3,7); 77 tensor2d.reshape(dim) = tensor; 78 79 float scratch[2*3*1*7*1]; 80 TensorMap<Tensor<float, 5>> tensor5d(scratch, 2,3,1,7,1); 81 tensor5d.reshape(dim).device(Eigen::DefaultDevice()) = tensor; 82 83 for (int i = 0; i < 2; ++i) { 84 for (int j = 0; j < 3; ++j) { 85 for (int k = 0; k < 7; ++k) { 86 VERIFY_IS_EQUAL(tensor2d(i+2*j,k), tensor(i,j,k)); 87 VERIFY_IS_EQUAL(tensor5d(i,j,0,k,0), tensor(i,j,k)); 88 } 89 } 90 } 91 } 92 93 template<int DataLayout> 94 static void test_simple_slice() 95 { 96 Tensor<float, 5, DataLayout> tensor(2,3,5,7,11); 97 tensor.setRandom(); 98 99 Tensor<float, 5, DataLayout> slice1(1,1,1,1,1); 100 Eigen::DSizes<ptrdiff_t, 5> indices(1,2,3,4,5); 101 Eigen::DSizes<ptrdiff_t, 5> sizes(1,1,1,1,1); 102 slice1 = tensor.slice(indices, sizes); 103 VERIFY_IS_EQUAL(slice1(0,0,0,0,0), tensor(1,2,3,4,5)); 104 105 Tensor<float, 5, DataLayout> slice2(1,1,2,2,3); 106 Eigen::DSizes<ptrdiff_t, 5> indices2(1,1,3,4,5); 107 Eigen::DSizes<ptrdiff_t, 5> sizes2(1,1,2,2,3); 108 slice2 = tensor.slice(indices2, sizes2); 109 for (int i = 0; i < 2; ++i) { 110 for (int j = 0; j < 2; ++j) { 111 for (int k = 0; k < 3; ++k) { 112 VERIFY_IS_EQUAL(slice2(0,0,i,j,k), tensor(1,1,3+i,4+j,5+k)); 113 } 114 } 115 } 116 } 117 118 template<typename=void> 119 static void test_const_slice() 120 { 121 const float b[1] = {42}; 122 TensorMap<Tensor<const float, 1> > m(b, 1); 123 DSizes<DenseIndex, 1> offsets; 124 offsets[0] = 0; 125 TensorRef<Tensor<const float, 1> > slice_ref(m.slice(offsets, m.dimensions())); 126 VERIFY_IS_EQUAL(slice_ref(0), 42); 127 } 128 129 template<int DataLayout> 130 static void test_slice_in_expr() { 131 typedef Matrix<float, Dynamic, Dynamic, DataLayout> Mtx; 132 Mtx m1(7,7); 133 Mtx m2(3,3); 134 m1.setRandom(); 135 m2.setRandom(); 136 137 Mtx m3 = m1.block(1, 2, 3, 3) * m2.block(0, 2, 3, 1); 138 139 TensorMap<Tensor<float, 2, DataLayout>> tensor1(m1.data(), 7, 7); 140 TensorMap<Tensor<float, 2, DataLayout>> tensor2(m2.data(), 3, 3); 141 Tensor<float, 2, DataLayout> tensor3(3,1); 142 typedef Tensor<float, 1>::DimensionPair DimPair; 143 array<DimPair, 1> contract_along{{DimPair(1, 0)}}; 144 145 Eigen::DSizes<ptrdiff_t, 2> indices1(1,2); 146 Eigen::DSizes<ptrdiff_t, 2> sizes1(3,3); 147 Eigen::DSizes<ptrdiff_t, 2> indices2(0,2); 148 Eigen::DSizes<ptrdiff_t, 2> sizes2(3,1); 149 tensor3 = tensor1.slice(indices1, sizes1).contract(tensor2.slice(indices2, sizes2), contract_along); 150 151 Map<Mtx> res(tensor3.data(), 3, 1); 152 for (int i = 0; i < 3; ++i) { 153 for (int j = 0; j < 1; ++j) { 154 VERIFY_IS_APPROX(res(i,j), m3(i,j)); 155 } 156 } 157 158 // Take an arbitrary slice of an arbitrarily sized tensor. 159 TensorMap<Tensor<const float, 2, DataLayout>> tensor4(m1.data(), 7, 7); 160 Tensor<float, 1, DataLayout> tensor6 = tensor4.reshape(DSizes<ptrdiff_t, 1>(7*7)).exp().slice(DSizes<ptrdiff_t, 1>(0), DSizes<ptrdiff_t, 1>(35)); 161 for (int i = 0; i < 35; ++i) { 162 VERIFY_IS_APPROX(tensor6(i), expf(tensor4.data()[i])); 163 } 164 } 165 166 template<int DataLayout> 167 static void test_slice_as_lvalue() 168 { 169 Tensor<float, 3, DataLayout> tensor1(2,2,7); 170 tensor1.setRandom(); 171 Tensor<float, 3, DataLayout> tensor2(2,2,7); 172 tensor2.setRandom(); 173 Tensor<float, 3, DataLayout> tensor3(4,3,5); 174 tensor3.setRandom(); 175 Tensor<float, 3, DataLayout> tensor4(4,3,2); 176 tensor4.setRandom(); 177 Tensor<float, 3, DataLayout> tensor5(10,13,12); 178 tensor5.setRandom(); 179 180 Tensor<float, 3, DataLayout> result(4,5,7); 181 Eigen::DSizes<ptrdiff_t, 3> sizes12(2,2,7); 182 Eigen::DSizes<ptrdiff_t, 3> first_slice(0,0,0); 183 result.slice(first_slice, sizes12) = tensor1; 184 Eigen::DSizes<ptrdiff_t, 3> second_slice(2,0,0); 185 result.slice(second_slice, sizes12).device(Eigen::DefaultDevice()) = tensor2; 186 187 Eigen::DSizes<ptrdiff_t, 3> sizes3(4,3,5); 188 Eigen::DSizes<ptrdiff_t, 3> third_slice(0,2,0); 189 result.slice(third_slice, sizes3) = tensor3; 190 191 Eigen::DSizes<ptrdiff_t, 3> sizes4(4,3,2); 192 Eigen::DSizes<ptrdiff_t, 3> fourth_slice(0,2,5); 193 result.slice(fourth_slice, sizes4) = tensor4; 194 195 for (int j = 0; j < 2; ++j) { 196 for (int k = 0; k < 7; ++k) { 197 for (int i = 0; i < 2; ++i) { 198 VERIFY_IS_EQUAL(result(i,j,k), tensor1(i,j,k)); 199 VERIFY_IS_EQUAL(result(i+2,j,k), tensor2(i,j,k)); 200 } 201 } 202 } 203 for (int i = 0; i < 4; ++i) { 204 for (int j = 2; j < 5; ++j) { 205 for (int k = 0; k < 5; ++k) { 206 VERIFY_IS_EQUAL(result(i,j,k), tensor3(i,j-2,k)); 207 } 208 for (int k = 5; k < 7; ++k) { 209 VERIFY_IS_EQUAL(result(i,j,k), tensor4(i,j-2,k-5)); 210 } 211 } 212 } 213 214 Eigen::DSizes<ptrdiff_t, 3> sizes5(4,5,7); 215 Eigen::DSizes<ptrdiff_t, 3> fifth_slice(0,0,0); 216 result.slice(fifth_slice, sizes5) = tensor5.slice(fifth_slice, sizes5); 217 for (int i = 0; i < 4; ++i) { 218 for (int j = 2; j < 5; ++j) { 219 for (int k = 0; k < 7; ++k) { 220 VERIFY_IS_EQUAL(result(i,j,k), tensor5(i,j,k)); 221 } 222 } 223 } 224 } 225 226 template<int DataLayout> 227 static void test_slice_raw_data() 228 { 229 Tensor<float, 4, DataLayout> tensor(3,5,7,11); 230 tensor.setRandom(); 231 232 Eigen::DSizes<ptrdiff_t, 4> offsets(1,2,3,4); 233 Eigen::DSizes<ptrdiff_t, 4> extents(1,1,1,1); 234 typedef TensorEvaluator<decltype(tensor.slice(offsets, extents)), DefaultDevice> SliceEvaluator; 235 auto slice1 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 236 VERIFY_IS_EQUAL(slice1.dimensions().TotalSize(), 1); 237 VERIFY_IS_EQUAL(slice1.data()[0], tensor(1,2,3,4)); 238 239 if (DataLayout == ColMajor) { 240 extents = Eigen::DSizes<ptrdiff_t, 4>(2,1,1,1); 241 auto slice2 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 242 VERIFY_IS_EQUAL(slice2.dimensions().TotalSize(), 2); 243 VERIFY_IS_EQUAL(slice2.data()[0], tensor(1,2,3,4)); 244 VERIFY_IS_EQUAL(slice2.data()[1], tensor(2,2,3,4)); 245 } else { 246 extents = Eigen::DSizes<ptrdiff_t, 4>(1,1,1,2); 247 auto slice2 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 248 VERIFY_IS_EQUAL(slice2.dimensions().TotalSize(), 2); 249 VERIFY_IS_EQUAL(slice2.data()[0], tensor(1,2,3,4)); 250 VERIFY_IS_EQUAL(slice2.data()[1], tensor(1,2,3,5)); 251 } 252 253 extents = Eigen::DSizes<ptrdiff_t, 4>(1,2,1,1); 254 auto slice3 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 255 VERIFY_IS_EQUAL(slice3.dimensions().TotalSize(), 2); 256 VERIFY_IS_EQUAL(slice3.data(), static_cast<float*>(0)); 257 258 if (DataLayout == ColMajor) { 259 offsets = Eigen::DSizes<ptrdiff_t, 4>(0,2,3,4); 260 extents = Eigen::DSizes<ptrdiff_t, 4>(3,2,1,1); 261 auto slice4 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 262 VERIFY_IS_EQUAL(slice4.dimensions().TotalSize(), 6); 263 for (int i = 0; i < 3; ++i) { 264 for (int j = 0; j < 2; ++j) { 265 VERIFY_IS_EQUAL(slice4.data()[i+3*j], tensor(i,2+j,3,4)); 266 } 267 } 268 } else { 269 offsets = Eigen::DSizes<ptrdiff_t, 4>(1,2,3,0); 270 extents = Eigen::DSizes<ptrdiff_t, 4>(1,1,2,11); 271 auto slice4 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 272 VERIFY_IS_EQUAL(slice4.dimensions().TotalSize(), 22); 273 for (int l = 0; l < 11; ++l) { 274 for (int k = 0; k < 2; ++k) { 275 VERIFY_IS_EQUAL(slice4.data()[l+11*k], tensor(1,2,3+k,l)); 276 } 277 } 278 } 279 280 if (DataLayout == ColMajor) { 281 offsets = Eigen::DSizes<ptrdiff_t, 4>(0,0,0,4); 282 extents = Eigen::DSizes<ptrdiff_t, 4>(3,5,7,2); 283 auto slice5 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 284 VERIFY_IS_EQUAL(slice5.dimensions().TotalSize(), 210); 285 for (int i = 0; i < 3; ++i) { 286 for (int j = 0; j < 5; ++j) { 287 for (int k = 0; k < 7; ++k) { 288 for (int l = 0; l < 2; ++l) { 289 int slice_index = i + 3 * (j + 5 * (k + 7 * l)); 290 VERIFY_IS_EQUAL(slice5.data()[slice_index], tensor(i,j,k,l+4)); 291 } 292 } 293 } 294 } 295 } else { 296 offsets = Eigen::DSizes<ptrdiff_t, 4>(1,0,0,0); 297 extents = Eigen::DSizes<ptrdiff_t, 4>(2,5,7,11); 298 auto slice5 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 299 VERIFY_IS_EQUAL(slice5.dimensions().TotalSize(), 770); 300 for (int l = 0; l < 11; ++l) { 301 for (int k = 0; k < 7; ++k) { 302 for (int j = 0; j < 5; ++j) { 303 for (int i = 0; i < 2; ++i) { 304 int slice_index = l + 11 * (k + 7 * (j + 5 * i)); 305 VERIFY_IS_EQUAL(slice5.data()[slice_index], tensor(i+1,j,k,l)); 306 } 307 } 308 } 309 } 310 311 } 312 313 offsets = Eigen::DSizes<ptrdiff_t, 4>(0,0,0,0); 314 extents = Eigen::DSizes<ptrdiff_t, 4>(3,5,7,11); 315 auto slice6 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice()); 316 VERIFY_IS_EQUAL(slice6.dimensions().TotalSize(), 3*5*7*11); 317 VERIFY_IS_EQUAL(slice6.data(), tensor.data()); 318 } 319 320 321 template<int DataLayout> 322 static void test_strided_slice() 323 { 324 typedef Tensor<float, 5, DataLayout> Tensor5f; 325 typedef Eigen::DSizes<Eigen::DenseIndex, 5> Index5; 326 typedef Tensor<float, 2, DataLayout> Tensor2f; 327 typedef Eigen::DSizes<Eigen::DenseIndex, 2> Index2; 328 Tensor<float, 5, DataLayout> tensor(2,3,5,7,11); 329 Tensor<float, 2, DataLayout> tensor2(7,11); 330 tensor.setRandom(); 331 tensor2.setRandom(); 332 333 if (true) { 334 Tensor2f slice(2,3); 335 Index2 strides(-2,-1); 336 Index2 indicesStart(5,7); 337 Index2 indicesStop(0,4); 338 slice = tensor2.stridedSlice(indicesStart, indicesStop, strides); 339 for (int j = 0; j < 2; ++j) { 340 for (int k = 0; k < 3; ++k) { 341 VERIFY_IS_EQUAL(slice(j,k), tensor2(5-2*j,7-k)); 342 } 343 } 344 } 345 346 if(true) { 347 Tensor2f slice(0,1); 348 Index2 strides(1,1); 349 Index2 indicesStart(5,4); 350 Index2 indicesStop(5,5); 351 slice = tensor2.stridedSlice(indicesStart, indicesStop, strides); 352 } 353 354 if(true) { // test clamped degenerate interavls 355 Tensor2f slice(7,11); 356 Index2 strides(1,-1); 357 Index2 indicesStart(-3,20); // should become 0,10 358 Index2 indicesStop(20,-11); // should become 11, -1 359 slice = tensor2.stridedSlice(indicesStart, indicesStop, strides); 360 for (int j = 0; j < 7; ++j) { 361 for (int k = 0; k < 11; ++k) { 362 VERIFY_IS_EQUAL(slice(j,k), tensor2(j,10-k)); 363 } 364 } 365 } 366 367 if(true) { 368 Tensor5f slice1(1,1,1,1,1); 369 Eigen::DSizes<Eigen::DenseIndex, 5> indicesStart(1, 2, 3, 4, 5); 370 Eigen::DSizes<Eigen::DenseIndex, 5> indicesStop(2, 3, 4, 5, 6); 371 Eigen::DSizes<Eigen::DenseIndex, 5> strides(1, 1, 1, 1, 1); 372 slice1 = tensor.stridedSlice(indicesStart, indicesStop, strides); 373 VERIFY_IS_EQUAL(slice1(0,0,0,0,0), tensor(1,2,3,4,5)); 374 } 375 376 if(true) { 377 Tensor5f slice(1,1,2,2,3); 378 Index5 start(1, 1, 3, 4, 5); 379 Index5 stop(2, 2, 5, 6, 8); 380 Index5 strides(1, 1, 1, 1, 1); 381 slice = tensor.stridedSlice(start, stop, strides); 382 for (int i = 0; i < 2; ++i) { 383 for (int j = 0; j < 2; ++j) { 384 for (int k = 0; k < 3; ++k) { 385 VERIFY_IS_EQUAL(slice(0,0,i,j,k), tensor(1,1,3+i,4+j,5+k)); 386 } 387 } 388 } 389 } 390 391 if(true) { 392 Tensor5f slice(1,1,2,2,3); 393 Index5 strides3(1, 1, -2, 1, -1); 394 Index5 indices3Start(1, 1, 4, 4, 7); 395 Index5 indices3Stop(2, 2, 0, 6, 4); 396 slice = tensor.stridedSlice(indices3Start, indices3Stop, strides3); 397 for (int i = 0; i < 2; ++i) { 398 for (int j = 0; j < 2; ++j) { 399 for (int k = 0; k < 3; ++k) { 400 VERIFY_IS_EQUAL(slice(0,0,i,j,k), tensor(1,1,4-2*i,4+j,7-k)); 401 } 402 } 403 } 404 } 405 406 if(false) { // tests degenerate interval 407 Tensor5f slice(1,1,2,2,3); 408 Index5 strides3(1, 1, 2, 1, 1); 409 Index5 indices3Start(1, 1, 4, 4, 7); 410 Index5 indices3Stop(2, 2, 0, 6, 4); 411 slice = tensor.stridedSlice(indices3Start, indices3Stop, strides3); 412 } 413 } 414 415 template<int DataLayout> 416 static void test_strided_slice_write() 417 { 418 typedef Tensor<float, 2, DataLayout> Tensor2f; 419 typedef Eigen::DSizes<Eigen::DenseIndex, 2> Index2; 420 421 Tensor<float, 2, DataLayout> tensor(7,11),tensor2(7,11); 422 tensor.setRandom(); 423 tensor2=tensor; 424 Tensor2f slice(2,3); 425 426 slice.setRandom(); 427 428 Index2 strides(1,1); 429 Index2 indicesStart(3,4); 430 Index2 indicesStop(5,7); 431 Index2 lengths(2,3); 432 433 tensor.slice(indicesStart,lengths)=slice; 434 tensor2.stridedSlice(indicesStart,indicesStop,strides)=slice; 435 436 for(int i=0;i<7;i++) for(int j=0;j<11;j++){ 437 VERIFY_IS_EQUAL(tensor(i,j), tensor2(i,j)); 438 } 439 } 440 441 442 template<int DataLayout> 443 static void test_composition() 444 { 445 Eigen::Tensor<float, 2, DataLayout> matrix(7, 11); 446 matrix.setRandom(); 447 448 const DSizes<ptrdiff_t, 3> newDims(1, 1, 11); 449 Eigen::Tensor<float, 3, DataLayout> tensor = 450 matrix.slice(DSizes<ptrdiff_t, 2>(2, 0), DSizes<ptrdiff_t, 2>(1, 11)).reshape(newDims); 451 452 VERIFY_IS_EQUAL(tensor.dimensions().TotalSize(), 11); 453 VERIFY_IS_EQUAL(tensor.dimension(0), 1); 454 VERIFY_IS_EQUAL(tensor.dimension(1), 1); 455 VERIFY_IS_EQUAL(tensor.dimension(2), 11); 456 for (int i = 0; i < 11; ++i) { 457 VERIFY_IS_EQUAL(tensor(0,0,i), matrix(2,i)); 458 } 459 } 460 461 462 void test_cxx11_tensor_morphing() 463 { 464 CALL_SUBTEST_1(test_simple_reshape<void>()); 465 CALL_SUBTEST_1(test_reshape_in_expr<void>()); 466 CALL_SUBTEST_1(test_reshape_as_lvalue<void>()); 467 468 CALL_SUBTEST_1(test_simple_slice<ColMajor>()); 469 CALL_SUBTEST_1(test_simple_slice<RowMajor>()); 470 CALL_SUBTEST_1(test_const_slice()); 471 CALL_SUBTEST_2(test_slice_in_expr<ColMajor>()); 472 CALL_SUBTEST_3(test_slice_in_expr<RowMajor>()); 473 CALL_SUBTEST_4(test_slice_as_lvalue<ColMajor>()); 474 CALL_SUBTEST_4(test_slice_as_lvalue<RowMajor>()); 475 CALL_SUBTEST_5(test_slice_raw_data<ColMajor>()); 476 CALL_SUBTEST_5(test_slice_raw_data<RowMajor>()); 477 478 CALL_SUBTEST_6(test_strided_slice_write<ColMajor>()); 479 CALL_SUBTEST_6(test_strided_slice<ColMajor>()); 480 CALL_SUBTEST_6(test_strided_slice_write<RowMajor>()); 481 CALL_SUBTEST_6(test_strided_slice<RowMajor>()); 482 483 CALL_SUBTEST_7(test_composition<ColMajor>()); 484 CALL_SUBTEST_7(test_composition<RowMajor>()); 485 } 486