Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:43:03

0001 //  Copyright (c) 2018-2019
0002 //  Cem Bassoy
0003 //
0004 //  Distributed under the Boost Software License, Version 1.0. (See
0005 //  accompanying file LICENSE_1_0.txt or copy at
0006 //  http://www.boost.org/LICENSE_1_0.txt)
0007 //
0008 //  The authors gratefully acknowledge the support of
0009 //  Fraunhofer and Google in producing this work
0010 //  which started as a Google Summer of Code project.
0011 //
0012 
0013 
0014 /// \file tensor.hpp Definition for the tensor template class
0015 
0016 #ifndef BOOST_UBLAS_TENSOR_IMPL_HPP
0017 #define BOOST_UBLAS_TENSOR_IMPL_HPP
0018 
0019 #include <boost/config.hpp>
0020 
0021 #include <initializer_list>
0022 
0023 #include "algorithms.hpp"
0024 #include "expression.hpp"
0025 #include "expression_evaluation.hpp"
0026 #include "extents.hpp"
0027 #include "strides.hpp"
0028 #include "index.hpp"
0029 
0030 namespace boost { namespace numeric { namespace ublas {
0031 
0032 template<class T, class F, class A>
0033 class tensor;
0034 
0035 template<class T, class F, class A>
0036 class matrix;
0037 
0038 template<class T, class A>
0039 class vector;
0040 
0041 ///** \brief Base class for Tensor container models
0042 // *
0043 // * it does not model the Tensor concept but all derived types should.
0044 // * The class defines a common base type and some common interface for all
0045 // * statically derived Tensor classes
0046 // * We implement the casts to the statically derived type.
0047 // */
0048 //template<class C>
0049 //class tensor_container:
0050 //      public detail::tensor_expression<C>
0051 //{
0052 //public:
0053 //  static const unsigned complexity = 0;
0054 //  typedef C container_type;
0055 //  typedef tensor_tag type_category;
0056 
0057 //  BOOST_UBLAS_INLINE
0058 //  const container_type &operator () () const {
0059 //          return *static_cast<const container_type *> (this);
0060 //  }
0061 //  BOOST_UBLAS_INLINE
0062 //  container_type &operator () () {
0063 //          return *static_cast<container_type *> (this);
0064 //  }
0065 //};
0066 
0067 
0068 
0069 /** @brief A dense tensor of values of type \c T.
0070         *
0071         * 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
0072         * to the \f$i\f$-th element of the container. A storage type \c A can be specified which defaults to \c unbounded_array.
0073         * Elements are constructed by \c A, which need not initialise their value.
0074         *
0075         * @tparam T type of the objects stored in the tensor (like int, double, complex,...)
0076         * @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
0077         */
0078 template<class T, class F = first_order, class A = std::vector<T,std::allocator<T>> >
0079 class tensor:
0080         public detail::tensor_expression<tensor<T, F, A>,tensor<T, F, A>>
0081 {
0082 
0083     static_assert( std::is_same<F,first_order>::value ||
0084                                  std::is_same<F,last_order >::value, "boost::numeric::tensor template class only supports first- or last-order storage formats.");
0085 
0086     using self_type  = tensor<T, F, A>;
0087 public:
0088 
0089 
0090 
0091     template<class derived_type>
0092     using tensor_expression_type = detail::tensor_expression<self_type,derived_type>;
0093 
0094     template<class derived_type>
0095     using matrix_expression_type = matrix_expression<derived_type>;
0096 
0097     template<class derived_type>
0098     using vector_expression_type = vector_expression<derived_type>;
0099 
0100     using super_type = tensor_expression_type<self_type>;
0101 
0102 //  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>");
0103 
0104     using array_type  = A;
0105     using layout_type = F;
0106 
0107 
0108     using size_type       = typename array_type::size_type;
0109     using difference_type = typename array_type::difference_type;
0110     using value_type      = typename array_type::value_type;
0111 
0112     using reference       = typename array_type::reference;
0113     using const_reference = typename array_type::const_reference;
0114 
0115     using pointer         = typename array_type::pointer;
0116     using const_pointer   = typename array_type::const_pointer;
0117 
0118     using iterator        = typename array_type::iterator;
0119     using const_iterator  = typename array_type::const_iterator;
0120 
0121     using reverse_iterator        = typename array_type::reverse_iterator;
0122     using const_reverse_iterator  = typename array_type::const_reverse_iterator;
0123 
0124     using tensor_temporary_type = self_type;
0125     using storage_category = dense_tag;
0126 
0127     using strides_type = basic_strides<std::size_t,layout_type>;
0128     using extents_type = shape;
0129 
0130     using matrix_type     = matrix<value_type,layout_type,array_type>;
0131     using vector_type     = vector<value_type,array_type>;
0132 
0133 
0134     /** @brief Constructs a tensor.
0135      *
0136      * @note the tensor is empty.
0137      * @note the tensor needs to reshaped for further use.
0138      *
0139      */
0140     BOOST_UBLAS_INLINE
0141     constexpr tensor ()
0142         : tensor_expression_type<self_type>() // container_type
0143         , extents_()
0144         , strides_()
0145         , data_()
0146     {
0147     }
0148 
0149 
0150     /** @brief Constructs a tensor with an initializer list
0151      *
0152      * By default, its elements are initialized to 0.
0153      *
0154      * @code tensor<float> A{4,2,3}; @endcode
0155      *
0156      * @param l initializer list for setting the dimension extents of the tensor
0157      */
0158     explicit BOOST_UBLAS_INLINE
0159     tensor (std::initializer_list<size_type> l)
0160         : tensor_expression_type<self_type>()
0161         , extents_ (std::move(l))
0162         , strides_ (extents_)
0163         , data_    (extents_.product())
0164     {
0165     }
0166 
0167 
0168     /** @brief Constructs a tensor with a \c shape
0169      *
0170      * By default, its elements are initialized to 0.
0171      *
0172      * @code tensor<float> A{extents{4,2,3}}; @endcode
0173      *
0174      * @param s initial tensor dimension extents
0175      */
0176     explicit BOOST_UBLAS_INLINE
0177     tensor (extents_type const& s)
0178         : tensor_expression_type<self_type>() //tensor_container<self_type>()
0179         , extents_ (s)
0180         , strides_ (extents_)
0181         , data_    (extents_.product())
0182     {}
0183 
0184 
0185     /** @brief Constructs a tensor with a \c shape and initiates it with one-dimensional data
0186      *
0187      * @code tensor<float> A{extents{4,2,3}, array }; @endcode
0188      *
0189      *
0190      *  @param s initial tensor dimension extents
0191      *  @param a container of \c array_type that is copied according to the storage layout
0192      */
0193     BOOST_UBLAS_INLINE
0194     tensor (extents_type const& s, const array_type &a)
0195         : tensor_expression_type<self_type>() //tensor_container<self_type>()
0196         , extents_ (s)
0197         , strides_ (extents_)
0198         , data_    (a)
0199     {
0200         if(this->extents_.product() != this->data_.size())
0201             throw std::runtime_error("Error in boost::numeric::ublas::tensor: size of provided data and specified extents do not match.");
0202     }
0203 
0204 
0205 
0206     /** @brief Constructs a tensor using a shape tuple and initiates it with a value.
0207      *
0208      *  @code tensor<float> A{extents{4,2,3}, 1 }; @endcode
0209      *
0210      *  @param e initial tensor dimension extents
0211      *  @param i initial value of all elements of type \c value_type
0212      */
0213     BOOST_UBLAS_INLINE
0214     tensor (extents_type const& e, const value_type &i)
0215         : tensor_expression_type<self_type>() //tensor_container<self_type> ()
0216         , extents_ (e)
0217         , strides_ (extents_)
0218         , data_    (extents_.product(), i)
0219     {}
0220 
0221 
0222 
0223     /** @brief Constructs a tensor from another tensor
0224      *
0225      *  @param v tensor to be copied.
0226      */
0227     BOOST_UBLAS_INLINE
0228     tensor (const tensor &v)
0229         : tensor_expression_type<self_type>()
0230         , extents_ (v.extents_)
0231         , strides_ (v.strides_)
0232         , data_    (v.data_   )
0233     {}
0234 
0235 
0236 
0237     /** @brief Constructs a tensor from another tensor
0238      *
0239      *  @param v tensor to be moved.
0240      */
0241     BOOST_UBLAS_INLINE
0242     tensor (tensor &&v)
0243         : tensor_expression_type<self_type>() //tensor_container<self_type> ()
0244         , extents_ (std::move(v.extents_))
0245         , strides_ (std::move(v.strides_))
0246         , data_    (std::move(v.data_   ))
0247     {}
0248 
0249 
0250     /** @brief Constructs a tensor with a matrix
0251      *
0252      * \note Initially the tensor will be two-dimensional.
0253      *
0254      *  @param v matrix to be copied.
0255      */
0256     BOOST_UBLAS_INLINE
0257     tensor (const matrix_type &v)
0258         : tensor_expression_type<self_type>()
0259         , extents_ ()
0260         , strides_ ()
0261         , data_    (v.data())
0262     {
0263         if(!data_.empty()){
0264             extents_ = extents_type{v.size1(),v.size2()};
0265             strides_ = strides_type(extents_);
0266         }
0267     }
0268 
0269     /** @brief Constructs a tensor with a matrix
0270      *
0271      * \note Initially the tensor will be two-dimensional.
0272      *
0273      *  @param v matrix to be moved.
0274      */
0275     BOOST_UBLAS_INLINE
0276     tensor (matrix_type &&v)
0277         : tensor_expression_type<self_type>()
0278         , extents_ {}
0279         , strides_ {}
0280         , data_    {}
0281     {
0282         if(v.size1()*v.size2() != 0){
0283             extents_ = extents_type{v.size1(),v.size2()};
0284             strides_ = strides_type(extents_);
0285             data_    = std::move(v.data());
0286         }
0287     }
0288 
0289     /** @brief Constructs a tensor using a \c vector
0290      *
0291      * @note It is assumed that vector is column vector
0292      * @note Initially the tensor will be one-dimensional.
0293      *
0294      *  @param v vector to be copied.
0295      */
0296     BOOST_UBLAS_INLINE
0297     tensor (const vector_type &v)
0298         : tensor_expression_type<self_type>()
0299         , extents_ ()
0300         , strides_ ()
0301         , data_    (v.data())
0302     {
0303         if(!data_.empty()){
0304             extents_ = extents_type{data_.size(),1};
0305             strides_ = strides_type(extents_);
0306         }
0307     }
0308 
0309     /** @brief Constructs a tensor using a \c vector
0310      *
0311      *  @param v vector to be moved.
0312      */
0313     BOOST_UBLAS_INLINE
0314     tensor (vector_type &&v)
0315         : tensor_expression_type<self_type>()
0316         , extents_ {}
0317         , strides_ {}
0318         , data_    {}
0319     {
0320         if(v.size() != 0){
0321             extents_ = extents_type{v.size(),1};
0322             strides_ = strides_type(extents_);
0323             data_    = std::move(v.data());
0324         }
0325     }
0326 
0327 
0328     /** @brief Constructs a tensor with another tensor with a different layout
0329      *
0330      * @param other tensor with a different layout to be copied.
0331      */
0332     BOOST_UBLAS_INLINE
0333     template<class other_layout>
0334     tensor (const tensor<value_type, other_layout> &other)
0335         : tensor_expression_type<self_type> ()
0336         , extents_ (other.extents())
0337         , strides_ (other.extents())
0338         , data_    (other.extents().product())
0339     {
0340         copy(this->rank(), this->extents().data(),
0341                  this->data(), this->strides().data(),
0342                  other.data(), other.strides().data());
0343     }
0344 
0345     /** @brief Constructs a tensor with an tensor expression
0346      *
0347      * @code tensor<float> A = B + 3 * C; @endcode
0348      *
0349      * @note type must be specified of tensor must be specified.
0350      * @note dimension extents are extracted from tensors within the expression.
0351      *
0352      * @param expr tensor expression
0353      */
0354     BOOST_UBLAS_INLINE
0355     template<class derived_type>
0356     tensor (const tensor_expression_type<derived_type> &expr)
0357         : tensor_expression_type<self_type> ()
0358         , extents_ ( detail::retrieve_extents(expr) )
0359         , strides_ ( extents_ )
0360         , data_    ( extents_.product() )
0361     {
0362         static_assert( detail::has_tensor_types<self_type, tensor_expression_type<derived_type>>::value,
0363                                      "Error in boost::numeric::ublas::tensor: expression does not contain a tensor. cannot retrieve shape.");
0364         detail::eval( *this, expr );
0365     }
0366 
0367     /** @brief Constructs a tensor with a matrix expression
0368      *
0369      * @code tensor<float> A = B + 3 * C; @endcode
0370      *
0371      * @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
0372      * @note extents are automatically extracted from the temporary matrix
0373      *
0374      * @param expr matrix expression
0375      */
0376     BOOST_UBLAS_INLINE
0377     template<class derived_type>
0378     tensor (const matrix_expression_type<derived_type> &expr)
0379         : tensor(  matrix_type ( expr )  )
0380     {
0381     }
0382 
0383     /** @brief Constructs a tensor with a vector expression
0384      *
0385      * @code tensor<float> A = b + 3 * b; @endcode
0386      *
0387      * @note matrix expression is evaluated and pushed into a temporary matrix before assignment.
0388      * @note extents are automatically extracted from the temporary matrix
0389      *
0390      * @param expr vector expression
0391      */
0392     BOOST_UBLAS_INLINE
0393     template<class derived_type>
0394     tensor (const vector_expression_type<derived_type> &expr)
0395         : tensor(  vector_type ( expr )  )
0396     {
0397     }
0398 
0399     /** @brief Evaluates the tensor_expression and assigns the results to the tensor
0400      *
0401      * @code A = B + C * 2;  @endcode
0402      *
0403      * @note rank and dimension extents of the tensors in the expressions must conform with this tensor.
0404      *
0405      * @param expr expression that is evaluated.
0406      */
0407     BOOST_UBLAS_INLINE
0408     template<class derived_type>
0409     tensor &operator = (const tensor_expression_type<derived_type> &expr)
0410     {
0411         detail::eval(*this, expr);
0412         return *this;
0413     }
0414 
0415     tensor& operator=(tensor other)
0416     {
0417         swap (*this, other);
0418         return *this;
0419     }
0420 
0421     tensor& operator=(const_reference v)
0422     {
0423         std::fill(this->begin(), this->end(), v);
0424         return *this;
0425     }
0426 
0427     /** @brief Returns true if the tensor is empty (\c size==0) */
0428     BOOST_UBLAS_INLINE
0429     bool empty () const {
0430         return this->data_.empty();
0431     }
0432 
0433 
0434     /** @brief Returns the size of the tensor */
0435     BOOST_UBLAS_INLINE
0436     size_type size () const {
0437         return this->data_.size ();
0438     }
0439 
0440     /** @brief Returns the size of the tensor */
0441     BOOST_UBLAS_INLINE
0442     size_type size (size_type r) const {
0443         return this->extents_.at(r);
0444     }
0445 
0446     /** @brief Returns the number of dimensions/modes of the tensor */
0447     BOOST_UBLAS_INLINE
0448     size_type rank () const {
0449         return this->extents_.size();
0450     }
0451 
0452     /** @brief Returns the number of dimensions/modes of the tensor */
0453     BOOST_UBLAS_INLINE
0454     size_type order () const {
0455         return this->extents_.size();
0456     }
0457 
0458     /** @brief Returns the strides of the tensor */
0459     BOOST_UBLAS_INLINE
0460     strides_type const& strides () const {
0461         return this->strides_;
0462     }
0463 
0464     /** @brief Returns the extents of the tensor */
0465     BOOST_UBLAS_INLINE
0466     extents_type const& extents () const {
0467         return this->extents_;
0468     }
0469 
0470 
0471     /** @brief Returns a \c const reference to the container. */
0472     BOOST_UBLAS_INLINE
0473     const_pointer data () const {
0474         return this->data_.data();
0475     }
0476 
0477     /** @brief Returns a \c const reference to the container. */
0478     BOOST_UBLAS_INLINE
0479     pointer data () {
0480         return this->data_.data();
0481     }
0482 
0483     /** @brief Element access using a single index.
0484      *
0485      *  @code auto a = A[i]; @endcode
0486      *
0487      *  @param i zero-based index where 0 <= i < this->size()
0488      */
0489     BOOST_UBLAS_INLINE
0490     const_reference operator [] (size_type i) const {
0491         return this->data_[i];
0492     }
0493 
0494     /** @brief Element access using a single index.
0495      *
0496      *
0497      *  @code A[i] = a; @endcode
0498      *
0499      *  @param i zero-based index where 0 <= i < this->size()
0500      */
0501     BOOST_UBLAS_INLINE
0502     reference operator [] (size_type i)
0503     {
0504         return this->data_[i];
0505     }
0506 
0507 
0508     /** @brief Element access using a multi-index or single-index.
0509      *
0510      *
0511      *  @code auto a = A.at(i,j,k); @endcode or
0512      *  @code auto a = A.at(i);     @endcode
0513      *
0514      *  @param i zero-based index where 0 <= i < this->size() if sizeof...(is) == 0, else 0<= i < this->size(0)
0515      *  @param is zero-based indices where 0 <= is[r] < this->size(r) where  0 < r < this->rank()
0516      */
0517     template<class ... size_types>
0518     BOOST_UBLAS_INLINE
0519     const_reference at (size_type i, size_types ... is) const {
0520         if constexpr (sizeof...(is) == 0)
0521             return this->data_[i];
0522         else
0523             return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
0524     }
0525 
0526     /** @brief Element access using a multi-index or single-index.
0527      *
0528      *
0529      *  @code A.at(i,j,k) = a; @endcode or
0530      *  @code A.at(i) = a;     @endcode
0531      *
0532      *  @param i zero-based index where 0 <= i < this->size() if sizeof...(is) == 0, else 0<= i < this->size(0)
0533      *  @param is zero-based indices where 0 <= is[r] < this->size(r) where  0 < r < this->rank()
0534      */
0535     BOOST_UBLAS_INLINE
0536     template<class ... size_types>
0537     reference at (size_type i, size_types ... is) {
0538         if constexpr (sizeof...(is) == 0)
0539             return this->data_[i];
0540         else
0541             return this->data_[detail::access<0ul>(size_type(0),this->strides_,i,std::forward<size_types>(is)...)];
0542     }
0543 
0544 
0545 
0546 
0547     /** @brief Element access using a single index.
0548      *
0549      *
0550      *  @code A(i) = a; @endcode
0551      *
0552      *  @param i zero-based index where 0 <= i < this->size()
0553      */
0554     BOOST_UBLAS_INLINE
0555     const_reference operator()(size_type i) const {
0556         return this->data_[i];
0557     }
0558 
0559 
0560     /** @brief Element access using a single index.
0561      *
0562      *  @code A(i) = a; @endcode
0563      *
0564      *  @param i zero-based index where 0 <= i < this->size()
0565      */
0566     BOOST_UBLAS_INLINE
0567     reference operator()(size_type i){
0568         return this->data_[i];
0569     }
0570 
0571 
0572 
0573 
0574     /** @brief Generates a tensor index for tensor contraction
0575      *
0576      *
0577      *  @code auto Ai = A(_i,_j,k); @endcode
0578      *
0579      *  @param i placeholder
0580      *  @param is zero-based indices where 0 <= is[r] < this->size(r) where  0 < r < this->rank()
0581      */
0582     BOOST_UBLAS_INLINE
0583     template<std::size_t I, class ... index_types>
0584     decltype(auto) operator() (index::index_type<I> p, index_types ... ps) const
0585     {
0586         constexpr auto N = sizeof...(ps)+1;
0587         if( N != this->rank() )
0588             throw std::runtime_error("Error in boost::numeric::ublas::operator(): size of provided index_types does not match with the rank.");
0589 
0590         return std::make_pair( std::cref(*this),  std::make_tuple( p, std::forward<index_types>(ps)... ) );
0591     }
0592 
0593 
0594 
0595 
0596 
0597     /** @brief Reshapes the tensor
0598      *
0599      *
0600      * (1) @code A.reshape(extents{m,n,o});     @endcode or
0601      * (2) @code A.reshape(extents{m,n,o},4);   @endcode
0602      *
0603      * If the size of this smaller than the specified extents than
0604      * default constructed (1) or specified (2) value is appended.
0605      *
0606      * @note rank of the tensor might also change.
0607      *
0608      * @param e extents with which the tensor is reshaped.
0609      * @param v value which is appended if the tensor is enlarged.
0610      */
0611     BOOST_UBLAS_INLINE
0612     void reshape (extents_type const& e, value_type v = value_type{})
0613     {
0614         this->extents_ = e;
0615         this->strides_ = strides_type(this->extents_);
0616 
0617         if(e.product() != this->size())
0618             this->data_.resize (this->extents_.product(), v);
0619     }
0620 
0621 
0622 
0623 
0624 
0625     friend void swap(tensor& lhs, tensor& rhs) {
0626         std::swap(lhs.data_   , rhs.data_   );
0627         std::swap(lhs.extents_, rhs.extents_);
0628         std::swap(lhs.strides_, rhs.strides_);
0629     }
0630 
0631 
0632     /// \brief return an iterator on the first element of the tensor
0633     BOOST_UBLAS_INLINE
0634     const_iterator begin () const {
0635         return data_.begin ();
0636     }
0637 
0638     /// \brief return an iterator on the first element of the tensor
0639     BOOST_UBLAS_INLINE
0640     const_iterator cbegin () const {
0641         return data_.cbegin ();
0642     }
0643 
0644     /// \brief return an iterator after the last element of the tensor
0645     BOOST_UBLAS_INLINE
0646     const_iterator end () const {
0647         return data_.end();
0648     }
0649 
0650     /// \brief return an iterator after the last element of the tensor
0651     BOOST_UBLAS_INLINE
0652     const_iterator cend () const {
0653         return data_.cend ();
0654     }
0655 
0656     /// \brief Return an iterator on the first element of the tensor
0657     BOOST_UBLAS_INLINE
0658     iterator begin () {
0659         return data_.begin();
0660     }
0661 
0662     /// \brief Return an iterator at the end of the tensor
0663     BOOST_UBLAS_INLINE
0664     iterator end () {
0665         return data_.end();
0666     }
0667 
0668     /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
0669     BOOST_UBLAS_INLINE
0670     const_reverse_iterator rbegin () const {
0671         return data_.rbegin();
0672     }
0673 
0674     /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
0675     BOOST_UBLAS_INLINE
0676     const_reverse_iterator crbegin () const {
0677         return data_.crbegin();
0678     }
0679 
0680     /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
0681     BOOST_UBLAS_INLINE
0682     const_reverse_iterator rend () const {
0683         return data_.rend();
0684     }
0685 
0686     /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
0687     BOOST_UBLAS_INLINE
0688     const_reverse_iterator crend () const {
0689         return data_.crend();
0690     }
0691 
0692     /// \brief Return a const reverse iterator before the first element of the reversed tensor (i.e. end() of normal tensor)
0693     BOOST_UBLAS_INLINE
0694     reverse_iterator rbegin () {
0695         return data_.rbegin();
0696     }
0697 
0698     /// \brief Return a const reverse iterator on the end of the reverse tensor (i.e. first element of the normal tensor)
0699     BOOST_UBLAS_INLINE
0700     reverse_iterator rend () {
0701         return data_.rend();
0702     }
0703 
0704 
0705 #if 0
0706     // -------------
0707     // Serialization
0708     // -------------
0709 
0710     /// Serialize a tensor into and archive as defined in Boost
0711     /// \param ar Archive object. Can be a flat file, an XML file or any other stream
0712     /// \param file_version Optional file version (not yet used)
0713     template<class Archive>
0714     void serialize(Archive & ar, const unsigned int /* file_version */){
0715         ar & serialization::make_nvp("data",data_);
0716     }
0717 #endif
0718 
0719 
0720 
0721 private:
0722 
0723     extents_type extents_;
0724     strides_type strides_;
0725     array_type data_;
0726 };
0727 
0728 }}} // namespaces
0729 
0730 
0731 
0732 
0733 
0734 #endif