blob: ea4521c29efc515078a7406e9b0ae901853e2e1d [file] [log] [blame]
// Copyright (c) 2018-2019
// Cem Bassoy
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// The authors gratefully acknowledge the support of
// Fraunhofer and Google in producing this work
// which started as a Google Summer of Code project.
//
/// \file tensor.hpp Definition for the tensor template class
#ifndef BOOST_UBLAS_TENSOR_IMPL_HPP
#define BOOST_UBLAS_TENSOR_IMPL_HPP
#include <boost/config.hpp>
#include <initializer_list>
#include "algorithms.hpp"
#include "expression.hpp"
#include "expression_evaluation.hpp"
#include "extents.hpp"
#include "strides.hpp"
#include "index.hpp"
namespace boost { namespace numeric { namespace ublas {
template<class T, class F, class A>
class tensor;
template<class T, class F, class A>
class matrix;
template<class T, class A>
class vector;
///** \brief Base class for Tensor container models
// *
// * it does not model the Tensor concept but all derived types should.
// * The class defines a common base type and some common interface for all
// * statically derived Tensor classes
// * We implement the casts to the statically derived type.
// */
//template<class C>
//class tensor_container:
// public detail::tensor_expression<C>
//{
//public:
// static const unsigned complexity = 0;
// typedef C container_type;
// typedef tensor_tag type_category;
// BOOST_UBLAS_INLINE
// const container_type &operator () () const {
// return *static_cast<const container_type *> (this);
// }
// BOOST_UBLAS_INLINE
// container_type &operator () () {
// return *static_cast<container_type *> (this);
// }
//};
/** @brief A dense tensor of values of type \c T.
*
* 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
* to the \f$i\f$-th element of the container. A storage type \c A can be specified which defaults to \c unbounded_array.
* Elements are constructed by \c A, which need not initialise their value.
*
* @tparam T type of the objects stored in the tensor (like int, double, complex,...)
* @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
*/
template<class T, class F = first_order, class A = std::vector<T,std::allocator<T>> >
class tensor:
public detail::tensor_expression<tensor<T, F, A>,tensor<T, F, A>>
{
static_assert( std::is_same<F,first_order>::value ||
std::is_same<F,last_order >::value, "boost::numeric::tensor template class only supports first- or last-order storage formats.");
using self_type = tensor<T, F, A>;
public:
template<class derived_type>
using tensor_expression_type = detail::tensor_expression<self_type,derived_type>;
template<class derived_type>
using matrix_expression_type = matrix_expression<derived_type>;
template<class derived_type>
using vector_expression_type = vector_expression<derived_type>;
using super_type = tensor_expression_type<self_type>;
// 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>");
using array_type = A;
using layout_type = F;
using size_type = typename array_type::size_type;
using difference_type = typename array_type::difference_type;
using value_type = typename array_type::value_type;
using reference = typename array_type::reference;
using const_reference = typename array_type::const_reference;
using pointer = typename array_type::pointer;
using const_pointer = typename array_type::const_pointer;
using iterator = typename array_type::iterator;
using const_iterator = typename array_type::const_iterator;
using reverse_iterator = typename array_type::reverse_iterator;
using const_reverse_iterator = typename array_type::const_reverse_iterator;
using tensor_temporary_type = self_type;
using storage_category = dense_tag;
using strides_type = basic_strides<std::size_t,layout_type>;
using extents_type = shape;
using matrix_type = matrix<value_type,layout_type,array_type>;
using vector_type = vector<value_type,array_type>;
/** @brief Constructs a tensor.
*
* @note the tensor is empty.
* @note the tensor needs to reshaped for further use.
*
*/
BOOST_UBLAS_INLINE
constexpr tensor ()
: tensor_expression_type<self_type>() // container_type
, extents_()
, strides_()
, data_()
{
}
/** @brief Constructs a tensor with an initializer list
*
* By default, its elements are initialized to 0.
*
* @code tensor<float> A{4,2,3}; @endcode
*
* @param l initializer list for setting the dimension extents of the tensor
*/
explicit BOOST_UBLAS_INLINE
tensor (std::initializer_list<size_type> l)
: tensor_expression_type<self_type>()
, extents_ (std::move(l))
, strides_ (extents_)
, data_ (extents_.product())
{
}
/** @brief Constructs a tensor with a \c shape
*
* By default, its elements are initialized to 0.
*
* @code tensor<float> A{extents{4,2,3}}; @endcode
*
* @param s initial tensor dimension extents
*/
explicit BOOST_UBLAS_INLINE
tensor (extents_type const& s)
: tensor_expression_type<self_type>() //tensor_container<self_type>()
, extents_ (s)
, strides_ (extents_)
, data_ (extents_.product())
{}
/** @brief Constructs a tensor with a \c shape and initiates it with one-dimensional data
*
* @code tensor<float> A{extents{4,2,3}, array }; @endcode
*
*
* @param s initial tensor dimension extents
* @param a container of \c array_type that is copied according to the storage layout
*/
BOOST_UBLAS_INLINE
tensor (extents_type const& s, const array_type &a)
: tensor_expression_type<self_type>() //tensor_container<self_type>()
, extents_ (s)
, strides_ (extents_)
, data_ (a)
{
if(this->extents_.product() != this->data_.size())
throw std::runtime_error("Error in boost::numeric::ublas::tensor: size of provided data and specified extents do not match.");
}
/** @brief Constructs a tensor using a shape tuple and initiates it with a value.
*
* @code tensor<float> A{extents{4,2,3}, 1 }; @endcode
*
* @param e initial tensor dimension extents
* @param i initial value of all elements of type \c value_type
*/
BOOST_UBLAS_INLINE
tensor (extents_type const& e, const value_type &i)
: tensor_expression_type<self_type>() //tensor_container<self_type> ()
, extents_ (e)
, strides_ (extents_)
, data_ (extents_.product(), i)
{}
/** @brief Constructs a tensor from another tensor
*
* @param v tensor to be copied.
*/
BOOST_UBLAS_INLINE
tensor (const tensor &v)
: tensor_expression_type<self_type>()
, extents_ (v.extents_)
, strides_ (v.strides_)
, data_ (v.data_ )
{}
/** @brief Constructs a tensor from another tensor
*
* @param v tensor to be moved.
*/
BOOST_UBLAS_INLINE
tensor (tensor &&v)
: tensor_expression_type<self_type>() //tensor_container<self_type> ()
, extents_ (std::move(v.extents_))
, strides_ (std::move(v.strides_))
, data_ (std::move(v.data_ ))
{}
/** @brief Constructs a tensor with a matrix
*
* \note Initially the tensor will be two-dimensional.
*
* @param v matrix to be copied.
*/
BOOST_UBLAS_INLINE
tensor (const matrix_type &v)
: tensor_expression_type<self_type>()
, extents_ ()
, strides_ ()
, data_ (v.data())
{
if(!data_.empty()){
extents_ = extents_type{v.size1(),v.size2()};
strides_ = strides_type(extents_);
}
}
/** @brief Constructs a tensor with a matrix
*
* \note Initially the tensor will be two-dimensional.
*
* @param v matrix to be moved.
*/
BOOST_UBLAS_INLINE
tensor (matrix_type &&v)
: tensor_expression_type<self_type>()
, extents_ {}
, strides_ {}
, data_ {}
{
if(v.size1()*v.size2() != 0){
extents_ = extents_type{v.size1(),v.size2()};
strides_ = strides_type(extents_);
data_ = std::move(v.data());
}
}
/** @brief Constructs a tensor using a \c vector
*
* @note It is assumed that vector is column vector
* @note Initially the tensor will be one-dimensional.
*
* @param v vector to be copied.
*/
BOOST_UBLAS_INLINE
tensor (const vector_type &v)
: tensor_expression_type<self_type>()
, extents_ ()
, strides_ ()
, data_ (v.data())
{
if(!data_.empty()){
extents_ = extents_type{data_.size(),1};
strides_ = strides_type(extents_);
}
}
/** @brief Constructs a tensor using a \c vector
*
* @param v vector to be moved.
*/
BOOST_UBLAS_INLINE
tensor (vector_type &&v)
: tensor_expression_type<self_type>()
, extents_ {}
, strides_ {}
, data_ {}
{
if(v.size() != 0){
extents_ = extents_type{v.size(),1};
strides_ = strides_type(extents_);
data_ = std::move(v.data());
}
}
/** @brief Constructs a tensor with another tensor with a different layout
*
* @param other tensor with a different layout to be copied.
*/
BOOST_UBLAS_INLINE
template<class other_layout>
tensor (const tensor<value_type, other_layout> &other)
: tensor_expression_type<self_type> ()
, extents_ (other.extents())
, strides_ (other.extents())
, data_ (other.extents().product())
{
copy(this->rank(), this->extents().data(),
this->data(), this->strides().data(),
other.data(), other.strides().data());
}
/** @brief Constructs a tensor with an tensor expression
*
* @code tensor<float> A = B + 3 * C; @endcode
*
* @note type must be specified of tensor must be specified.
* @note dimension extents are extracted from tensors within the expression.
*
* @param expr tensor expression
*/
BOOST_UBLAS_INLINE
template<class derived_type>
tensor (const tensor_expression_type<derived_type> &expr)
: tensor_expression_type<self_type> ()
, extents_ ( detail::retrieve_extents(expr) )
, strides_ ( extents_ )
, data_ ( extents_.product() )
{
static_assert( detail::has_tensor_types<self_type, tensor_expression_type<derived_type>>::value,
"Error in boost::numeric::ublas::tensor: expression does not contain a tensor. cannot retrieve shape.");
detail::eval( *this, expr );
}
/** @brief Constructs a tensor with a matrix expression
*
* @code tensor<float> A = B + 3 * C; @endcode
*
* @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
* @note extents are automatically extracted from the temporary matrix
*
* @param expr matrix expression
*/
BOOST_UBLAS_INLINE
template<class derived_type>
tensor (const matrix_expression_type<derived_type> &expr)
: tensor( matrix_type ( expr ) )
{
}
/** @brief Constructs a tensor with a vector expression
*
* @code tensor<float> A = b + 3 * b; @endcode
*
* @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
* @note extents are automatically extracted from the temporary matrix
*
* @param expr vector expression
*/
BOOST_UBLAS_INLINE
template<class derived_type>
tensor (const vector_expression_type<derived_type> &expr)
: tensor( vector_type ( expr ) )
{
}
/** @brief Evaluates the tensor_expression and assigns the results to the tensor
*
* @code A = B + C * 2; @endcode
*
* @note rank and dimension extents of the tensors in the expressions must conform with this tensor.
*
* @param expr expression that is evaluated.
*/
BOOST_UBLAS_INLINE
template<class derived_type>
tensor &operator = (const tensor_expression_type<derived_type> &expr)
{
detail::eval(*this, expr);
return *this;
}
tensor& operator=(tensor other)
{
swap (*this, other);
return *this;
}
tensor& operator=(const_reference v)
{
std::fill(this->begin(), this->end(), v);
return *this;
}
/** @brief Returns true if the tensor is empty (\c size==0) */
BOOST_UBLAS_INLINE
bool empty () const {
return this->data_.empty();
}
/** @brief Returns the size of the tensor */
BOOST_UBLAS_INLINE
size_type size () const {
return this->data_.size ();
}
/** @brief Returns the size of the tensor */
BOOST_UBLAS_INLINE
size_type size (size_type r) const {
return this->extents_.at(r);
}
/** @brief Returns the number of dimensions/modes of the tensor */
BOOST_UBLAS_INLINE
size_type rank () const {
return this->extents_.size();
}
/** @brief Returns the number of dimensions/modes of the tensor */
BOOST_UBLAS_INLINE
size_type order () const {
return this->extents_.size();
}
/** @brief Returns the strides of the tensor */
BOOST_UBLAS_INLINE
strides_type const& strides () const {
return this->strides_;
}
/** @brief Returns the extents of the tensor */
BOOST_UBLAS_INLINE
extents_type const& extents () const {
return this->extents_;
}
/** @brief Returns a \c const reference to the container. */
BOOST_UBLAS_INLINE
const_pointer data () const {
return this->data_.data();
}
/** @brief Returns a \c const reference to the container. */
BOOST_UBLAS_INLINE
pointer data () {
return this->data_.data();
}
/** @brief Element access using a single index.
*
* @code auto a = A[i]; @endcode
*
* @param i zero-based index where 0 <= i < this->size()
*/
BOOST_UBLAS_INLINE
const_reference operator [] (size_type i) const {
return this->data_[i];
}
/** @brief Element access using a single index.
*
*
* @code A[i] = a; @endcode
*
* @param i zero-based index where 0 <= i < this->size()
*/
BOOST_UBLAS_INLINE
reference operator [] (size_type i)
{
return this->data_[i];
}
/** @brief Element access using a multi-index or single-index.
*
*
* @code auto a = A.at(i,j,k); @endcode or
* @code auto a = A.at(i); @endcode
*
* @param i zero-based index where 0 <= i < this->size() if sizeof...(is) == 0, else 0<= i < this->size(0)
* @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
*/
template<class ... size_types>
BOOST_UBLAS_INLINE
const_reference at (size_type i, size_types ... is) const {
if constexpr (sizeof...(is) == 0)
return this->data_[i];
else
return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
}
/** @brief Element access using a multi-index or single-index.
*
*
* @code A.at(i,j,k) = a; @endcode or
* @code A.at(i) = a; @endcode
*
* @param i zero-based index where 0 <= i < this->size() if sizeof...(is) == 0, else 0<= i < this->size(0)
* @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
*/
BOOST_UBLAS_INLINE
template<class ... size_types>
reference at (size_type i, size_types ... is) {
if constexpr (sizeof...(is) == 0)
return this->data_[i];
else
return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
}
/** @brief Element access using a single index.
*
*
* @code A(i) = a; @endcode
*
* @param i zero-based index where 0 <= i < this->size()
*/
BOOST_UBLAS_INLINE
const_reference operator()(size_type i) const {
return this->data_[i];
}
/** @brief Element access using a single index.
*
* @code A(i) = a; @endcode
*
* @param i zero-based index where 0 <= i < this->size()
*/
BOOST_UBLAS_INLINE
reference operator()(size_type i){
return this->data_[i];
}
/** @brief Generates a tensor index for tensor contraction
*
*
* @code auto Ai = A(_i,_j,k); @endcode
*
* @param i placeholder
* @param is zero-based indices where 0 <= is[r] < this->size(r) where 0 < r < this->rank()
*/
BOOST_UBLAS_INLINE
template<std::size_t I, class ... index_types>
decltype(auto) operator() (index::index_type<I> p, index_types ... ps) const
{
constexpr auto N = sizeof...(ps)+1;
if( N != this->rank() )
throw std::runtime_error("Error in boost::numeric::ublas::operator(): size of provided index_types does not match with the rank.");
return std::make_pair( std::cref(*this), std::make_tuple( p, std::forward<index_types>(ps)... ) );
}
/** @brief Reshapes the tensor
*
*
* (1) @code A.reshape(extents{m,n,o}); @endcode or
* (2) @code A.reshape(extents{m,n,o},4); @endcode
*
* If the size of this smaller than the specified extents than
* default constructed (1) or specified (2) value is appended.
*
* @note rank of the tensor might also change.
*
* @param e extents with which the tensor is reshaped.
* @param v value which is appended if the tensor is enlarged.
*/
BOOST_UBLAS_INLINE
void reshape (extents_type const& e, value_type v = value_type{})
{
this->extents_ = e;
this->strides_ = strides_type(this->extents_);
if(e.product() != this->size())
this->data_.resize (this->extents_.product(), v);
}
friend void swap(tensor& lhs, tensor& rhs) {
std::swap(lhs.data_ , rhs.data_ );
std::swap(lhs.extents_, rhs.extents_);
std::swap(lhs.strides_, rhs.strides_);
}
/// \brief return an iterator on the first element of the tensor
BOOST_UBLAS_INLINE
const_iterator begin () const {
return data_.begin ();
}
/// \brief return an iterator on the first element of the tensor
BOOST_UBLAS_INLINE
const_iterator cbegin () const {
return data_.cbegin ();
}
/// \brief return an iterator after the last element of the tensor
BOOST_UBLAS_INLINE
const_iterator end () const {
return data_.end();
}
/// \brief return an iterator after the last element of the tensor
BOOST_UBLAS_INLINE
const_iterator cend () const {
return data_.cend ();
}
/// \brief Return an iterator on the first element of the tensor
BOOST_UBLAS_INLINE
iterator begin () {
return data_.begin();
}
/// \brief Return an iterator at the end of the tensor
BOOST_UBLAS_INLINE
iterator end () {
return data_.end();
}
/// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
BOOST_UBLAS_INLINE
const_reverse_iterator rbegin () const {
return data_.rbegin();
}
/// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
BOOST_UBLAS_INLINE
const_reverse_iterator crbegin () const {
return data_.crbegin();
}
/// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
BOOST_UBLAS_INLINE
const_reverse_iterator rend () const {
return data_.rend();
}
/// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
BOOST_UBLAS_INLINE
const_reverse_iterator crend () const {
return data_.crend();
}
/// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
BOOST_UBLAS_INLINE
reverse_iterator rbegin () {
return data_.rbegin();
}
/// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
BOOST_UBLAS_INLINE
reverse_iterator rend () {
return data_.rend();
}
#if 0
// -------------
// Serialization
// -------------
/// Serialize a tensor into and archive as defined in Boost
/// \param ar Archive object. Can be a flat file, an XML file or any other stream
/// \param file_version Optional file version (not yet used)
template<class Archive>
void serialize(Archive & ar, const unsigned int /* file_version */){
ar & serialization::make_nvp("data",data_);
}
#endif
private:
extents_type extents_;
strides_type strides_;
array_type data_;
};
}}} // namespaces
#endif