CUV  0.9.201304091348
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
tensor.hpp
Go to the documentation of this file.
1 //*LB*
2 // Copyright (c) 2010, University of Bonn, Institute for Computer Science VI
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of the University of Bonn
14 // nor the names of its contributors may be used to endorse or promote
15 // products derived from this software without specific prior written
16 // permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //*LE*
29 
38 #ifndef __TENSOR2_HPP__
39 # define __TENSOR2_HPP__
40 
41 #include <iostream>
42 #include <vector>
43 #include <stdexcept>
44 #include <numeric>
45 #include <boost/shared_ptr.hpp>
46 #include <boost/multi_array/extent_gen.hpp>
47 #include <boost/multi_array/index_gen.hpp>
48 #include <cuv/tools/cuv_general.hpp>
49 #include <cuv/tools/meta_programming.hpp>
50 #include "reference.hpp"
51 
52 namespace boost { namespace serialization {
53  class access; } }
54 
55 namespace cuv
56 {
66 
67  struct column_major {};
69  struct row_major {};
70 
74  struct pitched_memory_tag{}; // tags
82 
83  template<class T>
86  template<>
88  typedef row_major type;
89  };
91  template<>
93  typedef column_major type;
94  };
95 
96 
98  template<class T>
101  template<>
104  };
106  template<>
109  };
114  using boost::detail::multi_array::extent_gen;
115  using boost::detail::multi_array::index_gen;
125  typedef boost::detail::multi_array::index_range<boost::detail::multi_array::index,boost::detail::multi_array::size_type> index_range;
130 #ifndef CUV_DONT_CREATE_EXTENTS_OBJ
131  namespace{
142  extent_gen<0> extents;
159  index_gen<0,0> indices;
160  }
161 #endif
162 
163 
164  template<class V, class M, class L> class tensor;
165  template<class V, class M> class linear_memory;
166 
168  template<class V, class M, class L, class S>
169  void fill(tensor<V, M, L>& v, const V& p);
170 
178  template<class V, class M>
179  class memory{
180  public:
181  typedef typename unconst<V>::type value_type;
182  typedef const V const_value_type;
183  typedef M memory_space_type;
184  typedef unsigned int size_type;
185  typedef int index_type;
188  typedef value_type* pointer_type;
189  typedef const_value_type* const_pointer_type;
190 
191  private:
192  friend class boost::serialization::access;
194 
195  memory(const memory&){}
197  memory& operator=(const memory& o){return *this;}
198  protected:
199  pointer_type m_ptr;
201  public:
203  pointer_type ptr(){return m_ptr;}
205  const_pointer_type ptr()const{return m_ptr;}
206 
208  size_type size()const{ return m_size; }
210  size_type memsize()const{ return size()*sizeof(V); }
211 
213  void reset(pointer_type p, size_type s){ m_ptr = p; m_size = s; }
214 
215 
217  memory():m_ptr(NULL),m_size(0){}
218 
221 
223  ~memory(){ dealloc(); }
224 
228  void dealloc(){
229  if (m_ptr)
230  m_allocator.dealloc(&this->m_ptr);
231  m_ptr=NULL;
232  }
233  };
234 
238  template<class V, class M>
240  : public memory<V,M> {
241  private:
242  typedef memory<V,M> super;
243  public:
244  typedef typename super::value_type value_type;
247  typedef typename super::index_type index_type;
248  typedef typename super::size_type size_type;
251  private:
252  friend class boost::serialization::access;
253  typedef linear_memory<V,M> my_type;
255  using super::m_size;
256  using super::m_ptr;
257  public:
258 
261 
266 
268  value_type* release(){ value_type* ptr = m_ptr; m_ptr = NULL; return ptr; }
269 
273  if(s!=this->size()){
274  dealloc();
275  m_size = s;
276  alloc();
277  }
278  }
279 
283  void alloc(){
284  assert(this->m_ptr == NULL);
285  if(m_size>0)
286  m_allocator.alloc( &m_ptr,m_size);
287  }
288 
292  void dealloc(){
293  if (m_ptr)
294  m_allocator.dealloc(&this->m_ptr);
295  this->m_ptr=NULL;
296  this->m_size=0;
297  }
298 
307  my_type&
308  operator=(const my_type& o){
309  if(this == &o)
310  return *this;
311 
312  if(this->size() != o.size()){
313  this->dealloc();
314  m_size = o.size();
315  this->alloc();
316  }
317  m_allocator.copy(this->m_ptr,o.ptr(),this->size(),memory_space_type());
318 
319  return *this;
320  }
321 
332  template<class OM>
333  my_type&
335  if(this->size() != o.size()){
336  this->dealloc();
337  m_size = o.size();
338  this->alloc();
339  }
340  m_allocator.copy(m_ptr,o.ptr(),this->size(),OM());
341  return *this;
342  }
343 
348  operator=(o);
349  }
350 
354  template<class OM>
356  operator=(o);
357  }
358 
364  operator[](const index_type& idx)
365  {
366  assert(idx>=0);
367  assert((size_type)idx<m_size);
368  return reference_type(this->m_ptr+idx);
369  }
370 
378  operator[](const index_type& idx)const
379  {
380  assert(idx>=0);
381  assert((size_type)idx<m_size);
382  return const_reference_type(this->m_ptr+idx);
383  }
384 
387 
392  size_type size = 1;
393  for (int i = shape.size()-1; i >= 0; --i)
394  {
395  strides[i] = (shape[i] == 1) ? 0 : size;
396  size *= shape[i];
397  }
398  }
403  size_type size = 1;
404  for (unsigned int i = 0; i < shape.size(); ++i)
405  {
406  strides[i] = (shape[i] == 1) ? 0 : size;
407  size *= shape[i];
408  }
409  }
410 
415  void reverse(){
417  throw std::runtime_error("reverse of dev linear memory not implemented");
418  value_type* __first = m_ptr, *__last = m_ptr + this->size();
419  while (true)
420  if (__first == __last || __first == --__last)
421  return;
422  else
423  {
424  std::iter_swap(__first, __last);
425  ++__first;
426  }
427  }
428 
429  }; // data_structures
431 
432  namespace detail{
433 
437  inline bool is_c_contiguous(row_major, const linear_memory<unsigned int,host_memory_space>& shape, const linear_memory<int,host_memory_space>& stride){
438  bool c_contiguous = true;
439  int size = 1;
440  for (int i = shape.size()-1; (i >= 0) && c_contiguous; --i)
441  {
442  if (shape[i] == 1)
443  continue;
444  if (stride[i] != size)
445  c_contiguous = false;
446  size = size * shape[i];
447  }
448  return c_contiguous;
449  }
450 
454  inline bool is_c_contiguous(column_major, const linear_memory<unsigned int,host_memory_space>& shape, const linear_memory<int,host_memory_space>& stride){
455  bool c_contiguous = true;
456  int size = 1;
457  for (unsigned int i = 0; i<shape.size() && c_contiguous; ++i)
458  {
459  if (shape[i] == 1)
460  continue;
461  if (stride[i] != size)
462  c_contiguous = false;
463  size = size * shape[i];
464  }
465  return c_contiguous;
466  }
467 
469  inline bool is_2dcopyable(row_major, const linear_memory<unsigned int,host_memory_space>& shape, const linear_memory<int,host_memory_space>& stride){
470  bool c_contiguous = shape.size()>1;
471  int pitched_dim = shape.size()-1; // last dim
472  while(shape[pitched_dim]==1)
473  pitched_dim --;
474  int size = 1;
475  for (int i = shape.size()-1; (i >= 0) && c_contiguous; --i)
476  {
477  if(shape[i] == 1){
478  continue;
479  }else if(i == pitched_dim){
480  size *= stride[i-1];
481  }else if(stride[i] != size) {
482  c_contiguous = false;
483  }else{
484  size *= shape[i];
485  }
486  }
487  return c_contiguous;
488  }
489 
491  inline bool is_2dcopyable(column_major, const linear_memory<unsigned int,host_memory_space>& shape, const linear_memory<int,host_memory_space>& stride){
492  bool c_contiguous = shape.size()>1;
493  unsigned int pitched_dim = 0;
494  while(shape[pitched_dim]==1)
495  pitched_dim ++;
496  int size = 1;
497  for (unsigned int i = 0; (i < shape.size()) && c_contiguous; ++i)
498  {
499  if(shape[i] == 1){
500  continue;
501  }else if(i == pitched_dim){
502  size *= stride[i];
503  }else if(stride[i] != size) {
504  c_contiguous = false;
505  }else{
506  size *= shape[i];
507  }
508  }
509  return c_contiguous;
510  }
511 
518  template<class index_type, class size_type>
519  void get_pitched_params(size_type& rows, size_type& cols, size_type& pitch,
520  const linear_memory<size_type,host_memory_space>& shape,
521  const linear_memory<index_type,host_memory_space>& stride,
522  row_major){
523  // strided dimension is the LAST one
524  rows = std::accumulate(shape[0].ptr,
525  shape[0].ptr+shape.size()-1,
526  1, std::multiplies<index_type>());
527  cols = shape[shape.size()-1];
528  pitch = stride[shape.size()-2];
529  }
533  template<class index_type, class size_type>
534  void get_pitched_params(size_type& rows, size_type& cols, size_type& pitch,
535  const linear_memory<size_type,host_memory_space>& shape,
536  const linear_memory<index_type,host_memory_space>& stride,
537  column_major){
538  // strided dimension is the FIRST one
539  rows = std::accumulate(shape[0].ptr+1,
540  shape[0].ptr+shape.size(),
541  1, std::multiplies<index_type>());
542  cols = shape[0];
543  pitch = stride[1];
544  }
545  }
546 
555  template<class V, class M>
557  : public memory<V,M> {
558  private:
559  typedef memory<V,M> super;
560  public:
561  typedef typename super::value_type value_type;
564  typedef typename super::index_type index_type;
565  typedef typename super::size_type size_type;
568  private:
569  friend class boost::serialization::access;
570  typedef pitched_memory<V,M> my_type;
572  size_type m_rows;
573  size_type m_cols;
574  size_type m_pitch;
575  using super::m_ptr;
576  using super::m_size;
577  public:
578 
580  size_type rows()const{return m_rows;}
581 
583  size_type cols()const{return m_cols;}
584 
586  size_type pitch()const{return m_pitch;}
587 
589  size_type size()const{ return m_rows*m_pitch; }
590 
592  size_type memsize()const{ return size()*sizeof(V); }
593 
595  pitched_memory():m_rows(0),m_cols(0),m_pitch(0){}
596 
602  :m_rows(i),m_cols(j),m_pitch(0){alloc();}
603 
607  void alloc(){
608  assert(this->m_ptr == NULL);
609  m_allocator.alloc2d(&this->m_ptr,m_pitch,m_rows,m_cols);
610  assert(m_pitch%sizeof(value_type)==0);
611  m_pitch/=sizeof(value_type);
612  m_size = m_rows*m_pitch; // in class memory
613  }
614 
618  void dealloc(){
619  if (this->m_ptr)
620  m_allocator.dealloc(&this->m_ptr);
621  this->m_ptr=NULL;
622  this->m_size=NULL;
623  }
624 
626  value_type* release(){ value_type* ptr = m_ptr; m_ptr = NULL; return ptr; }
627 
633  void set_size(size_type rows, size_type cols){
634  if( cols>m_pitch
635  || rows>m_rows
636  ){
637  dealloc();
638  m_rows = rows;
639  m_cols = cols;
640  alloc();
641  }else{
642  m_rows = rows;
643  m_cols = cols;
644  }
645  }
646 
647 
656  my_type&
657  operator=(const my_type& o){
658  if(this==&o) return *this;
659 
660  if( m_pitch < o.m_cols
661  || m_rows < o.m_rows
662  ){
663  this->dealloc();
664  m_cols = o.m_cols;
665  m_rows = o.m_rows;
666  this->alloc();
667  }
668  m_cols = o.m_cols;
669  m_rows = o.m_rows;
670  m_allocator.copy2d(this->m_ptr,o.ptr(),m_pitch*sizeof(value_type),o.m_pitch*sizeof(value_type),m_rows,m_cols,memory_space_type());
671 
672  return *this;
673  }
674 
685  template<class OM>
686  my_type&
688  if( m_pitch < o.m_cols
689  || m_rows < o.m_rows
690  ){
691  this->dealloc();
692  m_cols = o.m_cols;
693  m_rows = o.m_rows;
694  this->alloc();
695  }
696  m_cols = o.m_cols;
697  m_rows = o.m_rows;
698  m_allocator.copy2d(this->m_ptr,o.ptr(),m_pitch*sizeof(value_type),o.m_pitch*sizeof(value_type),m_rows,m_cols,OM());
699  return *this;
700  }
701 
707  operator[](const index_type& idx)
708  {
709  assert(idx>=0);
710  index_type row = idx/m_cols;
711  index_type col = idx%m_cols;
712  assert((size_type)row < m_rows);
713  assert((size_type)col < m_cols);
714  return reference_type(this->m_ptr+row*m_pitch+col);
715  }
716 
724  operator[](const index_type& idx)const
725  {
726  return const_cast<pitched_memory&>(*this)(idx);
727  }
728 
737  operator()(const index_type& i, const index_type& j){
738  assert(i>=0);
739  assert(j>=0);
740  assert((size_type)i < m_rows);
741  assert((size_type)j < m_cols);
742  return reference_type(this->m_ptr+i*m_pitch+j);
743  }
746  operator()(const index_type& i, const index_type& j)const{
747  return const_cast<pitched_memory&>(*this)(i,j);
748  }
749 
750 
764  size_type size = 1;
765  assert(shape.size()>=2);
766  const int pitched_dim = shape.size()-1;
767  for (int i = shape.size()-1; i >= 0; --i)
768  {
769  if(shape[i] == 1){
770  strides[i] = 0;
771  }else if(i == pitched_dim){
772  strides[i] = 1;
773  size *= pitch();
774  }else {
775  strides[i] = size;
776  size *= shape[i];
777  }
778  }
779  }
788  size_type size = 1;
789  assert(shape.size()>=2);
790  const size_type pitched_dim = 0;
791  for (unsigned int i = 0; i < shape.size(); ++i)
792  {
793  if(shape[i] == 1){
794  strides[i] = 0;
795  }else if(i == pitched_dim){
796  strides[i] = 1;
797  size *= pitch();
798  }else {
799  strides[i] = size;
800  size *= shape[i];
801  }
802  }
803  }
804  };
805 
809  template<class M, class L>
810  struct tensor_info{
811  typedef unsigned int size_type;
812  typedef int index_type;
813  typedef M data_memory_space;
814 
818 
823 
826 
828  size_type size(){ return host_shape.size(); }
829 
832 
834  void resize(size_type s){
835  host_shape.set_size(s);
837  //data_shape.set_size(s);
838  //data_stride.set_size(s);
839  }
840 
845  //, data_shape(o.data_shape)
846  //, data_stride(o.data_stride)
847  {}
848 
850  template<class OM>
854  //, data_shape(o.data_shape)
855  //, data_stride(o.data_stride)
856  {}
857 
858  } ;
859 
860  template<class V, class M, class L>
861  class tensor;
862  template<class V, class M, class L>
863  class tensor_view;
864 
865 
866 
870  template<class V, class M, class L=row_major>
871  class tensor{
872  public:
880  typedef typename memory_type::pointer_type pointer_type;
881  typedef typename memory_type::const_pointer_type const_pointer_type;
882  typedef L memory_layout_type;
883 
886 
887  protected:
890 
892  boost::shared_ptr<memory_type> m_memory;
893 
895  V* m_ptr;
896 
898  template <class _V, class _M, class _L>
899  friend class tensor_view;
900 
913  size_type
914  index_of(int D, index_type* arr)const{
915  index_type pos = 0;
916  for(int i=0; i<D; i++){
917  index_type temp = arr[i];
918  if(temp<0) temp = m_info.host_shape[i]+temp;
919  pos += temp * m_info.host_stride[i];
920  }
921  return pos;
922  }
929  linear_memory<V,M> d(t.size());
931  t.m_ptr = d.ptr();
932  t.m_memory.reset(new memory<V,M>(d.release(), d.size()));
933  }
934 
941  typename tensor<V,M,L>::size_type row,col,pitch;
942  detail::get_pitched_params(row,col,pitch,t.m_info.host_shape, t.m_info.host_stride,L());
943  pitched_memory<V,M> d(row,col);
945  t.m_ptr = d.ptr();
946  t.m_memory.reset(new memory<V,M>(d.release(),d.size()));
947  }
948 
949 
950  public:
963  template<std::size_t D>
964  size_type
965  index_of(const extent_gen<D>& eg)const{
966  index_type pos = 0;
967  for(int i=0; i<D; i++){
968  index_type temp = eg.ranges_[i].finish();
969  if(temp<0) temp = m_info.host_shape[i]+temp;
970  pos += temp * m_info.host_stride[i];
971  }
972  return pos;
973  }
978 
979  index_type ndim()const{ return m_info.host_shape.size(); }
980 
984  size_type shape(const index_type& i)const{return m_info.host_shape[i];}
985 
989  index_type stride(const index_type& i)const{return m_info.host_stride[i];}
990 
992  pointer_type ptr() {return m_ptr;}
993 
998  const_pointer_type ptr() const {return m_ptr;}
999 
1001  void set_ptr_offset(long int i){ m_ptr = m_memory->ptr() + i; }
1002 
1004  boost::shared_ptr<memory_type>& mem(){ return m_memory; }
1009  const boost::shared_ptr<memory_type>& mem()const{ return m_memory; }
1010 
1011 
1014  size_type size()const{
1015  return std::accumulate(m_info.host_shape[0].ptr, m_info.host_shape[0].ptr+m_info.host_shape.size(), 1, std::multiplies<index_type>());
1016  }
1017 
1026 #ifndef NDEBUG
1028 #endif
1029  return std::accumulate(m_info.host_shape[0].ptr, m_info.host_shape[0].ptr+m_info.host_shape.size(), 1, std::multiplies<index_type>());
1030  }
1031 
1033  std::vector<size_type> shape()const{
1034  if(ndim()==0)
1035  return std::vector<size_type>();
1036  return std::vector<size_type>(m_info.host_shape[0].ptr, m_info.host_shape[0].ptr+m_info.host_shape.size());
1037  }
1038 
1044  std::vector<size_type> effective_shape()const{
1045  std::vector<size_type> shape;
1046  shape.reserve(ndim());
1047  if(ndim()==0)
1048  return shape;
1049  std::remove_copy_if(
1050  m_info.host_shape[0].ptr,
1052  std::back_inserter(shape),
1053  std::bind2nd(std::equal_to<size_type>(),1));
1054  return shape;
1055  }
1056 
1058  const info_type& info()const{return m_info;}
1059 
1061  info_type& info(){return m_info;}
1062 
1064  bool is_c_contiguous()const{
1066  }
1067 
1069  bool is_2dcopyable()const{
1071  }
1072  // accessors
1074 
1085  size_type* virtualstride = new size_type[ndim];
1086  size_type pos = 0;
1088  // row major
1089  { size_type virt_size = 1;
1090  for(int i=ndim-1;i>=0;--i){
1091  virtualstride[i] = virt_size;
1092  virt_size *= m_info.host_shape[i];
1093  }
1094  }
1095  for(size_type i=0; i<ndim; ++i){
1096  pos += (idx/virtualstride[i])*m_info.host_stride[i];
1097  idx -= (idx/virtualstride[i])*virtualstride[i];
1098  }
1099  }else{
1100  // column major
1101  { size_type virt_size = 1;
1102  for(unsigned int i=0;i<ndim;++i){
1103  virtualstride[i] = virt_size;
1104  virt_size *= m_info.host_shape[i];
1105  }
1106  }
1107  for(int i=ndim-1; i>=0; --i){
1108  pos += (idx/virtualstride[i])*m_info.host_stride[i];
1109  idx -= (idx/virtualstride[i])*virtualstride[i];
1110  }
1111  }
1112  delete[] virtualstride;
1113  return reference_type(m_ptr + pos);
1114  }
1115 
1118  return const_cast<tensor&>(*this)[idx];
1119  }
1120 
1127 #ifndef NDEBUG
1128  cuvAssert(ndim()==1);
1129  cuvAssert((i0>=0 && (size_type)i0 < shape(0)) || (i0<0 && (size_type)(-i0)<shape(0)+1) )
1130 #endif
1131  if(i0>=0){
1132  return reference_type(m_ptr + i0);
1133  }else{
1134  return reference_type(m_ptr + shape(0) - i0);
1135  }
1136  }
1138  const_reference_type operator()(index_type i0)const{ return const_cast<tensor&>(*this)(i0); }
1139 
1141  const_reference_type operator()(index_type i0, index_type i1)const{ return const_cast<tensor&>(*this)(i0,i1); }
1144 #ifndef NDEBUG
1145  cuvAssert(ndim()==2);
1146  cuvAssert((i0>=0 && (size_type)i0 < shape(0)) || (i0<0 && (size_type)(-i0)<shape(0)+1) )
1147  cuvAssert((i1>=0 && (size_type)i1 < shape(1)) || (i1<0 && (size_type)(-i1)<shape(1)+1) )
1148 #endif
1149  index_type arr[2] = {i0,i1};
1150  return reference_type(m_ptr + index_of( 2,arr));
1151  }
1152 
1154  const_reference_type operator()(index_type i0, index_type i1, index_type i2)const{ return const_cast<tensor&>(*this)(i0,i1,i2); }
1157 #ifndef NDEBUG
1158  cuvAssert(ndim()==3);
1159  cuvAssert((i0>=0 && (size_type)i0 < shape(0)) || (i0<0 && (size_type)-i0<shape(0)+1) )
1160  cuvAssert((i1>=0 && (size_type)i1 < shape(1)) || (i1<0 && (size_type)-i1<shape(1)+1) )
1161  cuvAssert((i2>=0 && (size_type)i2 < shape(2)) || (i2<0 && (size_type)-i2<shape(2)+1) )
1162 #endif
1163  index_type arr[3] = {i0,i1,i2};
1164  return reference_type(m_ptr + index_of( 3,arr));
1165  }
1166 
1168  const_reference_type operator()(index_type i0, index_type i1, index_type i2, index_type i3)const{ return const_cast<tensor&>(*this)(i0,i1,i2,i3); }
1171 #ifndef NDEBUG
1172  cuvAssert(ndim()==4);
1173  cuvAssert((i0>=0 && (size_type)i0 < shape(0)) || (i0<0 && (size_type)-i0<shape(0)+1) )
1174  cuvAssert((i1>=0 && (size_type)i1 < shape(1)) || (i1<0 && (size_type)-i1<shape(1)+1) )
1175  cuvAssert((i2>=0 && (size_type)i2 < shape(2)) || (i2<0 && (size_type)-i2<shape(2)+1) )
1176  cuvAssert((i3>=0 && (size_type)i3 < shape(3)) || (i3<0 && (size_type)-i3<shape(3)+1) )
1177 #endif
1178  index_type arr[4] = {i0,i1,i2,i3};
1179  return reference_type(m_ptr + index_of( 4,arr));
1180  }
1181 
1183  const_reference_type operator()(index_type i0, index_type i1, index_type i2, index_type i3, index_type i4)const{ return const_cast<tensor&>(*this)(i0,i1,i2,i3,i4); }
1186 #ifndef NDEBUG
1187  cuvAssert(ndim()==5);
1188  cuvAssert((i0>=0 && (size_type)i0 < shape(0)) || (i0<0 && (size_type)-i0<shape(0)+1) )
1189  cuvAssert((i1>=0 && (size_type)i1 < shape(1)) || (i1<0 && (size_type)-i1<shape(1)+1) )
1190  cuvAssert((i2>=0 && (size_type)i2 < shape(2)) || (i2<0 && (size_type)-i2<shape(2)+1) )
1191  cuvAssert((i3>=0 && (size_type)i3 < shape(3)) || (i3<0 && (size_type)-i3<shape(3)+1) )
1192  cuvAssert((i4>=0 && (size_type)i4 < shape(4)) || (i4<0 && (size_type)-i4<shape(4)+1) )
1193 #endif
1194  index_type arr[5] = {i0,i1,i2,i3,i4};
1195  return reference_type(m_ptr + index_of( 5,arr));
1196  } // accessing stored values
1198 
1205  tensor():m_ptr(NULL){}
1206 
1207  // ****************************************************************
1208  // Constructing from other tensor
1209  // ****************************************************************
1210 
1216  tensor(const tensor& o)
1217  : m_info(o.m_info) // copy only shape
1218  , m_memory(o.m_memory) // increase ref counter
1219  , m_ptr(o.m_ptr){} // same pointer in memory
1220 
1225  template<class OM>
1227  :m_info(o.info()) // primarily to copy shape
1228  ,m_ptr(NULL)
1229  {
1230  copy_memory(*this, o, linear_memory_tag());
1231  m_ptr = m_memory->ptr();
1232  }
1233 
1238  explicit tensor(const tensor& o, pitched_memory_tag)
1239  :m_info(o.m_info) // primarily to copy shape
1240  ,m_ptr(NULL)
1241  {
1242  copy_memory(*this, o, pitched_memory_tag());
1243  m_ptr = m_memory->ptr();
1244  }
1245 
1250  template<class OM>
1252  :m_info(o.info()) // primarily to copy shape
1253  ,m_ptr(NULL)
1254  {
1255  copy_memory(*this, o, pitched_memory_tag());
1256  m_ptr = m_memory->ptr();
1257  }
1258 
1263  explicit tensor(const tensor& o, linear_memory_tag)
1264  :m_info(o.m_info) // primarily to copy shape
1265  ,m_ptr(NULL)
1266  {
1267  copy_memory(*this, o, linear_memory_tag());
1268  m_ptr = m_memory->ptr();
1269  }
1274  template<class OM>
1276  :m_info(o.info()) // primarily to copy shape
1277  ,m_ptr(NULL)
1278  {
1279  copy_memory(*this, o, linear_memory_tag());
1280  m_ptr = m_memory->ptr();
1281  }
1288  template<class OL>
1289  explicit tensor(const tensor<value_type,M,OL>& o)
1290  : m_memory(o.mem()) // increase ref counter
1291  , m_ptr(const_cast<pointer_type>( o.ptr() )) { // same pointer in memory
1296  }
1297 
1298  // ****************************************************************
1299  // Constructing from SHAPE
1300  // ****************************************************************
1301 
1305  explicit tensor(const size_type i)
1306  :m_ptr(NULL)
1307  {
1308  m_info.resize(1);
1309  m_info.host_shape[0] = i;
1310  allocate(*this,linear_memory_tag());
1311  }
1315  explicit tensor(const size_type i, const int j)
1316  :m_ptr(NULL)
1317  {
1318  m_info.resize(2);
1319  m_info.host_shape[0] = i;
1320  m_info.host_shape[1] = j;
1321  allocate(*this,linear_memory_tag());
1322  }
1326  template<std::size_t D>
1327  explicit tensor(const extent_gen<D>& eg)
1328  :m_ptr(NULL)
1329  {
1330  m_info.resize(D);
1331  for(std::size_t i=0;i<D;i++)
1332  m_info.host_shape[i] = eg.ranges_[i].finish();
1333  allocate(*this,linear_memory_tag());
1334  }
1335 
1341  explicit tensor(const std::vector<size_type>& eg)
1342  :m_ptr(NULL)
1343  {
1344  m_info.resize(eg.size());
1345  for(std::size_t i=0;i<eg.size();i++)
1346  m_info.host_shape[i] = eg[i];
1347  allocate(*this,linear_memory_tag());
1348  }
1349 
1355  explicit tensor(const std::vector<size_type>& eg, pitched_memory_tag)
1356  :m_ptr(NULL)
1357  {
1358  m_info.resize(eg.size());
1359  for(std::size_t i=0;i<eg.size();i++)
1360  m_info.host_shape[i] = eg[i];
1361  allocate(*this,pitched_memory_tag());
1362  }
1363 
1367  template<std::size_t D>
1368  explicit tensor(const extent_gen<D>& eg, pitched_memory_tag)
1369  :m_ptr(NULL)
1370  {
1371  m_info.resize(D);
1372  for(std::size_t i=0;i<D;i++)
1373  m_info.host_shape[i] = eg.ranges_[i].finish();
1374  allocate(*this,pitched_memory_tag());
1375  }
1376 
1377  // ****************************************************************
1378  // Constructing from shape and raw pointer
1379  // ****************************************************************
1380 
1386  template<std::size_t D>
1387  explicit tensor(const extent_gen<D>& eg, value_type* ptr)
1388  :m_ptr(ptr)
1389  {
1390  m_info.resize(D);
1391  size_type size = 1;
1393  for(int i=D-1;i>=0;i--){
1394  m_info.host_shape[i] = eg.ranges_[i].finish();
1395  m_info.host_stride[i] = size;
1396  size *= eg.ranges_[i].finish();
1397  }
1398  else
1399  for(std::size_t i=0;i<D;i++){
1400  m_info.host_shape[i] = eg.ranges_[i].finish();
1401  m_info.host_stride[i] = size;
1402  size *= eg.ranges_[i].finish();
1403  }
1404  }
1405  explicit tensor(const std::vector<size_type>& shape, value_type* ptr)
1406  :m_ptr(ptr)
1407  {
1408  unsigned int D = shape.size();
1409  m_info.resize(D);
1410  size_type size = 1;
1412  for(int i=D-1;i>=0;i--){
1413  m_info.host_shape[i] = shape[i];
1414  m_info.host_stride[i] = size;
1415  size *= shape[i];
1416  }
1417  else
1418  for(std::size_t i=0;i<D;i++){
1419  m_info.host_shape[i] = shape[i];
1420  m_info.host_stride[i] = size;
1421  size *= shape[i];
1422  }
1423  }
1430  template<int D, int E>
1431  explicit tensor(const index_gen<D,E>& idx, value_type* ptr)
1432  :m_ptr(ptr)
1433  {
1434  m_info.resize(D);
1435  size_type size = 1;
1437  for(int i=D-1;i>=0;i--){
1438  m_info.host_shape[i] = idx.ranges_[i].finish();
1439  m_info.host_stride[i] = size;
1440  size *= idx.ranges_[i].finish();
1441  }
1442  else
1443  for(std::size_t i=0;i<D;i++){
1444  m_info.host_shape[i] = idx.ranges_[i].finish();
1445  m_info.host_stride[i] = size;
1446  size *= idx.ranges_[i].finish();
1447  }
1448  }
1449  // @} // constructors
1450 
1451 
1452  // ****************************************************************
1453  // assignment operators (try not to reallocate if shapes match)
1454  // ****************************************************************
1455 
1463  template<class _M, class _L>
1465  static_cast<tensor_view<V,M,L>&>(*this).operator=(o);
1466  return *this;
1467  }
1468 
1475  if(this==&o) return *this; // check for self-assignment
1476  /*
1477  *if(copy_memory(*this,o,false))
1478  * return *this;
1479  */
1480  m_memory = o.mem();
1481  m_ptr = const_cast<pointer_type>(o.ptr());
1482  m_info = o.info();
1483  return *this;
1484  }
1485 
1489  template<class _V>
1490  typename boost::enable_if_c<boost::is_convertible<_V,value_type>::value, tensor&>::type
1491  operator=(const _V& scalar){
1492  fill(*this, scalar);
1493  return *this;
1494  }
1495 
1503  template<class OM>
1505  if(!copy_memory(*this,o,false))
1506  copy_memory(*this,o,linear_memory_tag());
1507  if(mem())
1508  // if mem() does not exist, we're just wrapping a pointer
1509  // of a std::vector or so -> simply keep it
1510  m_ptr = mem()->ptr();
1511  return *this;
1512  }
1513 
1519  template<class OL>
1521  m_memory = o.mem();
1522  m_ptr = const_cast<V*>(o.ptr());
1527  return *this;
1528  }
1529  // assignment
1531 
1532 
1536  template<class T>
1538  tensor t;
1539  const tensor& o = *this;
1540  t.m_info = o.info();
1541  copy_memory(t,o,tag);
1542  t.m_ptr = t.mem()->ptr();
1543  return t;
1544  }
1545 
1549  tensor copy()const{
1550  return copy(linear_memory_tag());
1551  }
1552 
1553 
1579  template<int D, int E>
1581  operator[](const index_gen<D,E>& idx)const
1582  {
1584  const tensor& o = *this;
1585  t.m_memory = o.mem();
1586  t.m_ptr = const_cast<pointer_type>(o.ptr());
1587 
1588  std::vector<int> shapes;
1589  std::vector<int> strides;
1590  shapes.reserve(o.ndim());
1591  strides.reserve(o.ndim());
1592  for(std::size_t i=0;i<D;i++){
1593  int start = idx.ranges_[i].get_start(0);
1594  int finish = idx.ranges_[i].get_finish(o.shape(i));
1595  int stride = idx.ranges_[i].stride();
1596  if (start <0) start += o.shape(i);
1597  if (finish<0) finish += o.shape(i);
1598 #ifndef NDEBUG
1599  cuvAssert(finish>start);
1600 #endif
1601  t.m_ptr += start*o.stride(i);
1602  if(idx.ranges_[i].is_degenerate()){
1603  // skip dimension
1604  }else{
1605  shapes.push_back((finish-start)/stride);
1606  strides.push_back(o.stride(i)*stride);
1607  }
1608  }
1609 
1610 
1611  // adds missing shapes
1612  for(int i = D; i < o.ndim();i++){
1613  shapes.push_back(o.shape(i));
1614  strides.push_back(o.stride(i));
1615  }
1616 
1617  // store in m_info
1618  t.m_info.resize(shapes.size());
1619  std::copy(shapes.begin(),shapes.end(),t.m_info.host_shape[0].ptr);
1620  std::copy(strides.begin(),strides.end(),t.m_info.host_stride[0].ptr);
1621  return t; // should not copy mem, only m_info
1622  }
1623 
1631  template<std::size_t D>
1632  void reshape(const extent_gen<D>& eg){
1633  std::vector<size_type> shape(D);
1634  for(std::size_t i=0;i<D;i++)
1635  shape[i] = eg.ranges_[i].finish();
1636  reshape(shape);
1637  }
1645  void reshape(const std::vector<size_type>& shape){
1646  size_type new_size = std::accumulate(shape.begin(),shape.end(),1,std::multiplies<size_type>());
1647  if(!is_c_contiguous())
1648  throw std::runtime_error("cannot reshape: tensor is not c_contiguous");
1649  if(size() != new_size)
1650  throw std::runtime_error("cannot reshape: products do not match");
1651  m_info.resize(shape.size());
1652  size_type size = 1;
1654  for(int i=shape.size()-1;i>=0;i--){
1655  m_info.host_shape[i] = shape[i];
1656  m_info.host_stride[i] = size;
1657  size *= shape[i];
1658  }
1659  else
1660  for(std::size_t i=0;i<shape.size();i++){
1661  m_info.host_shape[i] = shape[i];
1662  m_info.host_stride[i] = size;
1663  size *= shape[i];
1664  }
1665  }
1672  reshape(extents[r][c]);
1673  }
1674 
1680  void resize(const std::vector<size_type>& shape){
1681  if(ndim()!=0){
1682  size_type new_size = std::accumulate(shape.begin(),shape.end(),1,std::multiplies<size_type>());
1683  if(is_c_contiguous() && size()==new_size)
1684  reshape(shape);
1685  else
1686  *this = tensor(shape);
1687  }
1688  else
1689  *this = tensor(shape);
1690  }
1698  template<std::size_t D>
1699  void resize(const extent_gen<D>& eg){
1700  std::vector<size_type> shape(D);
1701  for(std::size_t i=0;i<D;i++)
1702  shape[i] = eg.ranges_[i].finish();
1703  resize(shape);
1704  }
1705 
1709  void dealloc(){
1710  m_memory.reset();
1711  m_ptr = NULL;
1713  }
1714 
1715  };
1716 
1720  template<class V, class M, class L=row_major>
1722  : public tensor<V,M,L>
1723  {
1724  private:
1725  typedef tensor<V,M,L> super;
1726  using super::m_memory;
1727  using super::m_ptr;
1728  using super::m_info;
1729  public:
1732 
1737  if(!copy_memory(*this, o, false))
1738  throw std::runtime_error("copying tensor to tensor_view did not succeed. Maybe a shape mismatch?");
1739  return *this;
1740  }
1745  if(!copy_memory(*this, o, false))
1746  throw std::runtime_error("copying tensor to tensor_view did not succeed. Maybe a shape mismatch?");
1747  return *this;
1748  }
1754  template<class _V>
1755  typename boost::enable_if_c<boost::is_convertible<_V,V>::value, tensor_view&>::type
1756  operator=(const _V& scalar){
1757  super::operator=(scalar);
1758  return *this;
1759  }
1765  template<class OM>
1767  if(!copy_memory(*this, o, false))
1768  throw std::runtime_error("copying tensor to tensor_view did not succeed. Maybe a shape mismatch?");
1769  return *this;
1770  }
1771 
1777  template<class OM>
1779  if(!copy_memory(*this, o, false))
1780  throw std::runtime_error("copying tensor to tensor_view did not succeed. Maybe a shape mismatch?");
1781  return *this;
1782  }
1783 
1816  template<int D, int E>
1817  explicit tensor_view(const tensor<V,M,L>&o, const index_gen<D,E>& idx)
1818  {
1819  m_memory = o.mem();
1820  m_ptr = const_cast<V*>(o.ptr());
1821  std::vector<int> shapes;
1822  std::vector<int> strides;
1823  shapes.reserve(o.ndim());
1824  strides.reserve(o.ndim());
1825  for(std::size_t i=0;i<D;i++){
1826  int start = idx.ranges_[i].get_start(0);
1827  int finish = idx.ranges_[i].get_finish(o.shape(i));
1828  int stride = idx.ranges_[i].stride();
1829  if (start <0) start += o.shape(i);
1830  if (finish<0) finish += o.shape(i);
1831 #ifndef NDEBUG
1832  cuvAssert(finish>start);
1833 #endif
1834  m_ptr += start*o.stride(i);
1835  if(idx.ranges_[i].is_degenerate()){
1836  // skip dimension
1837  }else{
1838  shapes.push_back((finish-start)/stride);
1839  strides.push_back(o.stride(i)*stride);
1840  }
1841  }
1842 
1843  // adds missing shapes
1844  for(int i = D; i < o.ndim();i++){
1845  shapes.push_back(o.shape(i));
1846  strides.push_back(o.stride(i));
1847  }
1848 
1849  // store in m_info
1850  m_info.resize(shapes.size());
1851  std::copy(shapes.begin(),shapes.end(),m_info.host_shape[0].ptr);
1852  std::copy(strides.begin(),strides.end(),m_info.host_stride[0].ptr);
1853  }
1861  template<int D, int E>
1862  explicit tensor_view( const index_gen<D,E>& idx, const tensor<V,M,L>&o)
1863  {
1864  m_memory = o.mem();
1865  m_ptr = const_cast<V*>(o.ptr());
1866  std::vector<int> shapes;
1867  std::vector<int> strides;
1868  shapes.reserve(o.ndim());
1869  strides.reserve(o.ndim());
1870  for(std::size_t i=0;i<D;i++){
1871  int start = idx.ranges_[i].get_start(0);
1872  int finish = idx.ranges_[i].get_finish(o.shape(i));
1873  int stride = idx.ranges_[i].stride();
1874  if (start <0) start += o.shape(i);
1875  if (finish<0) finish += o.shape(i);
1876 #ifndef NDEBUG
1877  cuvAssert(finish>start);
1878 #endif
1879  m_ptr += start*o.stride(i);
1880  if(idx.ranges_[i].is_degenerate()){
1881  // skip dimension
1882  }else{
1883  shapes.push_back((finish-start)/stride);
1884  strides.push_back(o.stride(i)*stride);
1885  }
1886  }
1887 
1888  // adds missing shapes
1889  for(int i = D; i < o.ndim();i++){
1890  shapes.push_back(o.shape(i));
1891  strides.push_back(o.stride(i));
1892  }
1893 
1894  // store in m_info
1895  m_info.resize(shapes.size());
1896  std::copy(shapes.begin(),shapes.end(),m_info.host_shape[0].ptr);
1897  std::copy(strides.begin(),strides.end(),m_info.host_stride[0].ptr);
1898  }
1899  };
1900 
1901  //namespace detail{
1903  template<class V, class M0, class M1, class L0, class L1>
1904  bool copy_memory(tensor<V,M0,L0>&dst, const tensor<V,M1,L1>&src, bool force_dst_contiguous){
1905  typedef typename tensor<V,M0,L0>::size_type size_type;
1907  if(dst.effective_shape() == src.effective_shape() && dst.ptr()){
1908  if(dst.is_c_contiguous() && src.is_c_contiguous()){
1909  // can copy w/o bothering about m_memory
1910  a.copy(dst.ptr(), src.ptr(), src.size(), M1());
1911  }else if(dst.is_c_contiguous() && src.is_2dcopyable()){
1912  size_type row,col,pitch;
1913  detail::get_pitched_params(row,col,pitch,src.info().host_shape, src.info().host_stride,L1());
1914  a.copy2d(dst.ptr(), src.ptr(), col*sizeof(V),pitch*sizeof(V),row,col,M1());
1915  }else if(!force_dst_contiguous && dst.is_2dcopyable() && src.is_c_contiguous()){
1916  size_type row,col,pitch;
1917  detail::get_pitched_params(row,col,pitch,dst.info().host_shape, dst.info().host_stride,L0());
1918  a.copy2d(dst.ptr(), src.ptr(), pitch*sizeof(V),col*sizeof(V),row,col,M1());
1919  }else if(!force_dst_contiguous && dst.is_2dcopyable() && src.is_c_contiguous()){
1920  size_type srow,scol,spitch;
1921  size_type drow,dcol,dpitch;
1922  detail::get_pitched_params(drow,dcol,dpitch,dst.info().host_shape, dst.info().host_stride,L0());
1923  detail::get_pitched_params(srow,scol,spitch,src.info().host_shape, src.info().host_stride,L1());
1924  cuvAssert(scol==srow);
1925  cuvAssert(dcol==drow);
1926  a.copy2d(dst.ptr(), src.ptr(), dpitch*sizeof(V),spitch*sizeof(V),srow,scol,M1());
1927  }else{
1928  throw std::runtime_error("copying of generic strides not implemented yet");
1929  }
1931  dst.info().host_stride.reverse();
1932  dst.info().host_shape.reverse();
1933  }
1934  return true;
1935  }
1936  return false;
1937  }
1938 
1940  template<class V, class M0, class M1, class L0, class L1>
1942  typedef typename tensor<V,M0,L0>::size_type size_type;
1943  if(copy_memory(dst,src, true)) // destination must be contiguous
1944  return;
1945  dst.info().resize(src.ndim());
1946  dst.info().host_shape = src.info().host_shape;
1947  linear_memory<V,M0> d(src.size());
1948  d.set_strides(dst.info().host_stride,dst.info().host_shape, L0());
1950  if(src.is_c_contiguous()){
1951  // easiest case: both linear, simply copy
1952  a.copy(d.ptr(), src.ptr(), src.size(), M1());
1953  }
1954  else if(src.is_2dcopyable()){
1955  // other memory is probably a pitched memory or some view onto an array
1956  size_type row,col,pitch;
1957  detail::get_pitched_params(row,col,pitch,src.info().host_shape, src.info().host_stride,L1());
1958  a.copy2d(d.ptr(), src.ptr(), col*sizeof(V),pitch*sizeof(V),row,col,M1());
1959  }else{
1960  throw std::runtime_error("copying arbitrarily strided memory not implemented");
1961  }
1962  dst.mem().reset(new memory<V,M0>(d.release(),d.size()));
1964  dst.info().host_stride.reverse();
1965  dst.info().host_shape.reverse();
1966  }
1967  }
1968 
1970  template<class V, class M0, class M1, class L0, class L1>
1972  typedef typename tensor<V,M0,L0>::size_type size_type;
1973  assert(src.ndim()>=2);
1974  if(copy_memory(dst,src,false)) // destination need not be contiguous
1975  return;
1976  dst.info().resize(src.ndim());
1977  dst.info().host_shape = src.info().host_shape;
1978  size_type row,col,pitch;
1979  detail::get_pitched_params(row,col,pitch,src.info().host_shape, src.info().host_stride,L1());
1980  pitched_memory<V,M0> d(row,col);
1981  //dst.mem().reset(d);
1982  d->set_strides(dst.info().host_stride,dst.info().host_shape, L0());
1984  if(src.is_2dcopyable()){
1985  // other memory is probably a pitched memory or some view onto an array
1986  detail::get_pitched_params(row,col,pitch,src.info().host_shape, src.info().host_stride,L1());
1987  a.copy2d(d.ptr(), src.m_ptr, d.pitch()*sizeof(V),pitch*sizeof(V),row,col,M1());
1988  }else{
1989  throw std::runtime_error("copying arbitrarily strided memory not implemented");
1990  }
1991  dst.mem().reset(new memory<V,M0>(d.release(),d.size()));
1992 
1994  dst.info().host_stride.reverse();
1995  dst.info().host_shape.reverse();
1996  }
1997  }
1998  //}
1999  // data_structures
2001 
2002 
2009  template<class V, class V2, class M, class M2, class L>
2010  bool equal_shape(const tensor<V,M,L>& a, const tensor<V2,M2,L>& b){
2011  return a.effective_shape()==b.effective_shape();
2012  }
2013 
2017 
2018  template<class Mat, class NewVT>
2021  };
2023  template<class Mat, class NewML>
2026  };
2028  template<class Mat, class NewMS>
2031  };
2032 
2035 }
2036 
2043 namespace std{
2049  template<class V>
2050  ostream& operator<<(ostream& o, const cuv::linear_memory<V, cuv::host_memory_space>& t){
2051  o << "[ ";
2052  for(unsigned int i=0;i<t.size();i++)
2053  o<< t[i]<<" ";
2054  o <<"]";
2055  return o;
2056  }
2062  template<class V>
2063  ostream& operator<<(ostream& o, const cuv::linear_memory<V, cuv::dev_memory_space>& t_){
2065  o << "[ ";
2066  for(unsigned int i=0;i<t.size();i++)
2067  o<< t[i]<<" ";
2068  o <<"]";
2069  return o;
2070  }
2071 
2077  template<class V>
2078  ostream& operator<<(ostream& o, const cuv::pitched_memory<V, cuv::host_memory_space>& t){
2079  o << "[ ";
2080  for(unsigned int i=0;i<t.rows();i++){
2081  for(unsigned int j=0;j<t.rows();j++){
2082  o<< t(i,j)<<" ";
2083  }
2084  if(i<t.rows()-1)
2085  o<< std::endl;
2086  }
2087  o <<"]";
2088  return o;
2089  }
2095  template<class V>
2096  ostream& operator<<(ostream& o, const cuv::pitched_memory<V, cuv::dev_memory_space>& t_){
2098  o << "[ ";
2099  for(unsigned int i=0;i<t.rows();i++){
2100  for(unsigned int j=0;j<t.rows();j++){
2101  o<< t(i,j)<<" ";
2102  }
2103  if(i<t.rows()-1)
2104  o<< std::endl;
2105  }
2106  o <<"]";
2107  return o;
2108  }
2109 
2116  template<class V, class L>
2117  ostream& operator<<(ostream& o, const cuv::tensor<V, cuv::dev_memory_space, L>& t){
2118  return o << cuv::tensor<V,cuv::host_memory_space,L>(t);
2119  }
2126  template<class V, class L>
2127  ostream& operator<<(ostream& o, const cuv::tensor<V, cuv::host_memory_space, L>& t){
2128  if(t.ndim()==0)
2129  return o << "[]";
2130 
2131  if(t.ndim()==1){
2132  o << "[ ";
2133  for(unsigned int i=0;i<t.shape(0);i++) o<< t[i]<<" ";
2134  return o <<"]";
2135  }
2136  if(t.ndim()==2){
2137  o << "[";
2138  for(unsigned int i=0;i<t.shape(0);++i){
2139  if(i>0)
2140  o<<" ";
2141  o << "[ ";
2142  for(unsigned int j=0;j<t.shape(1);j++) o<< t(i,j)<<" ";
2143  o <<"]";
2144  if(i != t.shape(0)-1)
2145  o <<std::endl;
2146  }
2147  return o<<"]";
2148  }
2149  if(t.ndim()==3){
2150  o<<"["<<std::endl;
2151  for(unsigned int l=0;l<t.shape(0);l++){
2152  o << "[";
2153  for(unsigned int i=0;i<t.shape(1);++i){
2154  if(i>0)
2155  o<<" ";
2156  o << "[ ";
2157  //for(unsigned int j=0;j<t.shape(2);j++) o<< t(l,i,j)<<" ";
2158  for(unsigned int j=0;j<t.shape(2);j++) o<< t[l*t.shape(1)*t.shape(2) + i*t.shape(2) + j]<<" ";
2159  o <<"]";
2160  if(i != t.shape(1)-1)
2161  o <<std::endl;
2162  }
2163  o<<"]";
2164  if(l<t.shape(0)-1)
2165  o<<std::endl;
2166  }
2167  return o<<"]";
2168  }
2169  throw std::runtime_error("printing of tensors with >3 dimensions not implemented");
2170  }
2171 } // io
2173 #endif /* __TENSOR2_HPP__ */