cart-elc

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

cxx11_tensor_block_access.cpp (22378B)


      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2018 Andy Davis <andydavis@google.com>
      5 // Copyright (C) 2018 Eugene Zhulenev <ezhulenev@google.com>
      6 //
      7 // This Source Code Form is subject to the terms of the Mozilla
      8 // Public License v. 2.0. If a copy of the MPL was not distributed
      9 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     10 
     11 #include "main.h"
     12 
     13 #include <algorithm>
     14 #include <set>
     15 
     16 #include <Eigen/CXX11/Tensor>
     17 
     18 using Eigen::Tensor;
     19 using Eigen::Index;
     20 using Eigen::RowMajor;
     21 using Eigen::ColMajor;
     22 using Eigen::internal::TensorBlockShapeType;
     23 
     24 static TensorOpCost zeroCost() { return {0, 0, 0}; }
     25 
     26 template<typename T>
     27 static const T& choose(int layout, const T& col, const T& row) {
     28   return layout == ColMajor ? col : row;
     29 }
     30 
     31 static TensorBlockShapeType RandomShape() {
     32   return internal::random<bool>()
     33          ? TensorBlockShapeType::kUniformAllDims
     34          : TensorBlockShapeType::kSkewedInnerDims;
     35 }
     36 
     37 template <int NumDims>
     38 static size_t RandomTargetSize(const DSizes<Index, NumDims>& dims) {
     39   return internal::random<size_t>(1, dims.TotalSize());
     40 }
     41 
     42 template <int NumDims>
     43 static DSizes<Index, NumDims> RandomDims() {
     44   array<Index, NumDims> dims;
     45   for (int i = 0; i < NumDims; ++i) {
     46     dims[i] = internal::random<int>(1, 20);
     47   }
     48   return DSizes<Index, NumDims>(dims);
     49 }
     50 
     51 template <typename T>
     52 static T* GenerateRandomData(const Index& size) {
     53   T* data = new T[size];
     54   for (int i = 0; i < size; ++i) {
     55     data[i] = internal::random<T>();
     56   }
     57   return data;
     58 }
     59 
     60 template <int NumDims>
     61 static void Debug(DSizes<Index, NumDims> dims) {
     62   for (int i = 0; i < NumDims; ++i) {
     63     std::cout << dims[i] << "; ";
     64   }
     65   std::cout << std::endl;
     66 }
     67 
     68 template <int Layout>
     69 static void test_block_mapper_sanity()
     70 {
     71   typedef internal::TensorBlockMapper<2, Layout> TensorBlockMapper;
     72 
     73   DSizes<Index, 2> tensor_dims(100, 100);
     74 
     75   // Test uniform blocks.
     76   TensorBlockMapper uniform_block_mapper(
     77       tensor_dims, {TensorBlockShapeType::kUniformAllDims, 100, zeroCost()});
     78 
     79   VERIFY_IS_EQUAL(uniform_block_mapper.blockCount(), 100);
     80   VERIFY_IS_EQUAL(uniform_block_mapper.blockTotalSize(), 100);
     81 
     82   // 10x10 blocks
     83   auto uniform_b0 = uniform_block_mapper.blockDescriptor(0);
     84   VERIFY_IS_EQUAL(uniform_b0.dimensions().at(0), 10);
     85   VERIFY_IS_EQUAL(uniform_b0.dimensions().at(1), 10);
     86 
     87   // Test skewed to inner dims blocks.
     88   TensorBlockMapper skewed_block_mapper(
     89       tensor_dims, {TensorBlockShapeType::kSkewedInnerDims, 100, zeroCost()});
     90 
     91   VERIFY_IS_EQUAL(skewed_block_mapper.blockCount(), 100);
     92   VERIFY_IS_EQUAL(skewed_block_mapper.blockTotalSize(), 100);
     93 
     94   // 1x100 (100x1) rows/cols depending on a tensor layout.
     95   auto skewed_b0 = skewed_block_mapper.blockDescriptor(0);
     96   VERIFY_IS_EQUAL(skewed_b0.dimensions().at(0), choose(Layout, 100, 1));
     97   VERIFY_IS_EQUAL(skewed_b0.dimensions().at(1), choose(Layout, 1, 100));
     98 }
     99 
    100 // Given a TensorBlock "visit" every element accessible though it, and a keep an
    101 // index in the visited set. Verify that every coeff accessed only once.
    102 template<int NumDims, int Layout>
    103 static void UpdateCoeffSet(
    104     const DSizes<Index, NumDims>& tensor_strides,
    105     const internal::TensorBlockDescriptor<NumDims>& block,
    106     Index first_coeff_index, int dim_index, std::set<Index>* visited_coeffs) {
    107   const DSizes<Index, NumDims>& block_sizes = block.dimensions();
    108 
    109   for (int i = 0; i < block_sizes[dim_index]; ++i) {
    110     if (tensor_strides[dim_index] == 1) {
    111       typedef std::pair<std::set<Index>::iterator, bool> ReturnType;
    112       ReturnType inserted = visited_coeffs->insert(first_coeff_index + i);
    113       VERIFY_IS_EQUAL(inserted.second, true);
    114     } else {
    115       int next_dim_index = dim_index + choose(Layout, -1, 1);
    116       UpdateCoeffSet<NumDims, Layout>(tensor_strides, block, first_coeff_index,
    117                                          next_dim_index, visited_coeffs);
    118       first_coeff_index += tensor_strides[dim_index];
    119     }
    120   }
    121 }
    122 
    123 template <typename T, int NumDims, int Layout>
    124 static void test_block_mapper_maps_every_element() {
    125   typedef internal::TensorBlockMapper<NumDims, Layout> TensorBlockMapper;
    126 
    127   DSizes<Index, NumDims> dims = RandomDims<NumDims>();
    128   DSizes<Index, NumDims> strides = internal::strides<Layout>(dims);
    129 
    130   // Keep track of elements indices available via block access.
    131   std::set<Index> coeff_set;
    132 
    133   // Try different combinations of block types and sizes.
    134   TensorBlockMapper block_mapper(
    135       dims, {RandomShape(), RandomTargetSize(dims), zeroCost()});
    136 
    137   for (int i = 0; i < block_mapper.blockCount(); ++i) {
    138     auto block = block_mapper.blockDescriptor(i);
    139     UpdateCoeffSet<NumDims, Layout>(strides, block, block.offset(),
    140                                     choose(Layout, NumDims - 1, 0),
    141                                     &coeff_set);
    142   }
    143 
    144   // Verify that every coefficient in the original Tensor is accessible through
    145   // TensorBlock only once.
    146   Index total_coeffs = dims.TotalSize();
    147   VERIFY_IS_EQUAL(Index(coeff_set.size()), total_coeffs);
    148   VERIFY_IS_EQUAL(*coeff_set.begin(), 0);
    149   VERIFY_IS_EQUAL(*coeff_set.rbegin(), total_coeffs - 1);
    150 }
    151 
    152 template <int Layout, int NumDims>
    153 static Index GetInputIndex(Index output_index,
    154                          const array<Index, NumDims>& output_to_input_dim_map,
    155                          const array<Index, NumDims>& input_strides,
    156                          const array<Index, NumDims>& output_strides) {
    157   int input_index = 0;
    158   if (Layout == ColMajor) {
    159     for (int i = NumDims - 1; i > 0; --i) {
    160       const Index idx = output_index / output_strides[i];
    161       input_index += idx * input_strides[output_to_input_dim_map[i]];
    162       output_index -= idx * output_strides[i];
    163     }
    164     return input_index +
    165            output_index * input_strides[output_to_input_dim_map[0]];
    166   } else {
    167     for (int i = 0; i < NumDims - 1; ++i) {
    168       const Index idx = output_index / output_strides[i];
    169       input_index += idx * input_strides[output_to_input_dim_map[i]];
    170       output_index -= idx * output_strides[i];
    171     }
    172     return input_index +
    173            output_index * input_strides[output_to_input_dim_map[NumDims - 1]];
    174   }
    175 }
    176 
    177 template <int Layout, int NumDims>
    178 static array<Index, NumDims> ComputeStrides(
    179     const array<Index, NumDims>& sizes) {
    180   array<Index, NumDims> strides;
    181   if (Layout == ColMajor) {
    182     strides[0] = 1;
    183     for (int i = 1; i < NumDims; ++i) {
    184       strides[i] = strides[i - 1] * sizes[i - 1];
    185     }
    186   } else {
    187     strides[NumDims - 1] = 1;
    188     for (int i = NumDims - 2; i >= 0; --i) {
    189       strides[i] = strides[i + 1] * sizes[i + 1];
    190     }
    191   }
    192   return strides;
    193 }
    194 
    195 template<typename Scalar, typename StorageIndex, int Dim>
    196 class EqualityChecker
    197 {
    198     const Scalar* input_data;
    199     const DSizes<StorageIndex, Dim> &input_dims, &input_strides, &output_dims, &output_strides;
    200     void check_recursive(const Scalar* input, const Scalar* output, int depth=0) const
    201     {
    202         if(depth==Dim)
    203         {
    204             VERIFY_IS_EQUAL(*input, *output);
    205             return;
    206         }
    207 
    208         for(int i=0; i<output_dims[depth]; ++i)
    209         {
    210             check_recursive(input + i % input_dims[depth] * input_strides[depth], output + i*output_strides[depth], depth+1);
    211         }
    212     }
    213 public:
    214     EqualityChecker(const Scalar* input_data_,
    215             const DSizes<StorageIndex, Dim> &input_dims_, const DSizes<StorageIndex, Dim> &input_strides_,
    216             const DSizes<StorageIndex, Dim> &output_dims_, const DSizes<StorageIndex, Dim> &output_strides_)
    217         : input_data(input_data_)
    218         , input_dims(input_dims_), input_strides(input_strides_)
    219         , output_dims(output_dims_), output_strides(output_strides_)
    220         {}
    221 
    222     void operator()(const Scalar* output_data) const
    223     {
    224         check_recursive(input_data, output_data);
    225     }
    226 };
    227 
    228 template <int Layout>
    229 static void test_uniform_block_shape()
    230 {
    231   typedef internal::TensorBlockDescriptor<5> TensorBlock;
    232   typedef internal::TensorBlockMapper<5, Layout> TensorBlockMapper;
    233 
    234   {
    235     // Test shape 'UniformAllDims' with uniform 'max_coeff count'.
    236     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    237     const Index max_coeff_count = 5 * 5 * 5 * 5 * 5;
    238     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    239                                           max_coeff_count, zeroCost()});
    240     TensorBlock block = block_mapper.blockDescriptor(0);
    241     for (int i = 0; i < 5; ++i) {
    242       VERIFY_IS_EQUAL(5, block.dimensions()[i]);
    243     }
    244     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    245   }
    246 
    247   // Test shape 'UniformAllDims' with larger 'max_coeff count' which spills
    248   // partially into first inner-most dimension.
    249   if (Layout == ColMajor) {
    250     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    251     const Index max_coeff_count = 7 * 5 * 5 * 5 * 5;
    252     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    253                                           max_coeff_count, zeroCost()});
    254     TensorBlock block = block_mapper.blockDescriptor(0);
    255     VERIFY_IS_EQUAL(7, block.dimensions()[0]);
    256     for (int i = 1; i < 5; ++i) {
    257       VERIFY_IS_EQUAL(5, block.dimensions()[i]);
    258     }
    259     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    260   } else {
    261     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    262     const Index max_coeff_count = 5 * 5 * 5 * 5 * 6;
    263     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    264                                           max_coeff_count, zeroCost()});
    265     TensorBlock block = block_mapper.blockDescriptor(0);
    266     VERIFY_IS_EQUAL(6, block.dimensions()[4]);
    267     for (int i = 3; i >= 0; --i) {
    268       VERIFY_IS_EQUAL(5, block.dimensions()[i]);
    269     }
    270     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    271   }
    272 
    273   // Test shape 'UniformAllDims' with larger 'max_coeff count' which spills
    274   // fully into first inner-most dimension.
    275   if (Layout == ColMajor) {
    276     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    277     const Index max_coeff_count = 11 * 5 * 5 * 5 * 5;
    278     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    279                                           max_coeff_count, zeroCost()});
    280     TensorBlock block = block_mapper.blockDescriptor(0);
    281     VERIFY_IS_EQUAL(11, block.dimensions()[0]);
    282     for (int i = 1; i < 5; ++i) {
    283       VERIFY_IS_EQUAL(5, block.dimensions()[i]);
    284     }
    285     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    286   } else {
    287     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    288     const Index max_coeff_count = 5 * 5 * 5 * 5 * 7;
    289     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    290                                           max_coeff_count, zeroCost()});
    291     TensorBlock block = block_mapper.blockDescriptor(0);
    292     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    293     for (int i = 3; i >= 0; --i) {
    294       VERIFY_IS_EQUAL(5, block.dimensions()[i]);
    295     }
    296     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    297   }
    298 
    299   // Test shape 'UniformAllDims' with larger 'max_coeff count' which spills
    300   // fully into first few inner-most dimensions.
    301   if (Layout == ColMajor) {
    302     DSizes<Index, 5> dims(7, 5, 6, 17, 7);
    303     const Index max_coeff_count = 7 * 5 * 6 * 7 * 5;
    304     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    305                                           max_coeff_count, zeroCost()});
    306     TensorBlock block = block_mapper.blockDescriptor(0);
    307     VERIFY_IS_EQUAL(7, block.dimensions()[0]);
    308     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    309     VERIFY_IS_EQUAL(6, block.dimensions()[2]);
    310     VERIFY_IS_EQUAL(7, block.dimensions()[3]);
    311     VERIFY_IS_EQUAL(5, block.dimensions()[4]);
    312     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    313   } else {
    314     DSizes<Index, 5> dims(7, 5, 6, 9, 7);
    315     const Index max_coeff_count = 5 * 5 * 5 * 6 * 7;
    316     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    317                                           max_coeff_count, zeroCost()});
    318     TensorBlock block = block_mapper.blockDescriptor(0);
    319     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    320     VERIFY_IS_EQUAL(6, block.dimensions()[3]);
    321     VERIFY_IS_EQUAL(5, block.dimensions()[2]);
    322     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    323     VERIFY_IS_EQUAL(5, block.dimensions()[0]);
    324     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    325   }
    326 
    327   // Test shape 'UniformAllDims' with full allocation to all dims.
    328   if (Layout == ColMajor) {
    329     DSizes<Index, 5> dims(7, 5, 6, 17, 7);
    330     const Index max_coeff_count = 7 * 5 * 6 * 17 * 7;
    331     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    332                                           max_coeff_count, zeroCost()});
    333     TensorBlock block = block_mapper.blockDescriptor(0);
    334     VERIFY_IS_EQUAL(7, block.dimensions()[0]);
    335     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    336     VERIFY_IS_EQUAL(6, block.dimensions()[2]);
    337     VERIFY_IS_EQUAL(17, block.dimensions()[3]);
    338     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    339     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    340   } else {
    341     DSizes<Index, 5> dims(7, 5, 6, 9, 7);
    342     const Index max_coeff_count = 7 * 5 * 6 * 9 * 7;
    343     TensorBlockMapper block_mapper(dims, {TensorBlockShapeType::kUniformAllDims,
    344                                           max_coeff_count, zeroCost()});
    345     TensorBlock block = block_mapper.blockDescriptor(0);
    346     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    347     VERIFY_IS_EQUAL(9, block.dimensions()[3]);
    348     VERIFY_IS_EQUAL(6, block.dimensions()[2]);
    349     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    350     VERIFY_IS_EQUAL(7, block.dimensions()[0]);
    351     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    352   }
    353 }
    354 
    355 template <int Layout>
    356 static void test_skewed_inner_dim_block_shape()
    357 {
    358   typedef internal::TensorBlockDescriptor<5> TensorBlock;
    359   typedef internal::TensorBlockMapper<5, Layout> TensorBlockMapper;
    360 
    361   // Test shape 'SkewedInnerDims' with partial allocation to inner-most dim.
    362   if (Layout == ColMajor) {
    363     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    364     const Index max_coeff_count = 10 * 1 * 1 * 1 * 1;
    365     TensorBlockMapper block_mapper(
    366         dims,
    367         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    368     TensorBlock block = block_mapper.blockDescriptor(0);
    369     VERIFY_IS_EQUAL(10, block.dimensions()[0]);
    370     for (int i = 1; i < 5; ++i) {
    371       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    372     }
    373     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    374   } else {
    375     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    376     const Index max_coeff_count = 1 * 1 * 1 * 1 * 6;
    377     TensorBlockMapper block_mapper(
    378         dims,
    379         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    380     TensorBlock block = block_mapper.blockDescriptor(0);
    381     VERIFY_IS_EQUAL(6, block.dimensions()[4]);
    382     for (int i = 3; i >= 0; --i) {
    383       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    384     }
    385     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    386   }
    387 
    388   // Test shape 'SkewedInnerDims' with full allocation to inner-most dim.
    389   if (Layout == ColMajor) {
    390     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    391     const Index max_coeff_count = 11 * 1 * 1 * 1 * 1;
    392     TensorBlockMapper block_mapper(
    393         dims,
    394         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    395     TensorBlock block = block_mapper.blockDescriptor(0);
    396     VERIFY_IS_EQUAL(11, block.dimensions()[0]);
    397     for (int i = 1; i < 5; ++i) {
    398       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    399     }
    400     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    401   } else {
    402     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    403     const Index max_coeff_count = 1 * 1 * 1 * 1 * 7;
    404     TensorBlockMapper block_mapper(
    405         dims,
    406         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    407     TensorBlock block = block_mapper.blockDescriptor(0);
    408     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    409     for (int i = 3; i >= 0; --i) {
    410       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    411     }
    412     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    413   }
    414 
    415   // Test shape 'SkewedInnerDims' with full allocation to inner-most dim,
    416   // and partial allocation to second inner-dim.
    417   if (Layout == ColMajor) {
    418     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    419     const Index max_coeff_count = 11 * 3 * 1 * 1 * 1;
    420     TensorBlockMapper block_mapper(
    421         dims,
    422         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    423     TensorBlock block = block_mapper.blockDescriptor(0);
    424     VERIFY_IS_EQUAL(11, block.dimensions()[0]);
    425     VERIFY_IS_EQUAL(3, block.dimensions()[1]);
    426     for (int i = 2; i < 5; ++i) {
    427       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    428     }
    429     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    430   } else {
    431     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    432     const Index max_coeff_count = 1 * 1 * 1 * 15 * 7;
    433     TensorBlockMapper block_mapper(
    434         dims,
    435         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    436     TensorBlock block = block_mapper.blockDescriptor(0);
    437     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    438     VERIFY_IS_EQUAL(15, block.dimensions()[3]);
    439     for (int i = 2; i >= 0; --i) {
    440       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    441     }
    442     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    443   }
    444 
    445   // Test shape 'SkewedInnerDims' with full allocation to inner-most dim,
    446   // and partial allocation to third inner-dim.
    447   if (Layout == ColMajor) {
    448     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    449     const Index max_coeff_count = 11 * 5 * 5 * 1 * 1;
    450     TensorBlockMapper block_mapper(
    451         dims,
    452         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    453     TensorBlock block = block_mapper.blockDescriptor(0);
    454     VERIFY_IS_EQUAL(11, block.dimensions()[0]);
    455     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    456     VERIFY_IS_EQUAL(5, block.dimensions()[2]);
    457     for (int i = 3; i < 5; ++i) {
    458       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    459     }
    460     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    461   } else {
    462     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    463     const Index max_coeff_count = 1 * 1 * 5 * 17 * 7;
    464     TensorBlockMapper block_mapper(
    465         dims,
    466         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    467     TensorBlock block = block_mapper.blockDescriptor(0);
    468     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    469     VERIFY_IS_EQUAL(17, block.dimensions()[3]);
    470     VERIFY_IS_EQUAL(5, block.dimensions()[2]);
    471     for (int i = 1; i >= 0; --i) {
    472       VERIFY_IS_EQUAL(1, block.dimensions()[i]);
    473     }
    474     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    475   }
    476 
    477   // Test shape 'SkewedInnerDims' with full allocation to all dims.
    478   if (Layout == ColMajor) {
    479     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    480     const Index max_coeff_count = 11 * 5 * 6 * 17 * 7;
    481     TensorBlockMapper block_mapper(
    482         dims,
    483         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    484     TensorBlock block = block_mapper.blockDescriptor(0);
    485     VERIFY_IS_EQUAL(11, block.dimensions()[0]);
    486     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    487     VERIFY_IS_EQUAL(6, block.dimensions()[2]);
    488     VERIFY_IS_EQUAL(17, block.dimensions()[3]);
    489     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    490     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    491   } else {
    492     DSizes<Index, 5> dims(11, 5, 6, 17, 7);
    493     const Index max_coeff_count = 11 * 5 * 6 * 17 * 7;
    494     TensorBlockMapper block_mapper(
    495         dims,
    496         {TensorBlockShapeType::kSkewedInnerDims, max_coeff_count, zeroCost()});
    497     TensorBlock block = block_mapper.blockDescriptor(0);
    498     VERIFY_IS_EQUAL(7, block.dimensions()[4]);
    499     VERIFY_IS_EQUAL(17, block.dimensions()[3]);
    500     VERIFY_IS_EQUAL(6, block.dimensions()[2]);
    501     VERIFY_IS_EQUAL(5, block.dimensions()[1]);
    502     VERIFY_IS_EQUAL(11, block.dimensions()[0]);
    503     VERIFY(block.dimensions().TotalSize() <= max_coeff_count);
    504   }
    505 }
    506 
    507 template <int Layout>
    508 static void test_empty_dims(const internal::TensorBlockShapeType block_shape)
    509 {
    510   // Test blocking of tensors with zero dimensions:
    511   //  - we must not crash on asserts and divisions by zero
    512   //  - we must not return block with zero dimensions
    513   //    (recipe for overflows/underflows, divisions by zero and NaNs later)
    514   //  - total block count must be zero
    515   {
    516     typedef internal::TensorBlockMapper<1, Layout> TensorBlockMapper;
    517 
    518     DSizes<Index, 1> dims(0);
    519     for (size_t max_coeff_count = 0; max_coeff_count < 2; ++max_coeff_count) {
    520       TensorBlockMapper block_mapper(
    521           dims, {block_shape, max_coeff_count, zeroCost()});
    522       VERIFY_IS_EQUAL(block_mapper.blockCount(), 0);
    523       VERIFY(block_mapper.blockTotalSize() >= 1);
    524     }
    525   }
    526 
    527   {
    528     typedef internal::TensorBlockMapper<2, Layout> TensorBlockMapper;
    529 
    530     for (int dim1 = 0; dim1 < 3; ++dim1) {
    531       for (int dim2 = 0; dim2 < 3; ++dim2) {
    532         DSizes<Index, 2> dims(dim1, dim2);
    533         for (size_t max_coeff_count = 0; max_coeff_count < 2; ++max_coeff_count) {
    534           TensorBlockMapper block_mapper(
    535               dims, {block_shape, max_coeff_count, zeroCost()});
    536           if (dim1 * dim2 == 0) {
    537             VERIFY_IS_EQUAL(block_mapper.blockCount(), 0);
    538           }
    539           VERIFY(block_mapper.blockTotalSize() >= 1);
    540         }
    541       }
    542     }
    543   }
    544 }
    545 
    546 #define TEST_LAYOUTS(NAME) \
    547   CALL_SUBTEST(NAME<ColMajor>()); \
    548   CALL_SUBTEST(NAME<RowMajor>())
    549 
    550 #define TEST_LAYOUTS_AND_DIMS(TYPE, NAME)    \
    551   CALL_SUBTEST((NAME<TYPE, 1, ColMajor>())); \
    552   CALL_SUBTEST((NAME<TYPE, 1, RowMajor>())); \
    553   CALL_SUBTEST((NAME<TYPE, 2, ColMajor>())); \
    554   CALL_SUBTEST((NAME<TYPE, 2, RowMajor>())); \
    555   CALL_SUBTEST((NAME<TYPE, 3, ColMajor>())); \
    556   CALL_SUBTEST((NAME<TYPE, 3, RowMajor>())); \
    557   CALL_SUBTEST((NAME<TYPE, 4, ColMajor>())); \
    558   CALL_SUBTEST((NAME<TYPE, 4, RowMajor>())); \
    559   CALL_SUBTEST((NAME<TYPE, 5, ColMajor>())); \
    560   CALL_SUBTEST((NAME<TYPE, 5, RowMajor>()))
    561 
    562 #define TEST_LAYOUTS_WITH_ARG(NAME, ARG) \
    563   CALL_SUBTEST(NAME<ColMajor>(ARG)); \
    564   CALL_SUBTEST(NAME<RowMajor>(ARG))
    565 
    566 EIGEN_DECLARE_TEST(cxx11_tensor_block_access) {
    567   TEST_LAYOUTS(test_block_mapper_sanity);
    568   TEST_LAYOUTS_AND_DIMS(float, test_block_mapper_maps_every_element);
    569   TEST_LAYOUTS(test_uniform_block_shape);
    570   TEST_LAYOUTS(test_skewed_inner_dim_block_shape);
    571   TEST_LAYOUTS_WITH_ARG(test_empty_dims, TensorBlockShapeType::kUniformAllDims);
    572   TEST_LAYOUTS_WITH_ARG(test_empty_dims, TensorBlockShapeType::kSkewedInnerDims);
    573 }
    574 
    575 #undef TEST_LAYOUTS
    576 #undef TEST_LAYOUTS_WITH_ARG