]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/numeric/ublas/tensor/tensor.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / numeric / ublas / tensor / tensor.hpp
1 // Copyright (c) 2018-2019
2 // Cem Bassoy
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See
5 // accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // The authors gratefully acknowledge the support of
9 // Fraunhofer and Google in producing this work
10 // which started as a Google Summer of Code project.
11 //
12
13
14 /// \file tensor.hpp Definition for the tensor template class
15
16 #ifndef BOOST_UBLAS_TENSOR_IMPL_HPP
17 #define BOOST_UBLAS_TENSOR_IMPL_HPP
18
19 #include <boost/config.hpp>
20
21 #include <initializer_list>
22
23 #include "algorithms.hpp"
24 #include "expression.hpp"
25 #include "expression_evaluation.hpp"
26 #include "extents.hpp"
27 #include "strides.hpp"
28 #include "index.hpp"
29
30 namespace boost { namespace numeric { namespace ublas {
31
32 template<class T, class F, class A>
33 class tensor;
34
35 template<class T, class F, class A>
36 class matrix;
37
38 template<class T, class A>
39 class vector;
40
41 ///** \brief Base class for Tensor container models
42 // *
43 // * it does not model the Tensor concept but all derived types should.
44 // * The class defines a common base type and some common interface for all
45 // * statically derived Tensor classes
46 // * We implement the casts to the statically derived type.
47 // */
48 //template<class C>
49 //class tensor_container:
50 // public detail::tensor_expression<C>
51 //{
52 //public:
53 // static const unsigned complexity = 0;
54 // typedef C container_type;
55 // typedef tensor_tag type_category;
56
57 // BOOST_UBLAS_INLINE
58 // const container_type &operator () () const {
59 // return *static_cast<const container_type *> (this);
60 // }
61 // BOOST_UBLAS_INLINE
62 // container_type &operator () () {
63 // return *static_cast<container_type *> (this);
64 // }
65 //};
66
67
68
69 /** @brief A dense tensor of values of type \c T.
70 *
71 * For a \f$n\f$-dimensional tensor \f$v\f$ and \f$0\leq i < n\f$ every element \f$v_i\f$ is mapped
72 * to the \f$i\f$-th element of the container. A storage type \c A can be specified which defaults to \c unbounded_array.
73 * Elements are constructed by \c A, which need not initialise their value.
74 *
75 * @tparam T type of the objects stored in the tensor (like int, double, complex,...)
76 * @tparam A The type of the storage array of the tensor. Default is \c unbounded_array<T>. \c <bounded_array<T> and \c std::vector<T> can also be used
77 */
78 template<class T, class F = first_order, class A = std::vector<T,std::allocator<T>> >
79 class tensor:
80 public detail::tensor_expression<tensor<T, F, A>,tensor<T, F, A>>
81 {
82
83 static_assert( std::is_same<F,first_order>::value ||
84 std::is_same<F,last_order >::value, "boost::numeric::tensor template class only supports first- or last-order storage formats.");
85
86 using self_type = tensor<T, F, A>;
87 public:
88
89
90
91 template<class derived_type>
92 using tensor_expression_type = detail::tensor_expression<self_type,derived_type>;
93
94 template<class derived_type>
95 using matrix_expression_type = matrix_expression<derived_type>;
96
97 template<class derived_type>
98 using vector_expression_type = vector_expression<derived_type>;
99
100 using super_type = tensor_expression_type<self_type>;
101
102 // static_assert(std::is_same_v<tensor_expression_type<self_type>, detail::tensor_expression<tensor<T,F,A>,tensor<T,F,A>>>, "tensor_expression_type<self_type>");
103
104 using array_type = A;
105 using layout_type = F;
106
107
108 using size_type = typename array_type::size_type;
109 using difference_type = typename array_type::difference_type;
110 using value_type = typename array_type::value_type;
111
112 using reference = typename array_type::reference;
113 using const_reference = typename array_type::const_reference;
114
115 using pointer = typename array_type::pointer;
116 using const_pointer = typename array_type::const_pointer;
117
118 using iterator = typename array_type::iterator;
119 using const_iterator = typename array_type::const_iterator;
120
121 using reverse_iterator = typename array_type::reverse_iterator;
122 using const_reverse_iterator = typename array_type::const_reverse_iterator;
123
124 using tensor_temporary_type = self_type;
125 using storage_category = dense_tag;
126
127 using strides_type = basic_strides<std::size_t,layout_type>;
128 using extents_type = shape;
129
130 using matrix_type = matrix<value_type,layout_type,array_type>;
131 using vector_type = vector<value_type,array_type>;
132
133
134 /** @brief Constructs a tensor.
135 *
136 * @note the tensor is empty.
137 * @note the tensor needs to reshaped for further use.
138 *
139 */
140 BOOST_UBLAS_INLINE
141 constexpr tensor ()
142 : tensor_expression_type<self_type>() // container_type
143 , extents_()
144 , strides_()
145 , data_()
146 {
147 }
148
149
150 /** @brief Constructs a tensor with an initializer list
151 *
152 * By default, its elements are initialized to 0.
153 *
154 * @code tensor<float> A{4,2,3}; @endcode
155 *
156 * @param l initializer list for setting the dimension extents of the tensor
157 */
158 explicit BOOST_UBLAS_INLINE
159 tensor (std::initializer_list<size_type> l)
160 : tensor_expression_type<self_type>()
161 , extents_ (std::move(l))
162 , strides_ (extents_)
163 , data_ (extents_.product())
164 {
165 }
166
167
168 /** @brief Constructs a tensor with a \c shape
169 *
170 * By default, its elements are initialized to 0.
171 *
172 * @code tensor<float> A{extents{4,2,3}}; @endcode
173 *
174 * @param s initial tensor dimension extents
175 */
176 explicit BOOST_UBLAS_INLINE
177 tensor (extents_type const& s)
178 : tensor_expression_type<self_type>() //tensor_container<self_type>()
179 , extents_ (s)
180 , strides_ (extents_)
181 , data_ (extents_.product())
182 {}
183
184
185 /** @brief Constructs a tensor with a \c shape and initiates it with one-dimensional data
186 *
187 * @code tensor<float> A{extents{4,2,3}, array }; @endcode
188 *
189 *
190 * @param s initial tensor dimension extents
191 * @param a container of \c array_type that is copied according to the storage layout
192 */
193 BOOST_UBLAS_INLINE
194 tensor (extents_type const& s, const array_type &a)
195 : tensor_expression_type<self_type>() //tensor_container<self_type>()
196 , extents_ (s)
197 , strides_ (extents_)
198 , data_ (a)
199 {
200 if(this->extents_.product() != this->data_.size())
201 throw std::runtime_error("Error in boost::numeric::ublas::tensor: size of provided data and specified extents do not match.");
202 }
203
204
205
206 /** @brief Constructs a tensor using a shape tuple and initiates it with a value.
207 *
208 * @code tensor<float> A{extents{4,2,3}, 1 }; @endcode
209 *
210 * @param e initial tensor dimension extents
211 * @param i initial value of all elements of type \c value_type
212 */
213 BOOST_UBLAS_INLINE
214 tensor (extents_type const& e, const value_type &i)
215 : tensor_expression_type<self_type>() //tensor_container<self_type> ()
216 , extents_ (e)
217 , strides_ (extents_)
218 , data_ (extents_.product(), i)
219 {}
220
221
222
223 /** @brief Constructs a tensor from another tensor
224 *
225 * @param v tensor to be copied.
226 */
227 BOOST_UBLAS_INLINE
228 tensor (const tensor &v)
229 : tensor_expression_type<self_type>()
230 , extents_ (v.extents_)
231 , strides_ (v.strides_)
232 , data_ (v.data_ )
233 {}
234
235
236
237 /** @brief Constructs a tensor from another tensor
238 *
239 * @param v tensor to be moved.
240 */
241 BOOST_UBLAS_INLINE
242 tensor (tensor &&v)
243 : tensor_expression_type<self_type>() //tensor_container<self_type> ()
244 , extents_ (std::move(v.extents_))
245 , strides_ (std::move(v.strides_))
246 , data_ (std::move(v.data_ ))
247 {}
248
249
250 /** @brief Constructs a tensor with a matrix
251 *
252 * \note Initially the tensor will be two-dimensional.
253 *
254 * @param v matrix to be copied.
255 */
256 BOOST_UBLAS_INLINE
257 tensor (const matrix_type &v)
258 : tensor_expression_type<self_type>()
259 , extents_ ()
260 , strides_ ()
261 , data_ (v.data())
262 {
263 if(!data_.empty()){
264 extents_ = extents_type{v.size1(),v.size2()};
265 strides_ = strides_type(extents_);
266 }
267 }
268
269 /** @brief Constructs a tensor with a matrix
270 *
271 * \note Initially the tensor will be two-dimensional.
272 *
273 * @param v matrix to be moved.
274 */
275 BOOST_UBLAS_INLINE
276 tensor (matrix_type &&v)
277 : tensor_expression_type<self_type>()
278 , extents_ {}
279 , strides_ {}
280 , data_ {}
281 {
282 if(v.size1()*v.size2() != 0){
283 extents_ = extents_type{v.size1(),v.size2()};
284 strides_ = strides_type(extents_);
285 data_ = std::move(v.data());
286 }
287 }
288
289 /** @brief Constructs a tensor using a \c vector
290 *
291 * @note It is assumed that vector is column vector
292 * @note Initially the tensor will be one-dimensional.
293 *
294 * @param v vector to be copied.
295 */
296 BOOST_UBLAS_INLINE
297 tensor (const vector_type &v)
298 : tensor_expression_type<self_type>()
299 , extents_ ()
300 , strides_ ()
301 , data_ (v.data())
302 {
303 if(!data_.empty()){
304 extents_ = extents_type{data_.size(),1};
305 strides_ = strides_type(extents_);
306 }
307 }
308
309 /** @brief Constructs a tensor using a \c vector
310 *
311 * @param v vector to be moved.
312 */
313 BOOST_UBLAS_INLINE
314 tensor (vector_type &&v)
315 : tensor_expression_type<self_type>()
316 , extents_ {}
317 , strides_ {}
318 , data_ {}
319 {
320 if(v.size() != 0){
321 extents_ = extents_type{v.size(),1};
322 strides_ = strides_type(extents_);
323 data_ = std::move(v.data());
324 }
325 }
326
327
328 /** @brief Constructs a tensor with another tensor with a different layout
329 *
330 * @param other tensor with a different layout to be copied.
331 */
332 BOOST_UBLAS_INLINE
333 template<class other_layout>
334 tensor (const tensor<value_type, other_layout> &other)
335 : tensor_expression_type<self_type> ()
336 , extents_ (other.extents())
337 , strides_ (other.extents())
338 , data_ (other.extents().product())
339 {
340 copy(this->rank(), this->extents().data(),
341 this->data(), this->strides().data(),
342 other.data(), other.strides().data());
343 }
344
345 /** @brief Constructs a tensor with an tensor expression
346 *
347 * @code tensor<float> A = B + 3 * C; @endcode
348 *
349 * @note type must be specified of tensor must be specified.
350 * @note dimension extents are extracted from tensors within the expression.
351 *
352 * @param expr tensor expression
353 */
354 BOOST_UBLAS_INLINE
355 template<class derived_type>
356 tensor (const tensor_expression_type<derived_type> &expr)
357 : tensor_expression_type<self_type> ()
358 , extents_ ( detail::retrieve_extents(expr) )
359 , strides_ ( extents_ )
360 , data_ ( extents_.product() )
361 {
362 static_assert( detail::has_tensor_types<self_type, tensor_expression_type<derived_type>>::value,
363 "Error in boost::numeric::ublas::tensor: expression does not contain a tensor. cannot retrieve shape.");
364 detail::eval( *this, expr );
365 }
366
367 /** @brief Constructs a tensor with a matrix expression
368 *
369 * @code tensor<float> A = B + 3 * C; @endcode
370 *
371 * @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
372 * @note extents are automatically extracted from the temporary matrix
373 *
374 * @param expr matrix expression
375 */
376 BOOST_UBLAS_INLINE
377 template<class derived_type>
378 tensor (const matrix_expression_type<derived_type> &expr)
379 : tensor( matrix_type ( expr ) )
380 {
381 }
382
383 /** @brief Constructs a tensor with a vector expression
384 *
385 * @code tensor<float> A = b + 3 * b; @endcode
386 *
387 * @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
388 * @note extents are automatically extracted from the temporary matrix
389 *
390 * @param expr vector expression
391 */
392 BOOST_UBLAS_INLINE
393 template<class derived_type>
394 tensor (const vector_expression_type<derived_type> &expr)
395 : tensor( vector_type ( expr ) )
396 {
397 }
398
399 /** @brief Evaluates the tensor_expression and assigns the results to the tensor
400 *
401 * @code A = B + C * 2; @endcode
402 *
403 * @note rank and dimension extents of the tensors in the expressions must conform with this tensor.
404 *
405 * @param expr expression that is evaluated.
406 */
407 BOOST_UBLAS_INLINE
408 template<class derived_type>
409 tensor &operator = (const tensor_expression_type<derived_type> &expr)
410 {
411 detail::eval(*this, expr);
412 return *this;
413 }
414
415 tensor& operator=(tensor other)
416 {
417 swap (*this, other);
418 return *this;
419 }
420
421 tensor& operator=(const_reference v)
422 {
423 std::fill(this->begin(), this->end(), v);
424 return *this;
425 }
426
427 /** @brief Returns true if the tensor is empty (\c size==0) */
428 BOOST_UBLAS_INLINE
429 bool empty () const {
430 return this->data_.empty();
431 }
432
433
434 /** @brief Returns the size of the tensor */
435 BOOST_UBLAS_INLINE
436 size_type size () const {
437 return this->data_.size ();
438 }
439
440 /** @brief Returns the size of the tensor */
441 BOOST_UBLAS_INLINE
442 size_type size (size_type r) const {
443 return this->extents_.at(r);
444 }
445
446 /** @brief Returns the number of dimensions/modes of the tensor */
447 BOOST_UBLAS_INLINE
448 size_type rank () const {
449 return this->extents_.size();
450 }
451
452 /** @brief Returns the number of dimensions/modes of the tensor */
453 BOOST_UBLAS_INLINE
454 size_type order () const {
455 return this->extents_.size();
456 }
457
458 /** @brief Returns the strides of the tensor */
459 BOOST_UBLAS_INLINE
460 strides_type const& strides () const {
461 return this->strides_;
462 }
463
464 /** @brief Returns the extents of the tensor */
465 BOOST_UBLAS_INLINE
466 extents_type const& extents () const {
467 return this->extents_;
468 }
469
470
471 /** @brief Returns a \c const reference to the container. */
472 BOOST_UBLAS_INLINE
473 const_pointer data () const {
474 return this->data_.data();
475 }
476
477 /** @brief Returns a \c const reference to the container. */
478 BOOST_UBLAS_INLINE
479 pointer data () {
480 return this->data_.data();
481 }
482
483 /** @brief Element access using a single index.
484 *
485 * @code auto a = A[i]; @endcode
486 *
487 * @param i zero-based index where 0 <= i < this->size()
488 */
489 BOOST_UBLAS_INLINE
490 const_reference operator [] (size_type i) const {
491 return this->data_[i];
492 }
493
494 /** @brief Element access using a single index.
495 *
496 *
497 * @code A[i] = a; @endcode
498 *
499 * @param i zero-based index where 0 <= i < this->size()
500 */
501 BOOST_UBLAS_INLINE
502 reference operator [] (size_type i)
503 {
504 return this->data_[i];
505 }
506
507
508 /** @brief Element access using a multi-index or single-index.
509 *
510 *
511 * @code auto a = A.at(i,j,k); @endcode or
512 * @code auto a = A.at(i); @endcode
513 *
514 * @param i zero-based index where 0 <= i < this->size() if sizeof...(is) == 0, else 0<= i < this->size(0)
515 * @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
516 */
517 template<class ... size_types>
518 BOOST_UBLAS_INLINE
519 const_reference at (size_type i, size_types ... is) const {
520 if constexpr (sizeof...(is) == 0)
521 return this->data_[i];
522 else
523 return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
524 }
525
526 /** @brief Element access using a multi-index or single-index.
527 *
528 *
529 * @code A.at(i,j,k) = a; @endcode or
530 * @code A.at(i) = a; @endcode
531 *
532 * @param i zero-based index where 0 <= i < this->size() if sizeof...(is) == 0, else 0<= i < this->size(0)
533 * @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
534 */
535 BOOST_UBLAS_INLINE
536 template<class ... size_types>
537 reference at (size_type i, size_types ... is) {
538 if constexpr (sizeof...(is) == 0)
539 return this->data_[i];
540 else
541 return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
542 }
543
544
545
546
547 /** @brief Element access using a single index.
548 *
549 *
550 * @code A(i) = a; @endcode
551 *
552 * @param i zero-based index where 0 <= i < this->size()
553 */
554 BOOST_UBLAS_INLINE
555 const_reference operator()(size_type i) const {
556 return this->data_[i];
557 }
558
559
560 /** @brief Element access using a single index.
561 *
562 * @code A(i) = a; @endcode
563 *
564 * @param i zero-based index where 0 <= i < this->size()
565 */
566 BOOST_UBLAS_INLINE
567 reference operator()(size_type i){
568 return this->data_[i];
569 }
570
571
572
573
574 /** @brief Generates a tensor index for tensor contraction
575 *
576 *
577 * @code auto Ai = A(_i,_j,k); @endcode
578 *
579 * @param i placeholder
580 * @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
581 */
582 BOOST_UBLAS_INLINE
583 template<std::size_t I, class ... index_types>
584 decltype(auto) operator() (index::index_type<I> p, index_types ... ps) const
585 {
586 constexpr auto N = sizeof...(ps)+1;
587 if( N != this->rank() )
588 throw std::runtime_error("Error in boost::numeric::ublas::operator(): size of provided index_types does not match with the rank.");
589
590 return std::make_pair( std::cref(*this), std::make_tuple( p, std::forward<index_types>(ps)... ) );
591 }
592
593
594
595
596
597 /** @brief Reshapes the tensor
598 *
599 *
600 * (1) @code A.reshape(extents{m,n,o}); @endcode or
601 * (2) @code A.reshape(extents{m,n,o},4); @endcode
602 *
603 * If the size of this smaller than the specified extents than
604 * default constructed (1) or specified (2) value is appended.
605 *
606 * @note rank of the tensor might also change.
607 *
608 * @param e extents with which the tensor is reshaped.
609 * @param v value which is appended if the tensor is enlarged.
610 */
611 BOOST_UBLAS_INLINE
612 void reshape (extents_type const& e, value_type v = value_type{})
613 {
614 this->extents_ = e;
615 this->strides_ = strides_type(this->extents_);
616
617 if(e.product() != this->size())
618 this->data_.resize (this->extents_.product(), v);
619 }
620
621
622
623
624
625 friend void swap(tensor& lhs, tensor& rhs) {
626 std::swap(lhs.data_ , rhs.data_ );
627 std::swap(lhs.extents_, rhs.extents_);
628 std::swap(lhs.strides_, rhs.strides_);
629 }
630
631
632 /// \brief return an iterator on the first element of the tensor
633 BOOST_UBLAS_INLINE
634 const_iterator begin () const {
635 return data_.begin ();
636 }
637
638 /// \brief return an iterator on the first element of the tensor
639 BOOST_UBLAS_INLINE
640 const_iterator cbegin () const {
641 return data_.cbegin ();
642 }
643
644 /// \brief return an iterator after the last element of the tensor
645 BOOST_UBLAS_INLINE
646 const_iterator end () const {
647 return data_.end();
648 }
649
650 /// \brief return an iterator after the last element of the tensor
651 BOOST_UBLAS_INLINE
652 const_iterator cend () const {
653 return data_.cend ();
654 }
655
656 /// \brief Return an iterator on the first element of the tensor
657 BOOST_UBLAS_INLINE
658 iterator begin () {
659 return data_.begin();
660 }
661
662 /// \brief Return an iterator at the end of the tensor
663 BOOST_UBLAS_INLINE
664 iterator end () {
665 return data_.end();
666 }
667
668 /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
669 BOOST_UBLAS_INLINE
670 const_reverse_iterator rbegin () const {
671 return data_.rbegin();
672 }
673
674 /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
675 BOOST_UBLAS_INLINE
676 const_reverse_iterator crbegin () const {
677 return data_.crbegin();
678 }
679
680 /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
681 BOOST_UBLAS_INLINE
682 const_reverse_iterator rend () const {
683 return data_.rend();
684 }
685
686 /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
687 BOOST_UBLAS_INLINE
688 const_reverse_iterator crend () const {
689 return data_.crend();
690 }
691
692 /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
693 BOOST_UBLAS_INLINE
694 reverse_iterator rbegin () {
695 return data_.rbegin();
696 }
697
698 /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
699 BOOST_UBLAS_INLINE
700 reverse_iterator rend () {
701 return data_.rend();
702 }
703
704
705 #if 0
706 // -------------
707 // Serialization
708 // -------------
709
710 /// Serialize a tensor into and archive as defined in Boost
711 /// \param ar Archive object. Can be a flat file, an XML file or any other stream
712 /// \param file_version Optional file version (not yet used)
713 template<class Archive>
714 void serialize(Archive & ar, const unsigned int /* file_version */){
715 ar & serialization::make_nvp("data",data_);
716 }
717 #endif
718
719
720
721 private:
722
723 extents_type extents_;
724 strides_type strides_;
725 array_type data_;
726 };
727
728 }}} // namespaces
729
730
731
732
733
734 #endif