MADNESS  version 0.9
tensortrain.h
Go to the documentation of this file.
1 /*
2  This file is part of MADNESS.
3 
4  Copyright (C) 2007,2010 Oak Ridge National Laboratory
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 
20  For more information please contact:
21 
22  Robert J. Harrison
23  Oak Ridge National Laboratory
24  One Bethel Valley Road
25  P.O. Box 2008, MS-6367
26 
27  email: harrisonrj@ornl.gov
28  tel: 865-241-3937
29  fax: 865-572-0680
30 
31  $Id: srconf.h 2816 2012-03-23 14:59:52Z 3ru6ruWu $
32 */
33 
34 #ifndef TENSORTRAIN_H_
35 #define TENSORTRAIN_H_
36 
37 #include <madness/tensor/tensor.h>
38 #include <madness/tensor/srconf.h>
39 #include <madness/tensor/clapack.h>
41 #include <madness/fortran_ctypes.h>
42 
43 
54 namespace madness {
55 
68  template<typename T>
69  class TensorTrain {
70 
73  std::vector<Tensor<T> > core;
75  bool zero_rank;
76 
77  public:
78 
80 
90  TensorTrain(const Tensor<T>& t, double eps)
91  : core(), zero_rank(false) {
92 
93  MADNESS_ASSERT(t.size() != 0);
94  MADNESS_ASSERT(t.ndim() != 0);
95 
96  std::vector<long> dims(t.ndim());
97  for (int d=0; d<t.ndim(); ++d) dims[d]=t.dim(d);
98  decompose(t.flat(),eps,dims);
99 
100  }
101 
103 
114  TensorTrain(const Tensor<T>& t, double eps, const std::vector<long> dims)
115  : core(), zero_rank(false) {
116 
117  MADNESS_ASSERT(t.size() != 0);
118  MADNESS_ASSERT(t.ndim() != 0);
119  decompose(t,eps,dims);
120  }
121 
123 
127  void decompose(const Tensor<T>& t, double eps,
128  const std::vector<long>& dims) {
129 
130  core.resize(dims.size());
131  eps=eps/sqrt(dims.size()-1); // error is relative
132 
133  // the maximum rank is the smaller one of the ranks unfolded from the
134  // left and the right.
135  int rmax1=1;
136  int rmax2=1;
137  long n1=1, n2=1;
138  long lwork=0;
139  for (int i=0, j=dims.size()-1; i<=j; ++i, --j) {
140  rmax1*=dims[i];
141  rmax2*=dims[j];
142 
143  // work array for dgesvd
144  n1*=dims[i];
145  long m1=t.size()/dims[i];
146  n2*=dims[j];
147  long m2=t.size()/dims[j];
148  long max1=std::max(n1,m1);
149  long max2=std::max(n2,m2);
150  long min1=std::min(n1,m1);
151  long min2=std::min(n2,m2);
152 
153  lwork=std::max(lwork,std::max(3*min1+max1,5*min1));
154  lwork=std::max(lwork,std::max(3*min2+max2,5*min2));
155  }
156  const int rmax=std::min(rmax1,rmax2);
157 
158  // these are max dimensions, so we can avoid frequent reallocation
159  Tensor<T> u(rmax1*rmax2);
160  Tensor<T> dummy;
161  Tensor< typename Tensor<T>::scalar_type > s(rmax);
162 
163  // the dimension of the remainder tensor; will be cut down in each iteration
164  long vtdim=t.size();
165 
166  // c will be destroyed, and assignment is only shallow, so need to deep copy
167  Tensor<T> c=copy(t);
168 
169  // work array for dgesvd
170  Tensor<T> work(lwork);
171 
172 
173  // this keeps track of the ranks
174  std::vector<long> r(dims.size()+1,0l);
175  r[0] = r[dims.size()] = 1;
176 
177  for (std::size_t d=1; d<dims.size(); ++d) {
178 
179  // the core tensors will have dimensions c(rank_left,k,rank_right)
180  // or for lapack's purposes c(d1,rank_right)
181  const long k=dims[d-1];
182  const long d1=r[d-1]*k;
183  c=c.reshape(d1,c.size()/d1);
184  const long rmax=std::min(c.dim(0),c.dim(1));
185  vtdim=vtdim/k;
186 
187  // testing
188 #if 0
189  // c will be destroyed upon return
190  Tensor<T> aa=copy(c);
191 #endif
192  // The svd routine assumes lda=a etc. Pass in a flat tensor and reshape
193  // and slice it after processing.
194  u=u.flat();
195  svd_result(c,u,s,dummy,work);
196 
197  // this is rank_right
198  r[d]=SRConf<T>::max_sigma(eps,rmax,s)+1;
199  const long rank=r[d];
200 
201  // this is for testing
202 #if 0
203  if (0) {
204  Tensor<T> uu=(u(Slice(0,c.dim(0)*rmax-1))).reshape(c.dim(0),rmax);
205  Tensor<T> vtt=copy(c(Slice(0,rmax-1),_));
206  Tensor<T> b(d1,vtdim);
207  for (long i=0; i<c.dim(0); ++i)
208  for (long j=0; j<c.dim(1); ++j)
209  for (long k=0; k<rmax; ++k)
210  b(i,j) += uu(i,k) * T(s(k)) * vtt(k,j);
211  b -= aa;
212  print("b.conforms c",b.conforms(c));
213  print("early error:",b.absmax());
214  }
215 #endif
216 
217  // U = Tensor<T>(m,rmax);
218  // VT = Tensor<T>(rmax,n);
219 
220  // handle rank=0 explicitly
221  if (r[d]) {
222 
223  // done with this dimension -- slice and deep-copy
224  core[d-1]=copy((u(Slice(0,c.dim(0)*rmax-1)))
225  .reshape(c.dim(0),rmax)(_,Slice(0,rank-1)));
226  core[d-1]=core[d-1].reshape(r[d-1],k,r[d]);
227 
228  // continue with the next dimension
229  c=c(Slice(0,rank-1),_);
230 
231  for (int i=0; i<rank; ++i) {
232  for (int j=0; j<c.dim(1); ++j) {
233  c(i,j)*=s(i);
234  }
235  }
236 
237  if (d == dims.size()-1) core[d]=c;
238  }
239  else {
240  zero_rank = true;
241  core[d-1] = Tensor<T>(r[d-1],k,long(0));
242  // iterate through the rest -- fast forward
243  for(++d; d<dims.size(); ++d) {
244  const long k=dims[d-1];
245  core[d-1] = Tensor<T>(long(0),k,long(0));
246  }
247  core[dims.size()-1] = Tensor<T>(long(0),dims[dims.size()-1]);
248  }
249  }
250  core[0]=core[0].fusedim(0);
251 
252 
253  }
254 
256 
261 
262  // make sure dimensions conform
263  MADNESS_ASSERT(this->ndim()==rhs.ndim());
264 
265  if (this->zero_rank) {
266  *this=rhs;
267  } else if (rhs.zero_rank) {
268  ;
269  } else {
270 
271  // special treatment for first border cores (k,r1)
272  {
273  long k=core[0].dim(0);
274  long r1_this=core[0].dim(1);
275  long r1_rhs=rhs.core[0].dim(1);
276  Tensor<T> core_new(k,r1_this+r1_rhs);
277  core_new(_,Slice(0,r1_this-1))=core[0];
278  core_new(_,Slice(r1_this,r1_this+r1_rhs-1))=rhs.core[0];
279  core[0]=core_new;
280  }
281 
282  // interior cores (r0,k,r1)
283  for (std::size_t i=1; i<core.size()-1; ++i) {
284  MADNESS_ASSERT(core[i].ndim()==3 or i==core.size()-1);
285  long r0_this=core[i].dim(0);
286  long r0_rhs=rhs.core[i].dim(0);
287  long k=core[i].dim(1);
288  long r1_this=core[i].dim(2);
289  long r1_rhs=rhs.core[i].dim(2);
290  Tensor<T> core_new(r0_this+r0_rhs,k,r1_this+r1_rhs);
291  core_new(Slice(0,r0_this-1),_,Slice(0,r1_this-1))=core[i];
292  core_new(Slice(r0_this,r0_this+r0_rhs-1),_,Slice(r1_this,r1_this+r1_rhs-1))=rhs.core[i];
293  core[i]=core_new;
294  }
295 
296  // special treatment for last border core (r0,k)
297  {
298  std::size_t d=core.size()-1;
299  long r0_this=core[d].dim(0);
300  long r0_rhs=rhs.core[d].dim(0);
301  long k=core[d].dim(1);
302  Tensor<T> core_new(r0_this+r0_rhs,k);
303  core_new(Slice(0,r0_this-1),_)=core[d];
304  core_new(Slice(r0_this,r0_this+r0_rhs-1),_)=rhs.core[d];
305  core[d]=core_new;
306  }
307 
308  }
309 
310 
311  return *this;
312  }
313 
315 
318  void fusedim(const long i) {
319  // core_new = left * right
320  // (r1, k1*k2, r3) = sum_r2 (r1, k1, r2) * (r2, k2, r3)
321 
322  // determine index
323  const int index=core[i].ndim()-2; // (r-1, k, k, .. , k, r1)
324 
325  if (not zero_rank) core[i]=inner(core[i],core[i+1]);
326  core[i]=core[i].fusedim(index);
327 
328  // shift all subsequent cores and remove the last one
329  for (std::size_t d=i+1; d<core.size()-1; ++d) core[d]=core[d+1];
330  core.pop_back();
331 
332  }
333 
334 
336 
339  Tensor<T> reconstruct(const bool flat=false) const {
340 
341  if (zero_rank) {
342  if (flat) {
343  long size=1;
344  for (int i=1; i<this->ndim(); ++i) size*=core[i].dim(1);
345  return Tensor<T>(size);
346  } else {
347  std::vector<long> d(this->ndim());
348  d[0]=core[0].dim(0); // first core tensor has shape (k,r1)
349  for (int i=1; i<this->ndim(); ++i) d[i]=core[i].dim(1);
350  return Tensor<T>(d);
351  }
352  }
353 
354  Tensor<T> result=core.front();
355  typename std::vector<Tensor<T> >::const_iterator it;
356 
357  for (it=++core.begin(); it!=core.end(); ++it) {
358  result=inner(result,*it);
359  if (flat) result=result.fusedim(0);
360  }
361  return result;
362  }
363 
365 
369  void two_mode_representation(Tensor<T>& U, Tensor<T>& VT,
370  Tensor< typename Tensor<T>::scalar_type >& s) {
371 
372  // number of dimensions needs to be even
373  MADNESS_ASSERT(ndim()%2==0);
374 
375  if (not zero_rank) {
376  typename std::vector<Tensor<T> >::const_iterator it1, it2;
377  U=core.front();
378  VT=core.back();
379  for (it1=++core.begin(), it2=--(--core.end()); it1<it2; ++it1, --it2) {
380  U=inner(U,*it1);
381  VT=inner(*it2,VT);
382  }
383  s=Tensor< typename Tensor<T>::scalar_type >(VT.dim(0));
384  s=1.0;
385  }
386  else {
387  long dim1 = core.front().dim(0);
388  long dim2 = core.back().dim(1);
389  for (int d1=1, d2=core.size()-2; d1<d2; ++d1, --d2) {
390  dim1 *= core[d1].dim(1);
391  dim2 *= core[d2].dim(1);
392  }
393  U = Tensor<T>(dim1,long(0));
394  VT = Tensor<T>(long(0),dim2);
395  s = Tensor< typename Tensor<T>::scalar_type >(VT.dim(0));
396  }
397  }
398 
400 
403  void truncate(double eps) {
404  eps=eps/sqrt(this->ndim());
405 
406  // right-to-left orthogonalization (line 4)
407  Tensor<T> R;
408  long dims[TENSOR_MAXDIM];
409  for (std::size_t d=core.size()-1; d>0; --d) {
410 
411  // save tensor structure
412  const long ndim=core[d].ndim();
413  for (int i=0; i<ndim; ++i) dims[i]=core[d].dim(i);
414 
415  // G(r0, k*r1)
416  const long r0=core[d].dim(0);
417  core[d]=core[d].reshape(r0,core[d].size()/r0);
418 
419  // decompose the core tensor (line 5)
420  lq(core[d],R);
421  core[d]=core[d].reshape(ndim,dims);
422 
423  // multiply to the left (line 6)
424  core[d-1]=inner(core[d-1],R);
425  }
426 
427  // left-to-right SVD (line 9)
428  for (std::size_t d=0; d<core.size()-1; ++d) {
429 
430  // save tensor structure
431  const long ndim=core[d].ndim();
432  for (int i=0; i<ndim; ++i) dims[i]=core[d].dim(i);
433 
434  // reshape the core tensor (r0*k, r1)
435  long r1=core[d].dim(core[d].ndim()-1);
436  core[d]=core[d].reshape(core[d].size()/r1,r1);
437 
438  Tensor<T> U,VT;
439  long rmax=std::min(core[d].dim(0),core[d].dim(1));
440  Tensor< typename Tensor<T>::scalar_type > s(rmax);
441 
442  // decompose (line 10)
443  svd(core[d],U,s,VT);
444 
445  // truncate the SVD
446  int r_truncate=SRConf<T>::max_sigma(eps,rmax,s)+1;
447  U=copy(U(_,Slice(0,r_truncate-1)));
448  VT=copy(VT(Slice(0,r_truncate-1),_));
449 
450  dims[ndim-1]=r_truncate;
451  core[d]=U.reshape(ndim,dims);
452 
453 
454  for (int i=0; i<VT.dim(0); ++i) {
455  for (int j=0; j<VT.dim(1); ++j) {
456  VT(i,j)*=s(j);
457  }
458  }
459 
460  // multiply to the right (line 11)
461  core[d+1]=inner(VT,core[d+1]);
462 
463  }
464 
465  }
466 
468  long ndim() const {return core.size();}
469 
471  long size() const {
472  if (zero_rank) return 0;
473  long n=0;
474  typename std::vector<Tensor<T> >::const_iterator it;
475  for (it=core.begin(); it!=core.end(); ++it) n+=it->size();
476  return n;
477  }
478 
480  long real_size() const {
481  long n=this->size()*sizeof(T);
482  n+=core.size() * sizeof(Tensor<T>);
483  n+=sizeof(*this);
484  return n;
485  }
486 
488  long dim(const int i) const {
489  if (i==0) return core[0].dim(0);
490  return core[i].dim(1);
491  }
492 
494  bool is_zero_rank() const {return zero_rank;}
495 
497  std::vector<long> ranks() const {
498  if (zero_rank) return std::vector<long>(0,core.size()-1);
499  std::vector<long> r(core.size()-1);
500  for (std::size_t i=0; i<r.size(); ++i) r[i]=core[i+1].dim(0);
501  return r;
502  }
503 
504  };
505 
506 
507 }
508 
509 #endif /* TENSORTRAIN_H_ */
std::vector< long > ranks() const
return the TT ranks
Definition: tensortrain.h:497
long dim(const int i) const
return the number of entries in dimension i
Definition: tensortrain.h:488
Corresponding C and Fortran types.
C++ prototypes for Fortran LAPACK with associated typedefs and macos.
const double R
Definition: dielectric.cc:191
void two_mode_representation(Tensor< T > &U, Tensor< T > &VT, Tensor< typename Tensor< T >::scalar_type > &s)
construct a two-mode representation (aka unnormalized SVD)
Definition: tensortrain.h:369
void lq(Tensor< T > &A, Tensor< T > &R)
compute the LQ decomposition of the matrix A = L Q
Definition: lapack.cc:630
long real_size() const
return the size of this instance, including static memory for vectors and such
Definition: tensortrain.h:480
T inner(const vecfunc< T, NDIM > &a, const vecfunc< T, NDIM > &b)
the non-linear solver requires an inner product
Definition: nemo.h:112
TensorTrain(const Tensor< T > &t, double eps)
ctor for a TensorTrain, with the tolerance eps
Definition: tensortrain.h:90
Tensor< T > reconstruct(const bool flat=false) const
reconstruct this to a full representation
Definition: tensortrain.h:339
Defines and implements most of Tensor.
static int max_sigma(const double &thresh, const int &rank, const Tensor< double > &w)
Definition: srconf.h:125
long ndim() const
return the number of dimensions
Definition: tensortrain.h:468
const T1 &f1 return GTEST_2_TUPLE_() T(f0, f1)
#define max(a, b)
Definition: lda.h:53
const int k
Definition: dielectric.cc:184
Function< T, NDIM > copy(const Function< T, NDIM > &f, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool fence=true)
Create a new copy of the function with different distribution and optional fence. ...
Definition: mra.h:1835
const mpreal min(const mpreal &x, const mpreal &y)
Definition: mpreal.h:2675
Prototypes for a partial interface from Tensor to LAPACK.
void truncate(double eps)
recompress and truncate this TT representation
Definition: tensortrain.h:403
#define TENSOR_MAXDIM
Definition: tensor_macros.h:194
bool is_zero_rank() const
if rank is zero
Definition: tensortrain.h:494
void svd(const Tensor< T > &a, Tensor< T > &U, Tensor< typename Tensor< T >::scalar_type > &s, Tensor< T > &VT)
Compute the singluar value decomposition of an n-by-m matrix using *gesvd.
Definition: lapack.cc:273
tensorT sqrt(const tensorT &s, double tol=1e-8)
Computes matrix square root (not used any more?)
Definition: DFcode/moldft.cc:446
void decompose(const Tensor< T > &t, double eps, const std::vector< long > &dims)
decompose the input tensor into a TT representation
Definition: tensortrain.h:127
TensorTrain< T > & operator+=(const TensorTrain< T > &rhs)
inplace addition of two Tensortrains; will increase ranks of this
Definition: tensortrain.h:260
Definition: tensortrain.h:69
void svd_result(Tensor< T > &a, Tensor< T > &U, Tensor< typename Tensor< T >::scalar_type > &s, Tensor< T > &VT, Tensor< T > &work)
same as svd, but it optimizes away the tensor construction: a = U * diag(s) * VT
Definition: lapack.cc:308
void print(const A &a)
Print a single item to std::cout terminating with new line.
Definition: print.h:122
A slice defines a sub-range or patch of a dimension.
Definition: slice.h:103
void fusedim(const long i)
merge two dimensions into one
Definition: tensortrain.h:318
Holds machinery to set up Functions/FuncImpls using various Factories and Interfaces.
Definition: chem/atomutil.cc:45
const double c
Definition: gfit.cc:200
FLOAT b(int j, FLOAT z)
Definition: y1.cc:79
handles the low-level details of a separated representation tensor
long size() const
return the number of coefficients in all core tensors
Definition: tensortrain.h:471
TensorTrain(const Tensor< T > &t, double eps, const std::vector< long > dims)
ctor for a TensorTrain, with the tolerance eps
Definition: tensortrain.h:114