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 #ifndef EIGEN_CXX11_TENSOR_TENSOR_DIMENSIONS_H
11 #define EIGEN_CXX11_TENSOR_TENSOR_DIMENSIONS_H
12 
13 
14 namespace Eigen {
15 
16 /** \internal
17   *
18   * \class TensorDimensions
19   * \ingroup CXX11_Tensor_Module
20   *
21   * \brief Set of classes used to encode and store the dimensions of a Tensor.
22   *
23   * The Sizes class encodes as part of the type the number of dimensions and the
24   * sizes corresponding to each dimension. It uses no storage space since it is
25   * entirely known at compile time.
26   * The DSizes class is its dynamic sibling: the number of dimensions is known
27   * at compile time but the sizes are set during execution.
28   *
29   * \sa Tensor
30   */
31 
32 // Boilerplate code
33 namespace internal {
34 
35 template<std::size_t n, typename Dimension> struct dget {
36   static const std::size_t value = get<n, Dimension>::value;
37 };
38 
39 
40 template<typename Index, std::size_t NumIndices, std::size_t n, bool RowMajor>
41 struct fixed_size_tensor_index_linearization_helper
42 {
43   template <typename Dimensions> EIGEN_DEVICE_FUNC
runfixed_size_tensor_index_linearization_helper44   static inline Index run(array<Index, NumIndices> const& indices,
45                           const Dimensions& dimensions)
46   {
47     return array_get<RowMajor ? n - 1 : (NumIndices - n)>(indices) +
48         dget<RowMajor ? n - 1 : (NumIndices - n), Dimensions>::value *
49         fixed_size_tensor_index_linearization_helper<Index, NumIndices, n - 1, RowMajor>::run(indices, dimensions);
50   }
51 };
52 
53 template<typename Index, std::size_t NumIndices, bool RowMajor>
54 struct fixed_size_tensor_index_linearization_helper<Index, NumIndices, 0, RowMajor>
55 {
56   template <typename Dimensions> EIGEN_DEVICE_FUNC
57   static inline Index run(array<Index, NumIndices> const&, const Dimensions&)
58   {
59     return 0;
60   }
61 };
62 
63 template<typename Index, std::size_t n>
64 struct fixed_size_tensor_index_extraction_helper
65 {
66   template <typename Dimensions> EIGEN_DEVICE_FUNC
67   static inline Index run(const Index index,
68                           const Dimensions& dimensions)
69   {
70     const Index mult = (index == n-1) ? 1 : 0;
71     return array_get<n-1>(dimensions) * mult +
72         fixed_size_tensor_index_extraction_helper<Index, n - 1>::run(index, dimensions);
73   }
74 };
75 
76 template<typename Index>
77 struct fixed_size_tensor_index_extraction_helper<Index, 0>
78 {
79   template <typename Dimensions> EIGEN_DEVICE_FUNC
80   static inline Index run(const Index,
81                           const Dimensions&)
82   {
83     return 0;
84   }
85   };
86 
87 }  // end namespace internal
88 
89 
90 // Fixed size
91 #ifndef EIGEN_EMULATE_CXX11_META_H
92 template <typename std::ptrdiff_t... Indices>
93 struct Sizes : internal::numeric_list<std::ptrdiff_t, Indices...> {
94   typedef internal::numeric_list<std::ptrdiff_t, Indices...> Base;
95   static const std::ptrdiff_t total_size = internal::arg_prod(Indices...);
96 
97   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t rank() const {
98     return Base::count;
99   }
100 
101   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t TotalSize() {
102     return internal::arg_prod(Indices...);
103   }
104 
105   EIGEN_DEVICE_FUNC Sizes() { }
106   template <typename DenseIndex>
107   explicit EIGEN_DEVICE_FUNC Sizes(const array<DenseIndex, Base::count>& /*indices*/) {
108     // todo: add assertion
109   }
110 #if EIGEN_HAS_VARIADIC_TEMPLATES
111   template <typename... DenseIndex> EIGEN_DEVICE_FUNC Sizes(DenseIndex...) { }
112   explicit EIGEN_DEVICE_FUNC Sizes(std::initializer_list<std::ptrdiff_t> /*l*/) {
113     // todo: add assertion
114   }
115 #endif
116 
117   template <typename T> Sizes& operator = (const T& /*other*/) {
118     // add assertion failure if the size of other is different
119     return *this;
120   }
121 
122   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t operator[] (const std::size_t index) const {
123     return internal::fixed_size_tensor_index_extraction_helper<std::ptrdiff_t, Base::count>::run(index, *this);
124   }
125 
126   template <typename DenseIndex> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
127   size_t IndexOfColMajor(const array<DenseIndex, Base::count>& indices) const {
128     return internal::fixed_size_tensor_index_linearization_helper<DenseIndex, Base::count, Base::count, false>::run(indices, *static_cast<const Base*>(this));
129   }
130   template <typename DenseIndex> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
131   size_t IndexOfRowMajor(const array<DenseIndex, Base::count>& indices) const {
132     return internal::fixed_size_tensor_index_linearization_helper<DenseIndex, Base::count, Base::count, true>::run(indices, *static_cast<const Base*>(this));
133   }
134 };
135 
136 namespace internal {
137 template <typename std::ptrdiff_t... Indices>
138 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t array_prod(const Sizes<Indices...>&) {
139   return Sizes<Indices...>::total_size;
140 }
141 }
142 
143 #else
144 
145 template <std::size_t n>
146 struct non_zero_size {
147   typedef internal::type2val<std::size_t, n> type;
148 };
149 template <>
150 struct non_zero_size<0> {
151   typedef internal::null_type type;
152 };
153 
154 template <std::size_t V1=0, std::size_t V2=0, std::size_t V3=0, std::size_t V4=0, std::size_t V5=0> struct Sizes {
155   typedef typename internal::make_type_list<typename non_zero_size<V1>::type, typename non_zero_size<V2>::type, typename non_zero_size<V3>::type, typename non_zero_size<V4>::type, typename non_zero_size<V5>::type >::type Base;
156   static const size_t count = Base::count;
157   static const std::size_t total_size = internal::arg_prod<Base>::value;
158 
159   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE size_t rank() const {
160     return count;
161   }
162 
163   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE size_t TotalSize() {
164     return internal::arg_prod<Base>::value;
165   }
166 
167   Sizes() { }
168   template <typename DenseIndex>
169   explicit Sizes(const array<DenseIndex, Base::count>& /*indices*/) {
170     // todo: add assertion
171   }
172   template <typename T> Sizes& operator = (const T& /*other*/) {
173     // add assertion failure if the size of other is different
174     return *this;
175   }
176 
177 #if EIGEN_HAS_VARIADIC_TEMPLATES
178   template <typename... DenseIndex> Sizes(DenseIndex... /*indices*/) { }
179   explicit Sizes(std::initializer_list<std::size_t>) {
180     // todo: add assertion
181   }
182 #else
183   EIGEN_DEVICE_FUNC explicit Sizes(const DenseIndex) {
184   }
185   EIGEN_DEVICE_FUNC Sizes(const DenseIndex, const DenseIndex) {
186   }
187   EIGEN_DEVICE_FUNC Sizes(const DenseIndex, const DenseIndex, const DenseIndex) {
188   }
189   EIGEN_DEVICE_FUNC Sizes(const DenseIndex, const DenseIndex, const DenseIndex, const DenseIndex) {
190   }
191   EIGEN_DEVICE_FUNC Sizes(const DenseIndex, const DenseIndex, const DenseIndex, const DenseIndex, const DenseIndex) {
192   }
193 #endif
194 
195   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex operator[] (const int index) const {
196     switch (index) {
197       case 0:
198         return internal::get<0, Base>::value;
199       case 1:
200         return internal::get<1, Base>::value;
201       case 2:
202         return internal::get<2, Base>::value;
203       case 3:
204         return internal::get<3, Base>::value;
205       case 4:
206         return internal::get<4, Base>::value;
207       default:
208         eigen_assert(false && "index overflow");
209         return static_cast<DenseIndex>(-1);
210     }
211   }
212 
213   template <typename DenseIndex> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
214   size_t IndexOfColMajor(const array<DenseIndex, Base::count>& indices) const {
215     return internal::fixed_size_tensor_index_linearization_helper<DenseIndex, Base::count, Base::count, false>::run(indices, *reinterpret_cast<const Base*>(this));
216   }
217   template <typename DenseIndex> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
218   size_t IndexOfRowMajor(const array<DenseIndex, Base::count>& indices) const {
219     return internal::fixed_size_tensor_index_linearization_helper<DenseIndex, Base::count, Base::count, true>::run(indices, *reinterpret_cast<const Base*>(this));
220   }
221 };
222 
223 namespace internal {
224 template <std::size_t V1, std::size_t V2, std::size_t V3, std::size_t V4, std::size_t V5>
225 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::size_t array_prod(const Sizes<V1, V2, V3, V4, V5>&) {
226   return Sizes<V1, V2, V3, V4, V5>::total_size;
227 }
228 }
229 
230 #endif
231 
232 // Boilerplate
233 namespace internal {
234 template<typename Index, std::size_t NumIndices, std::size_t n, bool RowMajor>
235 struct tensor_index_linearization_helper
236 {
237   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
238   Index run(array<Index, NumIndices> const& indices, array<Index, NumIndices> const& dimensions)
239   {
240     return array_get<RowMajor ? n : (NumIndices - n - 1)>(indices) +
241       array_get<RowMajor ? n : (NumIndices - n - 1)>(dimensions) *
242         tensor_index_linearization_helper<Index, NumIndices, n - 1, RowMajor>::run(indices, dimensions);
243   }
244 };
245 
246 template<typename Index, std::size_t NumIndices, bool RowMajor>
247 struct tensor_index_linearization_helper<Index, NumIndices, 0, RowMajor>
248 {
249   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
250   Index run(array<Index, NumIndices> const& indices, array<Index, NumIndices> const&)
251   {
252     return array_get<RowMajor ? 0 : NumIndices - 1>(indices);
253   }
254 };
255 }  // end namespace internal
256 
257 
258 
259 // Dynamic size
260 template <typename DenseIndex, int NumDims>
261 struct DSizes : array<DenseIndex, NumDims> {
262   typedef array<DenseIndex, NumDims> Base;
263   static const int count = NumDims;
264 
265   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE size_t rank() const {
266     return NumDims;
267   }
268 
269   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex TotalSize() const {
270     return (NumDims == 0) ? 1 : internal::array_prod(*static_cast<const Base*>(this));
271   }
272 
273   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DSizes() {
274     for (int i = 0 ; i < NumDims; ++i) {
275       (*this)[i] = 0;
276     }
277   }
278   EIGEN_DEVICE_FUNC explicit DSizes(const array<DenseIndex, NumDims>& a) : Base(a) { }
279 
280   EIGEN_DEVICE_FUNC explicit DSizes(const DenseIndex i0) {
281     eigen_assert(NumDims == 1);
282     (*this)[0] = i0;
283   }
284 
285 #if EIGEN_HAS_VARIADIC_TEMPLATES
286   template<typename... IndexTypes> EIGEN_DEVICE_FUNC
287   EIGEN_STRONG_INLINE explicit DSizes(DenseIndex firstDimension, DenseIndex secondDimension, IndexTypes... otherDimensions) : Base({{firstDimension, secondDimension, otherDimensions...}}) {
288     EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 2 == NumDims, YOU_MADE_A_PROGRAMMING_MISTAKE)
289   }
290 #else
291   EIGEN_DEVICE_FUNC DSizes(const DenseIndex i0, const DenseIndex i1) {
292     eigen_assert(NumDims == 2);
293     (*this)[0] = i0;
294     (*this)[1] = i1;
295   }
296   EIGEN_DEVICE_FUNC DSizes(const DenseIndex i0, const DenseIndex i1, const DenseIndex i2) {
297     eigen_assert(NumDims == 3);
298     (*this)[0] = i0;
299     (*this)[1] = i1;
300     (*this)[2] = i2;
301   }
302   EIGEN_DEVICE_FUNC DSizes(const DenseIndex i0, const DenseIndex i1, const DenseIndex i2, const DenseIndex i3) {
303     eigen_assert(NumDims == 4);
304     (*this)[0] = i0;
305     (*this)[1] = i1;
306     (*this)[2] = i2;
307     (*this)[3] = i3;
308   }
309   EIGEN_DEVICE_FUNC DSizes(const DenseIndex i0, const DenseIndex i1, const DenseIndex i2, const DenseIndex i3, const DenseIndex i4) {
310     eigen_assert(NumDims == 5);
311     (*this)[0] = i0;
312     (*this)[1] = i1;
313     (*this)[2] = i2;
314     (*this)[3] = i3;
315     (*this)[4] = i4;
316   }
317 #endif
318 
319   EIGEN_DEVICE_FUNC DSizes& operator = (const array<DenseIndex, NumDims>& other) {
320     *static_cast<Base*>(this) = other;
321     return *this;
322   }
323 
324   // A constexpr would be so much better here
325   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex IndexOfColMajor(const array<DenseIndex, NumDims>& indices) const {
326     return internal::tensor_index_linearization_helper<DenseIndex, NumDims, NumDims - 1, false>::run(indices, *static_cast<const Base*>(this));
327   }
328   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE DenseIndex IndexOfRowMajor(const array<DenseIndex, NumDims>& indices) const {
329     return internal::tensor_index_linearization_helper<DenseIndex, NumDims, NumDims - 1, true>::run(indices, *static_cast<const Base*>(this));
330   }
331 };
332 
333 
334 
335 
336 // Boilerplate
337 namespace internal {
338 template<typename Index, std::size_t NumIndices, std::size_t n, bool RowMajor>
339 struct tensor_vsize_index_linearization_helper
340 {
341   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
342   Index run(array<Index, NumIndices> const& indices, std::vector<DenseIndex> const& dimensions)
343   {
344     return array_get<RowMajor ? n : (NumIndices - n - 1)>(indices) +
345       array_get<RowMajor ? n : (NumIndices - n - 1)>(dimensions) *
346         tensor_vsize_index_linearization_helper<Index, NumIndices, n - 1, RowMajor>::run(indices, dimensions);
347   }
348 };
349 
350 template<typename Index, std::size_t NumIndices, bool RowMajor>
351 struct tensor_vsize_index_linearization_helper<Index, NumIndices, 0, RowMajor>
352 {
353   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
354   Index run(array<Index, NumIndices> const& indices, std::vector<DenseIndex> const&)
355   {
356     return array_get<RowMajor ? 0 : NumIndices - 1>(indices);
357   }
358 };
359 }  // end namespace internal
360 
361 
362 namespace internal {
363 
364 template <typename DenseIndex, int NumDims> struct array_size<const DSizes<DenseIndex, NumDims> > {
365   static const size_t value = NumDims;
366 };
367 template <typename DenseIndex, int NumDims> struct array_size<DSizes<DenseIndex, NumDims> > {
368   static const size_t value = NumDims;
369 };
370 #ifndef EIGEN_EMULATE_CXX11_META_H
371 template <typename std::ptrdiff_t... Indices> struct array_size<const Sizes<Indices...> > {
372 static const std::ptrdiff_t value = Sizes<Indices...>::count;
373 };
374 template <typename std::ptrdiff_t... Indices> struct array_size<Sizes<Indices...> > {
375 static const std::ptrdiff_t value = Sizes<Indices...>::count;
376 };
377 template <std::ptrdiff_t n, typename std::ptrdiff_t... Indices> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t array_get(const Sizes<Indices...>&) {
378   return get<n, internal::numeric_list<std::size_t, Indices...> >::value;
379 }
380 template <std::ptrdiff_t n> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t array_get(const Sizes<>&) {
381   eigen_assert(false && "should never be called");
382   return -1;
383 }
384 #else
385 template <std::size_t V1, std::size_t V2, std::size_t V3, std::size_t V4, std::size_t V5> struct array_size<const Sizes<V1,V2,V3,V4,V5> > {
386   static const size_t value = Sizes<V1,V2,V3,V4,V5>::count;
387 };
388 template <std::size_t V1, std::size_t V2, std::size_t V3, std::size_t V4, std::size_t V5> struct array_size<Sizes<V1,V2,V3,V4,V5> > {
389   static const size_t value = Sizes<V1,V2,V3,V4,V5>::count;
390 };
391 template <std::size_t n, std::size_t V1, std::size_t V2, std::size_t V3, std::size_t V4, std::size_t V5> EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::size_t array_get(const Sizes<V1,V2,V3,V4,V5>&) {
392   return get<n, typename Sizes<V1,V2,V3,V4,V5>::Base>::value;
393 }
394 
395 #endif
396 
397 
398 template <typename Dims1, typename Dims2, size_t n, size_t m>
399 struct sizes_match_below_dim {
400   static EIGEN_DEVICE_FUNC  inline bool run(Dims1&, Dims2&) {
401     return false;
402   }
403 };
404 template <typename Dims1, typename Dims2, size_t n>
405 struct sizes_match_below_dim<Dims1, Dims2, n, n> {
406   static EIGEN_DEVICE_FUNC  inline bool run(Dims1& dims1, Dims2& dims2) {
407     return (array_get<n-1>(dims1) == array_get<n-1>(dims2)) &
408         sizes_match_below_dim<Dims1, Dims2, n-1, n-1>::run(dims1, dims2);
409   }
410 };
411 template <typename Dims1, typename Dims2>
412 struct sizes_match_below_dim<Dims1, Dims2, 0, 0> {
413   static EIGEN_DEVICE_FUNC  inline bool run(Dims1&, Dims2&) {
414     return true;
415   }
416 };
417 
418 } // end namespace internal
419 
420 
421 template <typename Dims1, typename Dims2>
422 EIGEN_DEVICE_FUNC bool dimensions_match(Dims1& dims1, Dims2& dims2) {
423   return internal::sizes_match_below_dim<Dims1, Dims2, internal::array_size<Dims1>::value, internal::array_size<Dims2>::value>::run(dims1, dims2);
424 }
425 
426 } // end namespace Eigen
427 
428 #endif // EIGEN_CXX11_TENSOR_TENSOR_DIMENSIONS_H
429