1 // Copyright (c) 2018-2019
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)
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.
14 /// \file tensor.hpp Definition for the tensor template class
16 #ifndef BOOST_UBLAS_TENSOR_IMPL_HPP
17 #define BOOST_UBLAS_TENSOR_IMPL_HPP
19 #include <boost/config.hpp>
21 #include <initializer_list>
23 #include "algorithms.hpp"
24 #include "expression.hpp"
25 #include "expression_evaluation.hpp"
26 #include "extents.hpp"
27 #include "strides.hpp"
30 namespace boost { namespace numeric { namespace ublas {
32 template<class T, class F, class A>
35 template<class T, class F, class A>
38 template<class T, class A>
41 ///** \brief Base class for Tensor container models
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.
49 //class tensor_container:
50 // public detail::tensor_expression<C>
53 // static const unsigned complexity = 0;
54 // typedef C container_type;
55 // typedef tensor_tag type_category;
58 // const container_type &operator () () const {
59 // return *static_cast<const container_type *> (this);
62 // container_type &operator () () {
63 // return *static_cast<container_type *> (this);
69 /** @brief A dense tensor of values of type \c T.
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.
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
78 template<class T, class F = first_order, class A = std::vector<T,std::allocator<T>> >
80 public detail::tensor_expression<tensor<T, F, A>,tensor<T, F, A>>
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.");
86 using self_type = tensor<T, F, A>;
91 template<class derived_type>
92 using tensor_expression_type = detail::tensor_expression<self_type,derived_type>;
94 template<class derived_type>
95 using matrix_expression_type = matrix_expression<derived_type>;
97 template<class derived_type>
98 using vector_expression_type = vector_expression<derived_type>;
100 using super_type = tensor_expression_type<self_type>;
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>");
104 using array_type = A;
105 using layout_type = F;
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;
112 using reference = typename array_type::reference;
113 using const_reference = typename array_type::const_reference;
115 using pointer = typename array_type::pointer;
116 using const_pointer = typename array_type::const_pointer;
118 using iterator = typename array_type::iterator;
119 using const_iterator = typename array_type::const_iterator;
121 using reverse_iterator = typename array_type::reverse_iterator;
122 using const_reverse_iterator = typename array_type::const_reverse_iterator;
124 using tensor_temporary_type = self_type;
125 using storage_category = dense_tag;
127 using strides_type = basic_strides<std::size_t,layout_type>;
128 using extents_type = shape;
130 using matrix_type = matrix<value_type,layout_type,array_type>;
131 using vector_type = vector<value_type,array_type>;
134 /** @brief Constructs a tensor.
136 * @note the tensor is empty.
137 * @note the tensor needs to reshaped for further use.
142 : tensor_expression_type<self_type>() // container_type
150 /** @brief Constructs a tensor with an initializer list
152 * By default, its elements are initialized to 0.
154 * @code tensor<float> A{4,2,3}; @endcode
156 * @param l initializer list for setting the dimension extents of the tensor
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())
168 /** @brief Constructs a tensor with a \c shape
170 * By default, its elements are initialized to 0.
172 * @code tensor<float> A{extents{4,2,3}}; @endcode
174 * @param s initial tensor dimension extents
176 explicit BOOST_UBLAS_INLINE
177 tensor (extents_type const& s)
178 : tensor_expression_type<self_type>() //tensor_container<self_type>()
180 , strides_ (extents_)
181 , data_ (extents_.product())
185 /** @brief Constructs a tensor with a \c shape and initiates it with one-dimensional data
187 * @code tensor<float> A{extents{4,2,3}, array }; @endcode
190 * @param s initial tensor dimension extents
191 * @param a container of \c array_type that is copied according to the storage layout
194 tensor (extents_type const& s, const array_type &a)
195 : tensor_expression_type<self_type>() //tensor_container<self_type>()
197 , strides_ (extents_)
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.");
206 /** @brief Constructs a tensor using a shape tuple and initiates it with a value.
208 * @code tensor<float> A{extents{4,2,3}, 1 }; @endcode
210 * @param e initial tensor dimension extents
211 * @param i initial value of all elements of type \c value_type
214 tensor (extents_type const& e, const value_type &i)
215 : tensor_expression_type<self_type>() //tensor_container<self_type> ()
217 , strides_ (extents_)
218 , data_ (extents_.product(), i)
223 /** @brief Constructs a tensor from another tensor
225 * @param v tensor to be copied.
228 tensor (const tensor &v)
229 : tensor_expression_type<self_type>()
230 , extents_ (v.extents_)
231 , strides_ (v.strides_)
237 /** @brief Constructs a tensor from another tensor
239 * @param v tensor to be moved.
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_ ))
250 /** @brief Constructs a tensor with a matrix
252 * \note Initially the tensor will be two-dimensional.
254 * @param v matrix to be copied.
257 tensor (const matrix_type &v)
258 : tensor_expression_type<self_type>()
264 extents_ = extents_type{v.size1(),v.size2()};
265 strides_ = strides_type(extents_);
269 /** @brief Constructs a tensor with a matrix
271 * \note Initially the tensor will be two-dimensional.
273 * @param v matrix to be moved.
276 tensor (matrix_type &&v)
277 : tensor_expression_type<self_type>()
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());
289 /** @brief Constructs a tensor using a \c vector
291 * @note It is assumed that vector is column vector
292 * @note Initially the tensor will be one-dimensional.
294 * @param v vector to be copied.
297 tensor (const vector_type &v)
298 : tensor_expression_type<self_type>()
304 extents_ = extents_type{data_.size(),1};
305 strides_ = strides_type(extents_);
309 /** @brief Constructs a tensor using a \c vector
311 * @param v vector to be moved.
314 tensor (vector_type &&v)
315 : tensor_expression_type<self_type>()
321 extents_ = extents_type{v.size(),1};
322 strides_ = strides_type(extents_);
323 data_ = std::move(v.data());
328 /** @brief Constructs a tensor with another tensor with a different layout
330 * @param other tensor with a different layout to be copied.
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())
340 copy(this->rank(), this->extents().data(),
341 this->data(), this->strides().data(),
342 other.data(), other.strides().data());
345 /** @brief Constructs a tensor with an tensor expression
347 * @code tensor<float> A = B + 3 * C; @endcode
349 * @note type must be specified of tensor must be specified.
350 * @note dimension extents are extracted from tensors within the expression.
352 * @param expr tensor expression
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() )
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 );
367 /** @brief Constructs a tensor with a matrix expression
369 * @code tensor<float> A = B + 3 * C; @endcode
371 * @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
372 * @note extents are automatically extracted from the temporary matrix
374 * @param expr matrix expression
377 template<class derived_type>
378 tensor (const matrix_expression_type<derived_type> &expr)
379 : tensor( matrix_type ( expr ) )
383 /** @brief Constructs a tensor with a vector expression
385 * @code tensor<float> A = b + 3 * b; @endcode
387 * @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
388 * @note extents are automatically extracted from the temporary matrix
390 * @param expr vector expression
393 template<class derived_type>
394 tensor (const vector_expression_type<derived_type> &expr)
395 : tensor( vector_type ( expr ) )
399 /** @brief Evaluates the tensor_expression and assigns the results to the tensor
401 * @code A = B + C * 2; @endcode
403 * @note rank and dimension extents of the tensors in the expressions must conform with this tensor.
405 * @param expr expression that is evaluated.
408 template<class derived_type>
409 tensor &operator = (const tensor_expression_type<derived_type> &expr)
411 detail::eval(*this, expr);
415 tensor& operator=(tensor other)
421 tensor& operator=(const_reference v)
423 std::fill(this->begin(), this->end(), v);
427 /** @brief Returns true if the tensor is empty (\c size==0) */
429 bool empty () const {
430 return this->data_.empty();
434 /** @brief Returns the size of the tensor */
436 size_type size () const {
437 return this->data_.size ();
440 /** @brief Returns the size of the tensor */
442 size_type size (size_type r) const {
443 return this->extents_.at(r);
446 /** @brief Returns the number of dimensions/modes of the tensor */
448 size_type rank () const {
449 return this->extents_.size();
452 /** @brief Returns the number of dimensions/modes of the tensor */
454 size_type order () const {
455 return this->extents_.size();
458 /** @brief Returns the strides of the tensor */
460 strides_type const& strides () const {
461 return this->strides_;
464 /** @brief Returns the extents of the tensor */
466 extents_type const& extents () const {
467 return this->extents_;
471 /** @brief Returns a \c const reference to the container. */
473 const_pointer data () const {
474 return this->data_.data();
477 /** @brief Returns a \c const reference to the container. */
480 return this->data_.data();
483 /** @brief Element access using a single index.
485 * @code auto a = A[i]; @endcode
487 * @param i zero-based index where 0 <= i < this->size()
490 const_reference operator [] (size_type i) const {
491 return this->data_[i];
494 /** @brief Element access using a single index.
497 * @code A[i] = a; @endcode
499 * @param i zero-based index where 0 <= i < this->size()
502 reference operator [] (size_type i)
504 return this->data_[i];
508 /** @brief Element access using a multi-index or single-index.
511 * @code auto a = A.at(i,j,k); @endcode or
512 * @code auto a = A.at(i); @endcode
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()
517 template<class ... size_types>
519 const_reference at (size_type i, size_types ... is) const {
520 if constexpr (sizeof...(is) == 0)
521 return this->data_[i];
523 return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
526 /** @brief Element access using a multi-index or single-index.
529 * @code A.at(i,j,k) = a; @endcode or
530 * @code A.at(i) = a; @endcode
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()
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];
541 return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
547 /** @brief Element access using a single index.
550 * @code A(i) = a; @endcode
552 * @param i zero-based index where 0 <= i < this->size()
555 const_reference operator()(size_type i) const {
556 return this->data_[i];
560 /** @brief Element access using a single index.
562 * @code A(i) = a; @endcode
564 * @param i zero-based index where 0 <= i < this->size()
567 reference operator()(size_type i){
568 return this->data_[i];
574 /** @brief Generates a tensor index for tensor contraction
577 * @code auto Ai = A(_i,_j,k); @endcode
579 * @param i placeholder
580 * @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
583 template<std::size_t I, class ... index_types>
584 decltype(auto) operator() (index::index_type<I> p, index_types ... ps) const
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.");
590 return std::make_pair( std::cref(*this), std::make_tuple( p, std::forward<index_types>(ps)... ) );
597 /** @brief Reshapes the tensor
600 * (1) @code A.reshape(extents{m,n,o}); @endcode or
601 * (2) @code A.reshape(extents{m,n,o},4); @endcode
603 * If the size of this smaller than the specified extents than
604 * default constructed (1) or specified (2) value is appended.
606 * @note rank of the tensor might also change.
608 * @param e extents with which the tensor is reshaped.
609 * @param v value which is appended if the tensor is enlarged.
612 void reshape (extents_type const& e, value_type v = value_type{})
615 this->strides_ = strides_type(this->extents_);
617 if(e.product() != this->size())
618 this->data_.resize (this->extents_.product(), v);
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_);
632 /// \brief return an iterator on the first element of the tensor
634 const_iterator begin () const {
635 return data_.begin ();
638 /// \brief return an iterator on the first element of the tensor
640 const_iterator cbegin () const {
641 return data_.cbegin ();
644 /// \brief return an iterator after the last element of the tensor
646 const_iterator end () const {
650 /// \brief return an iterator after the last element of the tensor
652 const_iterator cend () const {
653 return data_.cend ();
656 /// \brief Return an iterator on the first element of the tensor
659 return data_.begin();
662 /// \brief Return an iterator at the end of the tensor
668 /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
670 const_reverse_iterator rbegin () const {
671 return data_.rbegin();
674 /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
676 const_reverse_iterator crbegin () const {
677 return data_.crbegin();
680 /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
682 const_reverse_iterator rend () const {
686 /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
688 const_reverse_iterator crend () const {
689 return data_.crend();
692 /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
694 reverse_iterator rbegin () {
695 return data_.rbegin();
698 /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
700 reverse_iterator rend () {
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_);
723 extents_type extents_;
724 strides_type strides_;