cart-elc

Source code for CART-ELC
git clone git://git.laack.co/cart-elc.git
Log | Files | Refs | README | LICENSE

cxx11_tensor_reduction.cpp (15346B)


      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@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 #include <limits>
     12 #include <numeric>
     13 #include <Eigen/CXX11/Tensor>
     14 
     15 using Eigen::Tensor;
     16 
     17 template <int DataLayout>
     18 static void test_trivial_reductions() {
     19   {
     20     Tensor<float, 0, DataLayout> tensor;
     21     tensor.setRandom();
     22     array<ptrdiff_t, 0> reduction_axis;
     23 
     24     Tensor<float, 0, DataLayout> result = tensor.sum(reduction_axis);
     25     VERIFY_IS_EQUAL(result(), tensor());
     26   }
     27 
     28   {
     29     Tensor<float, 1, DataLayout> tensor(7);
     30     tensor.setRandom();
     31     array<ptrdiff_t, 0> reduction_axis;
     32 
     33     Tensor<float, 1, DataLayout> result = tensor.sum(reduction_axis);
     34     VERIFY_IS_EQUAL(result.dimension(0), 7);
     35     for (int i = 0; i < 7; ++i) {
     36       VERIFY_IS_EQUAL(result(i), tensor(i));
     37     }
     38   }
     39 
     40   {
     41     Tensor<float, 2, DataLayout> tensor(2, 3);
     42     tensor.setRandom();
     43     array<ptrdiff_t, 0> reduction_axis;
     44 
     45     Tensor<float, 2, DataLayout> result = tensor.sum(reduction_axis);
     46     VERIFY_IS_EQUAL(result.dimension(0), 2);
     47     VERIFY_IS_EQUAL(result.dimension(1), 3);
     48     for (int i = 0; i < 2; ++i) {
     49       for (int j = 0; j < 3; ++j) {
     50         VERIFY_IS_EQUAL(result(i, j), tensor(i, j));
     51       }
     52     }
     53   }
     54 }
     55 
     56 template <typename Scalar,int DataLayout>
     57 static void test_simple_reductions() {
     58   Tensor<Scalar, 4, DataLayout> tensor(2, 3, 5, 7);
     59   tensor.setRandom();
     60   // Add a little offset so that the product reductions won't be close to zero.
     61   tensor += tensor.constant(Scalar(0.5f));
     62   array<ptrdiff_t, 2> reduction_axis2;
     63   reduction_axis2[0] = 1;
     64   reduction_axis2[1] = 3;
     65 
     66   Tensor<Scalar, 2, DataLayout> result = tensor.sum(reduction_axis2);
     67   VERIFY_IS_EQUAL(result.dimension(0), 2);
     68   VERIFY_IS_EQUAL(result.dimension(1), 5);
     69   for (int i = 0; i < 2; ++i) {
     70     for (int j = 0; j < 5; ++j) {
     71       Scalar sum = Scalar(0.0f);
     72       for (int k = 0; k < 3; ++k) {
     73         for (int l = 0; l < 7; ++l) {
     74           sum += tensor(i, k, j, l);
     75         }
     76       }
     77       VERIFY_IS_APPROX(result(i, j), sum);
     78     }
     79   }
     80 
     81   {
     82     Tensor<Scalar, 0, DataLayout> sum1 = tensor.sum();
     83     VERIFY_IS_EQUAL(sum1.rank(), 0);
     84 
     85     array<ptrdiff_t, 4> reduction_axis4;
     86     reduction_axis4[0] = 0;
     87     reduction_axis4[1] = 1;
     88     reduction_axis4[2] = 2;
     89     reduction_axis4[3] = 3;
     90     Tensor<Scalar, 0, DataLayout> sum2 = tensor.sum(reduction_axis4);
     91     VERIFY_IS_EQUAL(sum2.rank(), 0);
     92 
     93     VERIFY_IS_APPROX(sum1(), sum2());
     94   }
     95 
     96   reduction_axis2[0] = 0;
     97   reduction_axis2[1] = 2;
     98   result = tensor.prod(reduction_axis2);
     99   VERIFY_IS_EQUAL(result.dimension(0), 3);
    100   VERIFY_IS_EQUAL(result.dimension(1), 7);
    101   for (int i = 0; i < 3; ++i) {
    102     for (int j = 0; j < 7; ++j) {
    103       Scalar prod = Scalar(1.0f);
    104       for (int k = 0; k < 2; ++k) {
    105         for (int l = 0; l < 5; ++l) {
    106           prod *= tensor(k, i, l, j);
    107         }
    108       }
    109       VERIFY_IS_APPROX(result(i, j), prod);
    110     }
    111   }
    112 
    113   {
    114     Tensor<Scalar, 0, DataLayout> prod1 = tensor.prod();
    115     VERIFY_IS_EQUAL(prod1.rank(), 0);
    116 
    117     array<ptrdiff_t, 4> reduction_axis4;
    118     reduction_axis4[0] = 0;
    119     reduction_axis4[1] = 1;
    120     reduction_axis4[2] = 2;
    121     reduction_axis4[3] = 3;
    122     Tensor<Scalar, 0, DataLayout> prod2 = tensor.prod(reduction_axis4);
    123     VERIFY_IS_EQUAL(prod2.rank(), 0);
    124 
    125     VERIFY_IS_APPROX(prod1(), prod2());
    126   }
    127 
    128   reduction_axis2[0] = 0;
    129   reduction_axis2[1] = 2;
    130   result = tensor.maximum(reduction_axis2);
    131   VERIFY_IS_EQUAL(result.dimension(0), 3);
    132   VERIFY_IS_EQUAL(result.dimension(1), 7);
    133   for (int i = 0; i < 3; ++i) {
    134     for (int j = 0; j < 7; ++j) {
    135       Scalar max_val = std::numeric_limits<Scalar>::lowest();
    136       for (int k = 0; k < 2; ++k) {
    137         for (int l = 0; l < 5; ++l) {
    138           max_val = (std::max)(max_val, tensor(k, i, l, j));
    139         }
    140       }
    141       VERIFY_IS_APPROX(result(i, j), max_val);
    142     }
    143   }
    144 
    145   {
    146     Tensor<Scalar, 0, DataLayout> max1 = tensor.maximum();
    147     VERIFY_IS_EQUAL(max1.rank(), 0);
    148 
    149     array<ptrdiff_t, 4> reduction_axis4;
    150     reduction_axis4[0] = 0;
    151     reduction_axis4[1] = 1;
    152     reduction_axis4[2] = 2;
    153     reduction_axis4[3] = 3;
    154     Tensor<Scalar, 0, DataLayout> max2 = tensor.maximum(reduction_axis4);
    155     VERIFY_IS_EQUAL(max2.rank(), 0);
    156 
    157     VERIFY_IS_APPROX(max1(), max2());
    158   }
    159 
    160   reduction_axis2[0] = 0;
    161   reduction_axis2[1] = 1;
    162   result = tensor.minimum(reduction_axis2);
    163   VERIFY_IS_EQUAL(result.dimension(0), 5);
    164   VERIFY_IS_EQUAL(result.dimension(1), 7);
    165   for (int i = 0; i < 5; ++i) {
    166     for (int j = 0; j < 7; ++j) {
    167       Scalar min_val = (std::numeric_limits<Scalar>::max)();
    168       for (int k = 0; k < 2; ++k) {
    169         for (int l = 0; l < 3; ++l) {
    170           min_val = (std::min)(min_val, tensor(k, l, i, j));
    171         }
    172       }
    173       VERIFY_IS_APPROX(result(i, j), min_val);
    174     }
    175   }
    176 
    177   {
    178     Tensor<Scalar, 0, DataLayout> min1 = tensor.minimum();
    179     VERIFY_IS_EQUAL(min1.rank(), 0);
    180 
    181     array<ptrdiff_t, 4> reduction_axis4;
    182     reduction_axis4[0] = 0;
    183     reduction_axis4[1] = 1;
    184     reduction_axis4[2] = 2;
    185     reduction_axis4[3] = 3;
    186     Tensor<Scalar, 0, DataLayout> min2 = tensor.minimum(reduction_axis4);
    187     VERIFY_IS_EQUAL(min2.rank(), 0);
    188 
    189     VERIFY_IS_APPROX(min1(), min2());
    190   }
    191 
    192   reduction_axis2[0] = 0;
    193   reduction_axis2[1] = 1;
    194   result = tensor.mean(reduction_axis2);
    195   VERIFY_IS_EQUAL(result.dimension(0), 5);
    196   VERIFY_IS_EQUAL(result.dimension(1), 7);
    197   for (int i = 0; i < 5; ++i) {
    198     for (int j = 0; j < 7; ++j) {
    199       Scalar sum = Scalar(0.0f);
    200       int count = 0;
    201       for (int k = 0; k < 2; ++k) {
    202         for (int l = 0; l < 3; ++l) {
    203           sum += tensor(k, l, i, j);
    204           ++count;
    205         }
    206       }
    207       VERIFY_IS_APPROX(result(i, j), sum / Scalar(count));
    208     }
    209   }
    210 
    211   {
    212     Tensor<Scalar, 0, DataLayout> mean1 = tensor.mean();
    213     VERIFY_IS_EQUAL(mean1.rank(), 0);
    214 
    215     array<ptrdiff_t, 4> reduction_axis4;
    216     reduction_axis4[0] = 0;
    217     reduction_axis4[1] = 1;
    218     reduction_axis4[2] = 2;
    219     reduction_axis4[3] = 3;
    220     Tensor<Scalar, 0, DataLayout> mean2 = tensor.mean(reduction_axis4);
    221     VERIFY_IS_EQUAL(mean2.rank(), 0);
    222 
    223     VERIFY_IS_APPROX(mean1(), mean2());
    224   }
    225 
    226   {
    227     Tensor<int, 1> ints(10);
    228     std::iota(ints.data(), ints.data() + ints.dimension(0), 0);
    229 
    230     TensorFixedSize<bool, Sizes<> > all_;
    231     all_ = ints.all();
    232     VERIFY(!all_());
    233     all_ = (ints >= ints.constant(0)).all();
    234     VERIFY(all_());
    235 
    236     TensorFixedSize<bool, Sizes<> > any;
    237     any = (ints > ints.constant(10)).any();
    238     VERIFY(!any());
    239     any = (ints < ints.constant(1)).any();
    240     VERIFY(any());
    241   }
    242 }
    243 
    244 
    245 template <int DataLayout>
    246 static void test_reductions_in_expr() {
    247   Tensor<float, 4, DataLayout> tensor(2, 3, 5, 7);
    248   tensor.setRandom();
    249   array<ptrdiff_t, 2> reduction_axis2;
    250   reduction_axis2[0] = 1;
    251   reduction_axis2[1] = 3;
    252 
    253   Tensor<float, 2, DataLayout> result(2, 5);
    254   result = result.constant(1.0f) - tensor.sum(reduction_axis2);
    255   VERIFY_IS_EQUAL(result.dimension(0), 2);
    256   VERIFY_IS_EQUAL(result.dimension(1), 5);
    257   for (int i = 0; i < 2; ++i) {
    258     for (int j = 0; j < 5; ++j) {
    259       float sum = 0.0f;
    260       for (int k = 0; k < 3; ++k) {
    261         for (int l = 0; l < 7; ++l) {
    262           sum += tensor(i, k, j, l);
    263         }
    264       }
    265       VERIFY_IS_APPROX(result(i, j), 1.0f - sum);
    266     }
    267   }
    268 }
    269 
    270 
    271 template <int DataLayout>
    272 static void test_full_reductions() {
    273   Tensor<float, 2, DataLayout> tensor(2, 3);
    274   tensor.setRandom();
    275   array<ptrdiff_t, 2> reduction_axis;
    276   reduction_axis[0] = 0;
    277   reduction_axis[1] = 1;
    278 
    279   Tensor<float, 0, DataLayout> result = tensor.sum(reduction_axis);
    280   VERIFY_IS_EQUAL(result.rank(), 0);
    281 
    282   float sum = 0.0f;
    283   for (int i = 0; i < 2; ++i) {
    284     for (int j = 0; j < 3; ++j) {
    285       sum += tensor(i, j);
    286     }
    287   }
    288   VERIFY_IS_APPROX(result(0), sum);
    289 
    290   result = tensor.square().sum(reduction_axis).sqrt();
    291   VERIFY_IS_EQUAL(result.rank(), 0);
    292 
    293   sum = 0.0f;
    294   for (int i = 0; i < 2; ++i) {
    295     for (int j = 0; j < 3; ++j) {
    296       sum += tensor(i, j) * tensor(i, j);
    297     }
    298   }
    299   VERIFY_IS_APPROX(result(), sqrtf(sum));
    300 }
    301 
    302 struct UserReducer {
    303   static const bool PacketAccess = false;
    304   UserReducer(float offset) : offset_(offset) {}
    305   void reduce(const float val, float* accum) { *accum += val * val; }
    306   float initialize() const { return 0; }
    307   float finalize(const float accum) const { return 1.0f / (accum + offset_); }
    308 
    309  private:
    310   const float offset_;
    311 };
    312 
    313 template <int DataLayout>
    314 static void test_user_defined_reductions() {
    315   Tensor<float, 2, DataLayout> tensor(5, 7);
    316   tensor.setRandom();
    317   array<ptrdiff_t, 1> reduction_axis;
    318   reduction_axis[0] = 1;
    319 
    320   UserReducer reducer(10.0f);
    321   Tensor<float, 1, DataLayout> result = tensor.reduce(reduction_axis, reducer);
    322   VERIFY_IS_EQUAL(result.dimension(0), 5);
    323   for (int i = 0; i < 5; ++i) {
    324     float expected = 10.0f;
    325     for (int j = 0; j < 7; ++j) {
    326       expected += tensor(i, j) * tensor(i, j);
    327     }
    328     expected = 1.0f / expected;
    329     VERIFY_IS_APPROX(result(i), expected);
    330   }
    331 }
    332 
    333 template <int DataLayout>
    334 static void test_tensor_maps() {
    335   int inputs[2 * 3 * 5 * 7];
    336   TensorMap<Tensor<int, 4, DataLayout> > tensor_map(inputs, 2, 3, 5, 7);
    337   TensorMap<Tensor<const int, 4, DataLayout> > tensor_map_const(inputs, 2, 3, 5,
    338                                                                 7);
    339   const TensorMap<Tensor<const int, 4, DataLayout> > tensor_map_const_const(
    340       inputs, 2, 3, 5, 7);
    341 
    342   tensor_map.setRandom();
    343   array<ptrdiff_t, 2> reduction_axis;
    344   reduction_axis[0] = 1;
    345   reduction_axis[1] = 3;
    346 
    347   Tensor<int, 2, DataLayout> result = tensor_map.sum(reduction_axis);
    348   Tensor<int, 2, DataLayout> result2 = tensor_map_const.sum(reduction_axis);
    349   Tensor<int, 2, DataLayout> result3 =
    350       tensor_map_const_const.sum(reduction_axis);
    351 
    352   for (int i = 0; i < 2; ++i) {
    353     for (int j = 0; j < 5; ++j) {
    354       int sum = 0;
    355       for (int k = 0; k < 3; ++k) {
    356         for (int l = 0; l < 7; ++l) {
    357           sum += tensor_map(i, k, j, l);
    358         }
    359       }
    360       VERIFY_IS_EQUAL(result(i, j), sum);
    361       VERIFY_IS_EQUAL(result2(i, j), sum);
    362       VERIFY_IS_EQUAL(result3(i, j), sum);
    363     }
    364   }
    365 }
    366 
    367 template <int DataLayout>
    368 static void test_static_dims() {
    369   Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
    370   Tensor<float, 2, DataLayout> out(72, 97);
    371   in.setRandom();
    372 
    373 #if !EIGEN_HAS_CONSTEXPR
    374   array<int, 2> reduction_axis;
    375   reduction_axis[0] = 1;
    376   reduction_axis[1] = 3;
    377 #else
    378   Eigen::IndexList<Eigen::type2index<1>, Eigen::type2index<3> > reduction_axis;
    379 #endif
    380 
    381   out = in.maximum(reduction_axis);
    382 
    383   for (int i = 0; i < 72; ++i) {
    384     for (int j = 0; j < 97; ++j) {
    385       float expected = -1e10f;
    386       for (int k = 0; k < 53; ++k) {
    387         for (int l = 0; l < 113; ++l) {
    388           expected = (std::max)(expected, in(i, k, j, l));
    389         }
    390       }
    391       VERIFY_IS_EQUAL(out(i, j), expected);
    392     }
    393   }
    394 }
    395 
    396 template <int DataLayout>
    397 static void test_innermost_last_dims() {
    398   Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
    399   Tensor<float, 2, DataLayout> out(97, 113);
    400   in.setRandom();
    401 
    402 // Reduce on the innermost dimensions.
    403 #if !EIGEN_HAS_CONSTEXPR
    404   array<int, 2> reduction_axis;
    405   reduction_axis[0] = 0;
    406   reduction_axis[1] = 1;
    407 #else
    408   // This triggers the use of packets for ColMajor.
    409   Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1> > reduction_axis;
    410 #endif
    411 
    412   out = in.maximum(reduction_axis);
    413 
    414   for (int i = 0; i < 97; ++i) {
    415     for (int j = 0; j < 113; ++j) {
    416       float expected = -1e10f;
    417       for (int k = 0; k < 53; ++k) {
    418         for (int l = 0; l < 72; ++l) {
    419           expected = (std::max)(expected, in(l, k, i, j));
    420         }
    421       }
    422       VERIFY_IS_EQUAL(out(i, j), expected);
    423     }
    424   }
    425 }
    426 
    427 template <int DataLayout>
    428 static void test_innermost_first_dims() {
    429   Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
    430   Tensor<float, 2, DataLayout> out(72, 53);
    431   in.setRandom();
    432 
    433 // Reduce on the innermost dimensions.
    434 #if !EIGEN_HAS_CONSTEXPR
    435   array<int, 2> reduction_axis;
    436   reduction_axis[0] = 2;
    437   reduction_axis[1] = 3;
    438 #else
    439   // This triggers the use of packets for RowMajor.
    440   Eigen::IndexList<Eigen::type2index<2>, Eigen::type2index<3>> reduction_axis;
    441 #endif
    442 
    443   out = in.maximum(reduction_axis);
    444 
    445   for (int i = 0; i < 72; ++i) {
    446     for (int j = 0; j < 53; ++j) {
    447       float expected = -1e10f;
    448       for (int k = 0; k < 97; ++k) {
    449         for (int l = 0; l < 113; ++l) {
    450           expected = (std::max)(expected, in(i, j, k, l));
    451         }
    452       }
    453       VERIFY_IS_EQUAL(out(i, j), expected);
    454     }
    455   }
    456 }
    457 
    458 template <int DataLayout>
    459 static void test_reduce_middle_dims() {
    460   Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
    461   Tensor<float, 2, DataLayout> out(72, 53);
    462   in.setRandom();
    463 
    464 // Reduce on the innermost dimensions.
    465 #if !EIGEN_HAS_CONSTEXPR
    466   array<int, 2> reduction_axis;
    467   reduction_axis[0] = 1;
    468   reduction_axis[1] = 2;
    469 #else
    470   // This triggers the use of packets for RowMajor.
    471   Eigen::IndexList<Eigen::type2index<1>, Eigen::type2index<2>> reduction_axis;
    472 #endif
    473 
    474   out = in.maximum(reduction_axis);
    475 
    476   for (int i = 0; i < 72; ++i) {
    477     for (int j = 0; j < 113; ++j) {
    478       float expected = -1e10f;
    479       for (int k = 0; k < 53; ++k) {
    480         for (int l = 0; l < 97; ++l) {
    481           expected = (std::max)(expected, in(i, k, l, j));
    482         }
    483       }
    484       VERIFY_IS_EQUAL(out(i, j), expected);
    485     }
    486   }
    487 }
    488 
    489 static void test_sum_accuracy() {
    490   Tensor<float, 3> tensor(101, 101, 101);
    491   for (float prescribed_mean : {1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f}) {
    492     tensor.setRandom();
    493     tensor += tensor.constant(prescribed_mean);
    494 
    495     Tensor<float, 0> sum = tensor.sum();
    496     double expected_sum = 0.0;
    497     for (int i = 0; i < 101; ++i) {
    498       for (int j = 0; j < 101; ++j) {
    499         for (int k = 0; k < 101; ++k) {
    500           expected_sum += static_cast<double>(tensor(i, j, k));
    501         }
    502       }
    503     }
    504     VERIFY_IS_APPROX(sum(), static_cast<float>(expected_sum));
    505   }
    506 }
    507 
    508 EIGEN_DECLARE_TEST(cxx11_tensor_reduction) {
    509   CALL_SUBTEST(test_trivial_reductions<ColMajor>());
    510   CALL_SUBTEST(test_trivial_reductions<RowMajor>());
    511   CALL_SUBTEST(( test_simple_reductions<float,ColMajor>() ));
    512   CALL_SUBTEST(( test_simple_reductions<float,RowMajor>() ));
    513   CALL_SUBTEST(( test_simple_reductions<Eigen::half,ColMajor>() ));
    514   CALL_SUBTEST(( test_simple_reductions<Eigen::bfloat16,ColMajor>() ));
    515   CALL_SUBTEST(test_reductions_in_expr<ColMajor>());
    516   CALL_SUBTEST(test_reductions_in_expr<RowMajor>());
    517   CALL_SUBTEST(test_full_reductions<ColMajor>());
    518   CALL_SUBTEST(test_full_reductions<RowMajor>());
    519   CALL_SUBTEST(test_user_defined_reductions<ColMajor>());
    520   CALL_SUBTEST(test_user_defined_reductions<RowMajor>());
    521   CALL_SUBTEST(test_tensor_maps<ColMajor>());
    522   CALL_SUBTEST(test_tensor_maps<RowMajor>());
    523   CALL_SUBTEST(test_static_dims<ColMajor>());
    524   CALL_SUBTEST(test_static_dims<RowMajor>());
    525   CALL_SUBTEST(test_innermost_last_dims<ColMajor>());
    526   CALL_SUBTEST(test_innermost_last_dims<RowMajor>());
    527   CALL_SUBTEST(test_innermost_first_dims<ColMajor>());
    528   CALL_SUBTEST(test_innermost_first_dims<RowMajor>());
    529   CALL_SUBTEST(test_reduce_middle_dims<ColMajor>());
    530   CALL_SUBTEST(test_reduce_middle_dims<RowMajor>());
    531   CALL_SUBTEST(test_sum_accuracy());
    532 }