cxx11_tensor_block_io.cpp
Go to the documentation of this file.
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // This Source Code Form is subject to the terms of the Mozilla
5 // Public License v. 2.0. If a copy of the MPL was not distributed
6 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 
8 // clang-format off
9 #include "main.h"
10 #include <Eigen/CXX11/Tensor>
11 // clang-format on
12 
13 // -------------------------------------------------------------------------- //
14 // A set of tests for TensorBlockIO: copying data between tensor blocks.
15 
16 template <int NumDims>
19  for (int i = 0; i < NumDims; ++i) {
20  dims[i] = internal::random<Index>(min, max);
21  }
22  return DSizes<Index, NumDims>(dims);
23 }
24 
26  return internal::random<bool>()
27  ? internal::TensorBlockShapeType::kUniformAllDims
28  : internal::TensorBlockShapeType::kSkewedInnerDims;
29 }
30 
31 template <int NumDims>
32 static size_t RandomTargetBlockSize(const DSizes<Index, NumDims>& dims) {
33  return internal::random<size_t>(1, dims.TotalSize());
34 }
35 
36 template <int Layout, int NumDims>
37 static Index GetInputIndex(Index output_index,
38  const array<Index, NumDims>& output_to_input_dim_map,
39  const array<Index, NumDims>& input_strides,
40  const array<Index, NumDims>& output_strides) {
41  int input_index = 0;
42  if (Layout == ColMajor) {
43  for (int i = NumDims - 1; i > 0; --i) {
44  const Index idx = output_index / output_strides[i];
45  input_index += idx * input_strides[output_to_input_dim_map[i]];
46  output_index -= idx * output_strides[i];
47  }
48  return input_index +
49  output_index * input_strides[output_to_input_dim_map[0]];
50  } else {
51  for (int i = 0; i < NumDims - 1; ++i) {
52  const Index idx = output_index / output_strides[i];
53  input_index += idx * input_strides[output_to_input_dim_map[i]];
54  output_index -= idx * output_strides[i];
55  }
56  return input_index +
57  output_index * input_strides[output_to_input_dim_map[NumDims - 1]];
58  }
59 }
60 
61 template <typename T, int NumDims, int Layout>
63  using TensorBlockIO = internal::TensorBlockIO<T, Index, NumDims, Layout>;
64  using IODst = typename TensorBlockIO::Dst;
65  using IOSrc = typename TensorBlockIO::Src;
66 
67  // Generate a random input Tensor.
68  DSizes<Index, NumDims> dims = RandomDims<NumDims>(1, 30);
69  Tensor<T, NumDims, Layout> input(dims);
70  input.setRandom();
71 
72  // Write data to an output Tensor.
73  Tensor<T, NumDims, Layout> output(dims);
74 
75  // Construct a tensor block mapper.
76  using TensorBlockMapper =
77  internal::TensorBlockMapper<NumDims, Layout, Index>;
78  TensorBlockMapper block_mapper(
79  dims, {RandomBlockShape(), RandomTargetBlockSize(dims), {0, 0, 0}});
80 
81  // We will copy data from input to output through this buffer.
82  Tensor<T, NumDims, Layout> block(block_mapper.blockDimensions());
83 
84  // Precompute strides for TensorBlockIO::Copy.
85  auto input_strides = internal::strides<Layout>(dims);
86  auto output_strides = internal::strides<Layout>(dims);
87 
88  const T* input_data = input.data();
89  T* output_data = output.data();
90  T* block_data = block.data();
91 
92  for (int i = 0; i < block_mapper.blockCount(); ++i) {
93  auto desc = block_mapper.blockDescriptor(i);
94 
95  auto blk_dims = desc.dimensions();
96  auto blk_strides = internal::strides<Layout>(blk_dims);
97 
98  {
99  // Read from input into a block buffer.
100  IODst dst(blk_dims, blk_strides, block_data, 0);
101  IOSrc src(input_strides, input_data, desc.offset());
102 
103  TensorBlockIO::Copy(dst, src);
104  }
105 
106  {
107  // Write from block buffer to output.
108  IODst dst(blk_dims, output_strides, output_data, desc.offset());
109  IOSrc src(blk_strides, block_data, 0);
110 
111  TensorBlockIO::Copy(dst, src);
112  }
113  }
114 
115  for (int i = 0; i < dims.TotalSize(); ++i) {
116  VERIFY_IS_EQUAL(input_data[i], output_data[i]);
117  }
118 }
119 
120 template <typename T, int NumDims, int Layout>
122  // Generate a random input Tensor.
123  DSizes<Index, NumDims> dims = RandomDims<NumDims>(1, 30);
124  Tensor<T, NumDims, Layout> input(dims);
125  input.setRandom();
126 
127  // Create a random dimension re-ordering/shuffle.
128  std::vector<int> shuffle;
129 
130  for (int i = 0; i < NumDims; ++i) shuffle.push_back(i);
131  std::shuffle(shuffle.begin(), shuffle.end(), std::mt19937(g_seed));
132 
133  DSizes<Index, NumDims> output_tensor_dims;
134  DSizes<Index, NumDims> input_to_output_dim_map;
135  DSizes<Index, NumDims> output_to_input_dim_map;
136  for (Index i = 0; i < NumDims; ++i) {
137  output_tensor_dims[shuffle[i]] = dims[i];
138  input_to_output_dim_map[i] = shuffle[i];
139  output_to_input_dim_map[shuffle[i]] = i;
140  }
141 
142  // Write data to an output Tensor.
143  Tensor<T, NumDims, Layout> output(output_tensor_dims);
144 
145  // Construct a tensor block mapper.
146  // NOTE: Tensor block mapper works with shuffled dimensions.
147  using TensorBlockMapper =
148  internal::TensorBlockMapper<NumDims, Layout, Index>;
149  TensorBlockMapper block_mapper(output_tensor_dims,
150  {RandomBlockShape(),
151  RandomTargetBlockSize(output_tensor_dims),
152  {0, 0, 0}});
153 
154  // We will copy data from input to output through this buffer.
155  Tensor<T, NumDims, Layout> block(block_mapper.blockDimensions());
156 
157  // Precompute strides for TensorBlockIO::Copy.
158  auto input_strides = internal::strides<Layout>(dims);
159  auto output_strides = internal::strides<Layout>(output_tensor_dims);
160 
161  const T* input_data = input.data();
162  T* output_data = output.data();
163  T* block_data = block.data();
164 
165  for (Index i = 0; i < block_mapper.blockCount(); ++i) {
166  auto desc = block_mapper.blockDescriptor(i);
167 
168  const Index first_coeff_index = GetInputIndex<Layout, NumDims>(
169  desc.offset(), output_to_input_dim_map, input_strides,
170  output_strides);
171 
172  // NOTE: Block dimensions are in the same order as output dimensions.
173 
174  using TensorBlockIO = internal::TensorBlockIO<T, Index, NumDims, Layout>;
175  using IODst = typename TensorBlockIO::Dst;
176  using IOSrc = typename TensorBlockIO::Src;
177 
178  auto blk_dims = desc.dimensions();
179  auto blk_strides = internal::strides<Layout>(blk_dims);
180 
181  {
182  // Read from input into a block buffer.
183  IODst dst(blk_dims, blk_strides, block_data, 0);
184  IOSrc src(input_strides, input_data, first_coeff_index);
185 
186  // TODO(ezhulenev): Remove when fully switched to TensorBlock.
187  DSizes<int, NumDims> dim_map;
188  for (int j = 0; j < NumDims; ++j)
189  dim_map[j] = static_cast<int>(output_to_input_dim_map[j]);
190  TensorBlockIO::Copy(dst, src, /*dst_to_src_dim_map=*/dim_map);
191  }
192 
193  {
194  // We need to convert block dimensions from output to input order.
195  auto dst_dims = blk_dims;
196  for (int out_dim = 0; out_dim < NumDims; ++out_dim) {
197  dst_dims[output_to_input_dim_map[out_dim]] = blk_dims[out_dim];
198  }
199 
200  // Write from block buffer to output.
201  IODst dst(dst_dims, input_strides, output_data, first_coeff_index);
202  IOSrc src(blk_strides, block_data, 0);
203 
204  // TODO(ezhulenev): Remove when fully switched to TensorBlock.
205  DSizes<int, NumDims> dim_map;
206  for (int j = 0; j < NumDims; ++j)
207  dim_map[j] = static_cast<int>(input_to_output_dim_map[j]);
208  TensorBlockIO::Copy(dst, src, /*dst_to_src_dim_map=*/dim_map);
209  }
210  }
211 
212  for (Index i = 0; i < dims.TotalSize(); ++i) {
213  VERIFY_IS_EQUAL(input_data[i], output_data[i]);
214  }
215 }
216 
217 // This is the special case for reading data with reordering, when dimensions
218 // before/after reordering are the same. Squeezing reads along inner dimensions
219 // in this case is illegal, because we reorder innermost dimension.
220 template <int Layout>
222  DSizes<Index, 3> tensor_dims(7, 9, 7);
223  DSizes<Index, 3> block_dims = tensor_dims;
224 
225  DSizes<int, 3> block_to_tensor_dim;
226  block_to_tensor_dim[0] = 2;
227  block_to_tensor_dim[1] = 1;
228  block_to_tensor_dim[2] = 0;
229 
230  auto tensor_strides = internal::strides<Layout>(tensor_dims);
231  auto block_strides = internal::strides<Layout>(block_dims);
232 
233  Tensor<float, 3, Layout> block(block_dims);
234  Tensor<float, 3, Layout> tensor(tensor_dims);
235  tensor.setRandom();
236 
237  float* tensor_data = tensor.data();
238  float* block_data = block.data();
239 
240  using TensorBlockIO = internal::TensorBlockIO<float, Index, 3, Layout>;
241  using IODst = typename TensorBlockIO::Dst;
242  using IOSrc = typename TensorBlockIO::Src;
243 
244  // Read from a tensor into a block.
245  IODst dst(block_dims, block_strides, block_data, 0);
246  IOSrc src(tensor_strides, tensor_data, 0);
247 
248  TensorBlockIO::Copy(dst, src, /*dst_to_src_dim_map=*/block_to_tensor_dim);
249 
250  TensorMap<Tensor<float, 3, Layout> > block_tensor(block_data, block_dims);
251  TensorMap<Tensor<float, 3, Layout> > tensor_tensor(tensor_data, tensor_dims);
252 
253  for (Index d0 = 0; d0 < tensor_dims[0]; ++d0) {
254  for (Index d1 = 0; d1 < tensor_dims[1]; ++d1) {
255  for (Index d2 = 0; d2 < tensor_dims[2]; ++d2) {
256  float block_value = block_tensor(d2, d1, d0);
257  float tensor_value = tensor_tensor(d0, d1, d2);
258  VERIFY_IS_EQUAL(block_value, tensor_value);
259  }
260  }
261  }
262 }
263 
264 // This is the special case for reading data with reordering, when dimensions
265 // before/after reordering are the same. Squeezing reads in this case is allowed
266 // because we reorder outer dimensions.
267 template <int Layout>
269  DSizes<Index, 4> tensor_dims(7, 5, 9, 9);
270  DSizes<Index, 4> block_dims = tensor_dims;
271 
272  DSizes<int, 4> block_to_tensor_dim;
273  block_to_tensor_dim[0] = 0;
274  block_to_tensor_dim[1] = 1;
275  block_to_tensor_dim[2] = 3;
276  block_to_tensor_dim[3] = 2;
277 
278  auto tensor_strides = internal::strides<Layout>(tensor_dims);
279  auto block_strides = internal::strides<Layout>(block_dims);
280 
281  Tensor<float, 4, Layout> block(block_dims);
282  Tensor<float, 4, Layout> tensor(tensor_dims);
283  tensor.setRandom();
284 
285  float* tensor_data = tensor.data();
286  float* block_data = block.data();
287 
288  using TensorBlockIO = internal::TensorBlockIO<float, Index, 4, Layout>;
289  using IODst = typename TensorBlockIO::Dst;
290  using IOSrc = typename TensorBlockIO::Src;
291 
292  // Read from a tensor into a block.
293  IODst dst(block_dims, block_strides, block_data, 0);
294  IOSrc src(tensor_strides, tensor_data, 0);
295 
296  TensorBlockIO::Copy(dst, src, /*dst_to_src_dim_map=*/block_to_tensor_dim);
297 
298  TensorMap<Tensor<float, 4, Layout> > block_tensor(block_data, block_dims);
299  TensorMap<Tensor<float, 4, Layout> > tensor_tensor(tensor_data, tensor_dims);
300 
301  for (Index d0 = 0; d0 < tensor_dims[0]; ++d0) {
302  for (Index d1 = 0; d1 < tensor_dims[1]; ++d1) {
303  for (Index d2 = 0; d2 < tensor_dims[2]; ++d2) {
304  for (Index d3 = 0; d3 < tensor_dims[3]; ++d3) {
305  float block_value = block_tensor(d0, d1, d3, d2);
306  float tensor_value = tensor_tensor(d0, d1, d2, d3);
307  VERIFY_IS_EQUAL(block_value, tensor_value);
308  }
309  }
310  }
311  }
312 }
313 
314 template <int Layout>
316  DSizes<Index, 5> rnd_dims = RandomDims<5>(1, 30);
317 
318  DSizes<Index, 5> input_tensor_dims = rnd_dims;
319  input_tensor_dims[0] = 1;
320  input_tensor_dims[2] = 1;
321  input_tensor_dims[4] = 1;
322 
323  Tensor<float, 5, Layout> input(input_tensor_dims);
324  input.setRandom();
325 
326  DSizes<Index, 5> output_tensor_dims = rnd_dims;
327 
328  auto input_tensor_strides = internal::strides<Layout>(input_tensor_dims);
329  auto output_tensor_strides = internal::strides<Layout>(output_tensor_dims);
330 
331  auto input_tensor_strides_with_zeros = input_tensor_strides;
332  input_tensor_strides_with_zeros[0] = 0;
333  input_tensor_strides_with_zeros[2] = 0;
334  input_tensor_strides_with_zeros[4] = 0;
335 
336  Tensor<float, 5, Layout> output(output_tensor_dims);
337  output.setRandom();
338 
339  using TensorBlockIO = internal::TensorBlockIO<float, Index, 5, Layout>;
340  using IODst = typename TensorBlockIO::Dst;
341  using IOSrc = typename TensorBlockIO::Src;
342 
343  // Write data from input to output with broadcasting in dims [0, 2, 4].
344  IODst dst(output_tensor_dims, output_tensor_strides, output.data(), 0);
345  IOSrc src(input_tensor_strides_with_zeros, input.data(), 0);
346  TensorBlockIO::Copy(dst, src);
347 
348  for (int i = 0; i < output_tensor_dims[0]; ++i) {
349  for (int j = 0; j < output_tensor_dims[1]; ++j) {
350  for (int k = 0; k < output_tensor_dims[2]; ++k) {
351  for (int l = 0; l < output_tensor_dims[3]; ++l) {
352  for (int m = 0; m < output_tensor_dims[4]; ++m) {
353  float input_value = input(0, j, 0, l, 0);
354  float output_value = output(i, j, k, l, m);
355  VERIFY_IS_EQUAL(input_value, output_value);
356  }
357  }
358  }
359  }
360  }
361 }
362 
363 template <int Layout>
365  using TensorBlockIO = internal::TensorBlockIO<float, Index, 5, Layout>;
366  using IODst = typename TensorBlockIO::Dst;
367  using IOSrc = typename TensorBlockIO::Src;
368 
369  // Total size > 1.
370  {
371  DSizes<Index, 5> block_sizes(1, 2, 1, 2, 1);
372  auto strides = internal::strides<Layout>(block_sizes);
373 
374  // Create a random input tensor.
375  Tensor<float, 5> input(block_sizes);
376  input.setRandom();
377 
378  Tensor<float, 5> output(block_sizes);
379 
380  IODst dst(block_sizes, strides, output.data(), 0);
381  IOSrc src(strides, input.data());
382  TensorBlockIO::Copy(dst, src);
383 
384  for (Index i = 0; i < block_sizes.TotalSize(); ++i) {
385  VERIFY_IS_EQUAL(output.data()[i], input.data()[i]);
386  }
387  }
388 
389  // Total size == 1.
390  {
391  DSizes<Index, 5> block_sizes(1, 1, 1, 1, 1);
392  auto strides = internal::strides<Layout>(block_sizes);
393 
394  // Create a random input tensor.
395  Tensor<float, 5> input(block_sizes);
396  input.setRandom();
397 
398  Tensor<float, 5> output(block_sizes);
399 
400  IODst dst(block_sizes, strides, output.data(), 0);
401  IOSrc src(strides, input.data());
402  TensorBlockIO::Copy(dst, src);
403 
404  for (Index i = 0; i < block_sizes.TotalSize(); ++i) {
405  VERIFY_IS_EQUAL(output.data()[i], input.data()[i]);
406  }
407  }
408 }
409 
410 #define CALL_SUBTESTS(NAME) \
411  CALL_SUBTEST((NAME<float, 1, RowMajor>())); \
412  CALL_SUBTEST((NAME<float, 2, RowMajor>())); \
413  CALL_SUBTEST((NAME<float, 4, RowMajor>())); \
414  CALL_SUBTEST((NAME<float, 5, RowMajor>())); \
415  CALL_SUBTEST((NAME<float, 1, ColMajor>())); \
416  CALL_SUBTEST((NAME<float, 2, ColMajor>())); \
417  CALL_SUBTEST((NAME<float, 4, ColMajor>())); \
418  CALL_SUBTEST((NAME<float, 5, ColMajor>())); \
419  CALL_SUBTEST((NAME<bool, 1, RowMajor>())); \
420  CALL_SUBTEST((NAME<bool, 2, RowMajor>())); \
421  CALL_SUBTEST((NAME<bool, 4, RowMajor>())); \
422  CALL_SUBTEST((NAME<bool, 5, RowMajor>())); \
423  CALL_SUBTEST((NAME<bool, 1, ColMajor>())); \
424  CALL_SUBTEST((NAME<bool, 2, ColMajor>())); \
425  CALL_SUBTEST((NAME<bool, 4, ColMajor>())); \
426  CALL_SUBTEST((NAME<bool, 5, ColMajor>()))
427 
428 EIGEN_DECLARE_TEST(cxx11_tensor_block_io) {
429  // clang-format off
432 
433  CALL_SUBTEST(test_block_io_copy_using_reordered_dimensions_do_not_squeeze<RowMajor>());
434  CALL_SUBTEST(test_block_io_copy_using_reordered_dimensions_do_not_squeeze<ColMajor>());
435 
436  CALL_SUBTEST(test_block_io_copy_using_reordered_dimensions_squeeze<RowMajor>());
437  CALL_SUBTEST(test_block_io_copy_using_reordered_dimensions_squeeze<ColMajor>());
438 
439  CALL_SUBTEST(test_block_io_zero_stride<RowMajor>());
440  CALL_SUBTEST(test_block_io_zero_stride<ColMajor>());
441 
442  CALL_SUBTEST(test_block_io_squeeze_ones<RowMajor>());
443  CALL_SUBTEST(test_block_io_squeeze_ones<ColMajor>());
444  // clang-format on
445 }
Matrix3f m
#define max(a, b)
Definition: datatypes.h:20
static size_t RandomTargetBlockSize(const DSizes< Index, NumDims > &dims)
m m block(1, 0, 2, 2)<< 4
static void test_block_io_copy_data_from_source_to_target()
#define min(a, b)
Definition: datatypes.h:19
static void test_block_io_copy_using_reordered_dimensions()
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Tensor< Scalar_, NumIndices_, Options_, IndexType_ > & setRandom()
Definition: TensorBase.h:996
static Index GetInputIndex(Index output_index, const array< Index, NumDims > &output_to_input_dim_map, const array< Index, NumDims > &input_strides, const array< Index, NumDims > &output_strides)
static const Line3 l(Rot3(), 1, 1)
static void test_block_io_squeeze_ones()
#define VERIFY_IS_EQUAL(a, b)
Definition: main.h:386
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex TotalSize() const
A tensor expression mapping an existing array of data.
EIGEN_ALWAYS_INLINE DSizes< IndexType, NumDims > strides(const DSizes< IndexType, NumDims > &dimensions)
Definition: TensorBlock.h:26
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:74
static void test_block_io_copy_using_reordered_dimensions_squeeze()
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar * data()
Definition: Tensor.h:104
#define CALL_SUBTEST(FUNC)
Definition: main.h:399
static void test_block_io_zero_stride()
static DSizes< Index, NumDims > RandomDims(Index min, Index max)
EIGEN_DECLARE_TEST(cxx11_tensor_block_io)
static internal::TensorBlockShapeType RandomBlockShape()
static void test_block_io_copy_using_reordered_dimensions_do_not_squeeze()
#define CALL_SUBTESTS(NAME)
std::ptrdiff_t j
The tensor class.
Definition: Tensor.h:63
static unsigned int g_seed
Definition: main.h:170


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:34:07