MADNESS 0.10.1
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
43
44/**
45 \file tensortrain.h
46 \brief Defines and implements the tensor train decomposition as described in
47 I.V. Oseledets, Siam J. Sci. Comput. 33, 2295 (2011).
48 \ingroup tensor
49 \addtogroup tensor
50
51*/
52
53namespace madness {
54
55 /// decompose the input tensor A into U and V, skipping singular vectors of
56 /// small singular values. One deep copy/allocation for U is performed
57
58 /// @param[inout] A the input matrix, on exit the matrix VT (right sing. vectors)
59 /// @param[out] U contiguous new tensor holding the left sing. vectors
60 /// @param[in] thresh threshold for truncation of the singular vectors
61 /// @param[in] s scratch tensor for the singular values, dimension min(n,m)
62 /// @param[in] scr scratch tensor
63 /// the dimension of the scratch tensor may be computed as
64 /// long lwork=std::max(3*std::min(m,n)+std::max(m,n),5*std::min(m,n)) + n*m;
65 template<typename T>
67 const double thresh, Tensor< typename Tensor<T>::scalar_type > & s,
68 Tensor<T>& scr) {
69
70 MADNESS_ASSERT(A.ndim()==2); // must be a matrix
71 const long n=A.dim(0);
72 const long m=A.dim(1);
73 const long rmax=std::min(n,m);
74 long lwork=std::max(3*std::min(m,n)+std::max(m,n),5*std::min(m,n));
75
76 // set up contiguous scratch arrays
77 MADNESS_ASSERT(scr.size()>lwork+n*m);
78 scr=scr.flat();
79 Tensor<T> work=scr(Slice(0,lwork-1));
80 Tensor<T> utmp=scr(Slice(lwork,lwork+n*m-1));
81 Tensor<T> dummy; // real dummy
82
83 svd_result(A,utmp,s,dummy,work);
84
85 // this is rank_right
86 const long R1=SRConf<T>::max_sigma(thresh,rmax,s)+1;
87
88 // skip if rank=0
89 if (R1>0) {
90
91 U=madness::copy((utmp(Slice(0,n*rmax-1)))
92 .reshape(n,rmax)(_,Slice(0,R1-1)));
93
94 A=A(Slice(0,R1-1),_);
95
96 // continue with the next dimension
97 for (int j=0; j<m; ++j) {
98 for (int i=0; i<R1; ++i) {
99 A(i,j)*=s(i);
100 }
101 }
102 } else {
103 U=Tensor<T>(n,0l);
104 }
105 return R1;
106 }
107
108
109
110 /**
111 * A tensor train is a multi-modal representation of a tensor t
112 * \code
113 * t(i,j,k,l) = \sum G^1_{a1,i,a2} G^2_{a2,j,a3} G^3_{a3,k,a4} G^4_{a4,l,a5}
114 * \endcode
115 * The "core" tensors G are connected via a linear index network, where the
116 * first index a1 and the last index a5 are boundary indices and are set to 1.
117 *
118 * The tensor train representation is suited for any number of dimensions and
119 * in general at least as fast as the 2-way decomposition SVD. If the tensor
120 * has full rank it will need about twice the storage space of the full tensor
121 */
122 template<typename T>
123 class TensorTrain : public BaseTensor {
124
125 // make all types of TensorTrain friends of each other (for type conversion)
126 template<typename Q>
127 friend class TensorTrain;
128
129 /// C++ typename of this tensor.
130 typedef T type;
131
132 /// C++ typename of the real type associated with a complex type.
134
135 /// C++ typename of the floating point type associated with scalar real type
137
138 /// holding the core tensors of a tensor train
139 /// the tensors have the shape (k,r0) (r0,k,r1) (r1,k,r2) .. (rn-1,k)
140 std::vector<Tensor<T> > core;
141 /// true if rank is zero
143
144 public:
145
146 /// empty constructor
149 }
150
151 /// ctor for a TensorTrain, with the tolerance eps
152
153 /// The tensor train will represent the input tensor with
154 /// accuracy || t - this ||_2 < eps
155 ///
156 /// Note that we rely on specific layout of the memory in the tensors, e.g.
157 /// we pass SliceTensors on to lapack. This will only work if the slices are
158 /// contiguous.
159 ///
160 /// @param[in] t full representation of a tensor
161 /// @param[in] eps the accuracy threshold
162 TensorTrain(const Tensor<T>& t, double eps)
163 : core(), zero_rank(false) {
165 if (t.size()==0) return;
166
167 MADNESS_ASSERT(t.size() != 0);
168 MADNESS_ASSERT(t.ndim() != 0);
169
170 std::vector<long> dims(t.ndim());
171 for (int d=0; d<t.ndim(); ++d) dims[d]=t.dim(d);
172 decompose(t.flat(),eps,dims);
173
174 }
175
176 /// ctor for a TensorTrain, with the tolerance eps
177
178 /// The tensor train will represent the input tensor with
179 /// accuracy || t - this ||_2 < eps
180 ///
181 /// Note that we rely on specific layout of the memory in the tensors, e.g.
182 /// we pass SliceTensors on to lapack. This will only work if the slices are
183 /// contiguous.
184 ///
185 /// @param[in] t full representation of a tensor
186 /// @param[in] eps the accuracy threshold
187 /// @param[in] dims the tt structure
188 TensorTrain(const Tensor<T>& t, double eps, const std::vector<long> dims)
189 : core(), zero_rank(false) {
191
192 MADNESS_ASSERT(t.size() != 0);
193 MADNESS_ASSERT(t.ndim() != 0);
194 decompose(t,eps,dims);
195 }
196
197 /// ctor for a TensorTrain, set up only the dimensions, no data
198 TensorTrain(const long& ndims, const long* dims) {
199 zero_rank = true;
200 set_dims_and_size(ndims,dims);
201 }
202
203 /// ctor for a TensorTrain, set up only the dimensions, no data
204 TensorTrain(const std::vector<long>& dims) {
205 zero_rank = true;
207
208// core.resize(dims.size());
209// // first and last core tensor
210// core[0] = Tensor<T>(dims[0],long(0));
211// core[dims.size()-1] = Tensor<T>(long(0),dims[dims.size()-1]);
212//
213// // iterate through the rest -- fast forward
214// for (int d=1; d<dims.size()-1; ++d) {
215// core[d] = Tensor<T>(long(0),dims[d],long(0));
216// }
217
218 }
219
220
221 /// ctor for a TensorTrain, with core tensors explicitly given
222
223 /// the core tensors must have the shape (k1,r1) (r1,k2,r2) .. (r2,k3)
224 /// @param[in] core vector of core tensors, properly shaped
225 TensorTrain(const std::vector<Tensor<T> >& c) : core(c) {
226 zero_rank=false;
227 std::vector<long> d(c.size());
228 d[0]=core[0].dim(0); // dim,rank
229 for (long i=1; i<d.size(); ++i) d[i]=core[i].dim(1); // rank,dim,rank
231
232 // check for zero ranks
233 for (int d=1; d<core.size(); ++d) if (core[d].dim(0)==0) zero_me();
234 }
235
236
237 /// copy constructor, shallow
238 TensorTrain(const TensorTrain& other) : core(other.core),
239 zero_rank(other.zero_rank) {
240 set_dims_and_size(other.ndim(),other.dims());
241
242 }
243
244 /// assigment operator
246 if (this!=&other) {
247 set_dims_and_size(other.ndim(),other.dims());
248 zero_rank=other.zero_rank;
249 core=other.core;
250 }
251 return *this;
252 }
253
254 /// Type conversion makes a deep copy
255 template <class Q> operator TensorTrain<Q>() const { // type conv => deep copy
256
257 TensorTrain<Q> result(this->dims());
258 result.zero_rank=zero_rank;
259 for (const Tensor<T>& c : core) {
260 result.core.push_back(Tensor<Q>(c));
261 }
262 return result;
263 }
264
265 void set_size_and_dim(std::vector<long> dims) {
267 }
268
269 /// serialize this
270 template <typename Archive>
271 void serialize(Archive& ar) {
272
273 // serialize basetensor
276
277 long core_size=core.size();
278 ar & zero_rank & core_size;
279
280 // no empty tensor
281 if (core_size>0) ar & core;
282 }
283
284
285 /// deep copy of the whole tensor
286
287 /// if argument has zero rank return a zero-rank tensor of the same dimensions
288 friend TensorTrain copy(const TensorTrain& other) {
289
290 // fast return
291 if (other.zero_rank) return TensorTrain(other.ndim(),other.dims());
292
293 TensorTrain result(other.ndim(),other.dims());
294 for (const Tensor<T>& t: other.core) {
295 if (t.size()==0) return TensorTrain(other.ndim(),other.dims()); // also zero
296 result.core.push_back(madness::copy(t));
297 }
298 result.zero_rank=other.zero_rank;
299 return result;
300 }
301
302 /// deep copy of a slice of the tensor
303
304 /// this operation does not change the ranks, i.e. the resulting
305 /// tensor is most likely not in an optimal compression state
306 /// @param[in] other tensor to be sliced
307 /// @param[in] s vector of slices
308 friend TensorTrain copy(const TensorTrain& other, const std::array<Slice,TENSOR_MAXDIM>& s) {
309
310 std::vector<long> dims(s.size());
311 for (int i=0; i<dims.size(); ++i) dims[i]=s[i].end-s[i].start+1;
312
313 TensorTrain result(other.ndim(),&dims.front());
314 if (other.zero_rank) return result;
315 const long nd=other.ndim();
316 result.zero_rank=other.zero_rank;
317 result.core.resize(nd);
318
319 // special treatment for first and last core tensor
320 // slice dim only, keep ranks
321 result.core[0]=copy(other.core[0](s[0],_));
322 for (long i=1; i<nd-1; ++i) {
323 result.core[i]=copy(other.core[i](_,s[i],_));
324 }
325
326 if (other.core.size()>1) result.core[nd-1]=copy(other.core[nd-1](_,s[nd-1]));
327 return result;
328 }
329
330 /// decompose the input tensor into a TT representation
331
332 /// @param[in] t tensor in full rank
333 /// @param[in] eps the precision threshold
334 /// @param[in] dims the tt structure
335 void decompose(const Tensor<T>& t, double eps,
336 const std::vector<long>& dims) {
337
338 core.resize(dims.size());
339 eps=eps/sqrt(dims.size()-1); // error is relative
340
341 // the maximum rank of the SVD decompositions
342 long Udim0=1;
343 long rmax=1;
344 for (long i=0; i<dims.size(); ++i) {
345 Udim0*=dims[i];
346 rmax=std::max(rmax,std::min(Udim0,t.size()/Udim0));
347 }
348
349 long rmax1=1;
350 long rmax2=1;
351 long n1=1, n2=1;
352 long lwork=0;
353 for (int i=0, j=dims.size()-1; i<=j; ++i, --j) {
354 rmax1*=dims[i];
355 rmax2*=dims[j];
356
357 // work array for dgesvd
358 n1*=dims[i];
359 long m1=t.size()/dims[i];
360 n2*=dims[j];
361 long m2=t.size()/dims[j];
362 long max1=std::max(n1,m1);
363 long max2=std::max(n2,m2);
364 long min1=std::min(n1,m1);
365 long min2=std::min(n2,m2);
366
367 lwork=std::max(lwork,std::max(3*min1+max1,5*min1));
368 lwork=std::max(lwork,std::max(3*min2+max2,5*min2));
369 }
370
371 // these are max dimensions, so we can avoid frequent reallocation
372 Tensor<T> u(rmax1*rmax2);
373 Tensor<T> dummy;
375
376 // the dimension of the remainder tensor; will be cut down in each iteration
377 long vtdim=t.size();
378
379 // c will be destroyed, and assignment is only shallow, so need to deep copy
381
382 // work array for dgesvd
383 Tensor<T> work(lwork);
384
385
386 // this keeps track of the ranks
387 std::vector<long> r(dims.size()+1,0l);
388 r[0] = r[dims.size()] = 1;
389
390 for (std::size_t d=1; d<dims.size(); ++d) {
391
392 // the core tensors will have dimensions c(rank_left,k,rank_right)
393 // or for lapack's purposes c(d1,rank_right)
394 const long k=dims[d-1];
395 const long d1=r[d-1]*k;
396 c=c.reshape(d1,c.size()/d1);
397 const long rmax=std::min(c.dim(0),c.dim(1));
398 vtdim=vtdim/k;
399
400 // testing
401#if 0
402 // c will be destroyed upon return
404#endif
405 // The svd routine assumes lda=a etc. Pass in a flat tensor and reshape
406 // and slice it after processing.
407 u=u.flat();
408 svd_result(c,u,s,dummy,work);
409
410 // this is rank_right
411 r[d]=SRConf<T>::max_sigma(eps,rmax,s)+1;
412 const long rank=r[d];
413
414 // this is for testing
415#if 0
416 if (0) {
417 Tensor<T> uu=(u(Slice(0,c.dim(0)*rmax-1))).reshape(c.dim(0),rmax);
418 Tensor<T> vtt=copy(c(Slice(0,rmax-1),_));
419 Tensor<T> b(d1,vtdim);
420 for (long i=0; i<c.dim(0); ++i)
421 for (long j=0; j<c.dim(1); ++j)
422 for (long k=0; k<rmax; ++k)
423 b(i,j) += uu(i,k) * T(s(k)) * vtt(k,j);
424 b -= aa;
425 print("b.conforms c",b.conforms(c));
426 print("early error:",b.absmax());
427 }
428#endif
429
430 // U = Tensor<T>(m,rmax);
431 // VT = Tensor<T>(rmax,n);
432
433 // handle rank=0 explicitly
434 if (r[d]) {
435
436 // done with this dimension -- slice and deep-copy
437 core[d-1]=madness::copy((u(Slice(0,c.dim(0)*rmax-1)))
438 .reshape(c.dim(0),rmax)(_,Slice(0,rank-1)));
439 core[d-1]=core[d-1].reshape(r[d-1],k,r[d]);
440
441 // continue with the next dimension
442 c=c(Slice(0,rank-1),_);
443
444 for (int i=0; i<rank; ++i) {
445 for (int j=0; j<c.dim(1); ++j) {
446 c(i,j)*=s(i);
447 }
448 }
449
450 if (d == dims.size()-1) core[d]=c;
451 }
452 else {
453 zero_rank = true;
454 core[d-1] = Tensor<T>(r[d-1],k,long(0));
455 // iterate through the rest -- fast forward
456 for(++d; d<dims.size(); ++d) {
457 const long k=dims[d-1];
458 core[d-1] = Tensor<T>(long(0),k,long(0));
459 }
460 core[dims.size()-1] = Tensor<T>(long(0),dims[dims.size()-1]);
461 }
462 }
463 core[0]=core[0].fusedim(0);
464
465
466 }
467
468
469 /// turn this into an empty tensor with all cores properly shaped
470 void zero_me() {
471 *this=TensorTrain<T>(this->ndim(),this->dims());
472 }
473
474 /// turn this into an empty tensor with all cores properly shaped
475 void zero_me(const std::vector<long>& dim) {
476 *this=TensorTrain<T>(dim);
477 }
478
479 /// assign a number to this tensor
480 TensorTrain<T>& operator=(const T& number) {
481
482 // tensor will have ranks = 1 all over
483 core[0]=Tensor<T>(dim(0),1);
484 for (int i=1; i<ndim()-1; ++i) core[i]=Tensor<T>(1,dim(i),1);
485 if (ndim()>1) core[ndim()-1]=Tensor<T>(1,dim(ndim()-1));
486
487 core[0]=number;
488 for (int i=1; i<ndim(); ++i) core[i]=1.0;
489
490 zero_rank=false;
491 return *this;
492 }
493
494 /// return this multiplied by a scalar
495
496 /// @return new tensor
497 TensorTrain<T> operator*(const T& factor) const {
498 TensorTrain result=copy(*this);
499 result.scale(factor);
500 return result;
501 }
502
503 /// inplace addition of two Tensortrains; will increase ranks of this
504
505 /// inefficient if many additions are performed, since it requires
506 /// many calls of new.
507 /// @param[in] rhs a TensorTrain to be added
509 gaxpy(1.0,rhs,1.0);
510 return *this;
511 }
512
513 /// inplace subtraction of two Tensortrains; will increase ranks of this
514
515 /// inefficient if many subtractions are performed, since it requires
516 /// many calls of new.
517 /// @param[in] rhs a TensorTrain to be added
519 gaxpy(1.0,rhs,-1.0);
520 return *this;
521 }
522
523 /// Inplace generalized saxpy ... this = this*alpha + other*beta
525
526 // make sure dimensions conform
527 MADNESS_ASSERT(this->ndim()==rhs.ndim());
528
529 if (this->zero_rank or (alpha==0.0)) {
530 *this=rhs*beta;
531 } else if (rhs.zero_rank or (beta==0.0)) {
532 scale(alpha);
533 } else if (ndim()==1) { // simple algorithm for ndim=1
534 core[0].gaxpy(alpha,rhs.core[0],beta);
535 } else {
536
537 // special treatment for first border cores (k,r1)
538
539 // alpha and beta are only included in the first core(!)
540 {
541 long k=core[0].dim(0);
542 long r1_this=core[0].dim(1);
543 long r1_rhs=rhs.core[0].dim(1);
544 Tensor<T> core_new(k,r1_this+r1_rhs);
545 core_new(_,Slice(0,r1_this-1))=alpha*core[0];
546 core_new(_,Slice(r1_this,r1_this+r1_rhs-1))=beta*rhs.core[0];
547 core[0]=core_new;
548 }
549
550 // interior cores (r0,k,r1)
551 for (std::size_t i=1; i<core.size()-1; ++i) {
552 MADNESS_ASSERT(core[i].ndim()==3 or i==core.size()-1);
553 long r0_this=core[i].dim(0);
554 long r0_rhs=rhs.core[i].dim(0);
555 long k=core[i].dim(1);
556 long r1_this=core[i].dim(2);
557 long r1_rhs=rhs.core[i].dim(2);
558 Tensor<T> core_new(r0_this+r0_rhs,k,r1_this+r1_rhs);
559 core_new(Slice(0,r0_this-1),_,Slice(0,r1_this-1))=core[i];
560 core_new(Slice(r0_this,r0_this+r0_rhs-1),_,Slice(r1_this,r1_this+r1_rhs-1))=rhs.core[i];
561 core[i]=core_new;
562 }
563
564 // special treatment for last border core (r0,k)
565 {
566 std::size_t d=core.size()-1;
567 long r0_this=core[d].dim(0);
568 long r0_rhs=rhs.core[d].dim(0);
569 long k=core[d].dim(1);
570 Tensor<T> core_new(r0_this+r0_rhs,k);
571 core_new(Slice(0,r0_this-1),_)=core[d];
572 core_new(Slice(r0_this,r0_this+r0_rhs-1),_)=rhs.core[d];
573 core[d]=core_new;
574 }
575 }
576 if (not verify()) MADNESS_EXCEPTION("ranks in TensorTrain inconsistent",1);
577 return *this;
578 }
579
580 /// Inplace generalized saxpy with slices and without alpha
581
582 /// return this = this(s1) + other(s2) * beta
583 TensorTrain<T>& gaxpy(const std::array<Slice,TENSOR_MAXDIM>& s1,
584 const TensorTrain<T>& rhs, T beta, const std::array<Slice,TENSOR_MAXDIM>& s2) {
585
586 // make sure dimensions conform
587 MADNESS_ASSERT(this->ndim()==rhs.ndim());
588
589 if (rhs.zero_rank or (beta==0.0)) return *this;
590 if (this->zero_rank) {
591 core.resize(ndim(),Tensor<T>());
592 }
593 // special treatment for first border cores (k,r1)
594
595 // alpha and beta are only included in the first core(!)
596 {
597 long k=dim(0); // this is max dimension: k>=slice1; k>=slice2
598 long r1_this= zero_rank ? 0 : core[0].dim(1);
599 long r1_rhs=rhs.core[0].dim(1);
600
601 Tensor<T> core_new(k,r1_this+r1_rhs);
602 if (r1_this>0) core_new(_,Slice(0,r1_this-1))=core[0];
603 if (r1_rhs>0) core_new(s1[0],Slice(r1_this,r1_this+r1_rhs-1))=beta*rhs.core[0](s2[0],_);
604 core[0]=core_new;
605 }
606
607 // interior cores (r0,k,r1)
608 for (std::size_t i=1; i<core.size()-1; ++i) {
609 MADNESS_ASSERT(zero_rank or core[i].ndim()==3 or i==core.size()-1);
610 long r0_this= zero_rank ? 0 : core[i].dim(0);
611 long r0_rhs=rhs.core[i].dim(0);
612 long k=dim(i);
613 long r1_this=zero_rank ? 0 : core[i].dim(2);
614 long r1_rhs=rhs.core[i].dim(2);
615 Tensor<T> core_new(r0_this+r0_rhs,k,r1_this+r1_rhs);
616 if (r1_this>0) core_new(Slice(0,r0_this-1),_,Slice(0,r1_this-1))=core[i];
617 if (r1_rhs>0) core_new(Slice(r0_this,r0_this+r0_rhs-1),s1[i],Slice(r1_this,r1_this+r1_rhs-1))=rhs.core[i](_,s2[i],_);
618 core[i]=core_new;
619 }
620
621 // special treatment for last border core (r0,k)
622 {
623 std::size_t d=core.size()-1;
624 long r0_this= zero_rank ? 0 : core[d].dim(0);
625 long r0_rhs=rhs.core[d].dim(0);
626 long k=dim(d);
627 Tensor<T> core_new(r0_this+r0_rhs,k);
628 if (r0_this>0) core_new(Slice(0,r0_this-1),_)=core[d];
629 if (r0_rhs>0) core_new(Slice(r0_this,r0_this+r0_rhs-1),s1[d])=rhs.core[d](_,s2[d]);
630 core[d]=core_new;
631 }
632 if (not rhs.zero_rank) zero_rank=false;
633 if (not verify()) MADNESS_EXCEPTION("ranks in TensorTrain inconsistent",1);
634 return *this;
635 }
636
637 /// compute the Hadamard product of two TensorTrains
638
639 /// see Sec. 4.2 of the TT paper
640 /// @return *this with the ranks of the input tensors multiplied!
642
643 // consistency checks
644 MADNESS_ASSERT(ndim()==other.ndim());
645 for (int i=0; i<ndim(); ++i) MADNESS_ASSERT(dim(i)==other.dim(i));
646
647 // set up the result tensor
648 std::vector<long> cranks(ndim()-1); // ranks of result C
649 for (int i=0; i<ndim()-1; ++i) cranks[i]=ranks(i)*other.ranks(i);
650
651 // check for large sizes
652 for (int i=0; i<ndim()-2; ++i) {
653 long size=cranks[i]*cranks[i+1]*dim(i);
654 if (size>10000000)
655 MADNESS_EXCEPTION("emul in TensorTrain too large -- use full rank tenspr",1);
656 }
657 TensorTrain result(ndim(),dims());
658 result.core.resize(ndim());
659
660 // fast return for zero ranks
661 if (zero_rank or other.zero_rank) {
662 ;
663 } else if (ndim()==1) {
664 // fast return for one core only (all ranks equal 1)
665 result.core[0]=copy(core[0]);
666 result.core[0].emul(other.core[0]);
667
668 } else {
669 result.zero_rank=false;
670 // compute outer product for each core for each k
671
672 // special treatment for first core
673 result.core[0]=Tensor<T>(dim(0),cranks[0]);
674 for (int k=0; k<dim(0); ++k) {
675 Tensor<T> a1=core[0](Slice(k,k),_);
676 Tensor<T> b1=other.core[0](Slice(k,k),_);
677 result.core[0](Slice(k,k),_)=outer(a1,b1).reshape(1,cranks[0]);
678 // before reshape rhs has dimensions (1, r1, 1, r1')
679 // after reshape rhs has dimensions (1, r1*r1')
680 }
681
682 for (int i=1; i<ndim()-1; ++i) {
683 result.core[i]=Tensor<T>(cranks[i-1],dim(i),cranks[i]);
684 for (int k=0; k<dim(i); ++k) {
685 Tensor<T> a1=core[i](_,Slice(k,k),_).fusedim(1); // (r1,r2)
686 Tensor<T> b1=other.core[i](_,Slice(k,k),_).fusedim(1); // (r1',r2')
687 Tensor<T> tmp=copy(outer(a1,b1).swapdim(1,2)); // make contiguous
688 result.core[i](_,Slice(k,k),_)=tmp.reshape(cranks[i-1],1,cranks[i]);
689 // before swap/fuse/splitdim rhs has dimensions (r1, r2, r1', r2')
690 // after fusedim/splitdim rhs has dimensions (r1*r1', 1, r2*r2')
691 }
692 }
693
694 // special treatment for last core
695 const long n=ndim()-1;
696 result.core[n]=Tensor<T>(cranks[n-1],dim(n));
697 for (int k=0; k<dim(n); ++k) {
698 Tensor<T> a1=core[n](_,Slice(k,k));
699 Tensor<T> b1=other.core[n](_,Slice(k,k));
700 result.core[n](_,Slice(k,k))=outer(a1,b1).reshape(cranks[n-1],1);
701 // before reshape rhs has dimensions (r1,1, r1',1)
702 // after reshape rhs has dimensions (r1*r1', 1)
703
704 }
705 }
706 *this=result;
707 return *this;
708 }
709
710
711
712 /// merge two dimensions into one
713
714 /// merge dimension i and i+1 into new dimension i
715 /// @param[in] i the first dimension
716 void fusedim(const long i) {
717 // core_new = left * right
718 // (r1, k1*k2, r3) = sum_r2 (r1, k1, r2) * (r2, k2, r3)
719
721 // determine index
722 const int index=core[i].ndim()-2; // (r-1, k, k, .. , k, r1)
723
724 if (not zero_rank) {
725 core[i]=inner(core[i],core[i+1]);
726 core[i]=core[i].fusedim(index);
727 } else {
728 if (i==0) { // (k1*k2, r2=0)
729 core[i]=Tensor<T>(core[i].dim(0)*core[i+1].dim(1),0l);
730 } else { /// (r1=0, k1*k2, r2=0)
731 core[i]=Tensor<T>(0l,core[i].dim(0)*core[i+1].dim(1),0l);
732 }
733 }
734
735 // shift all subsequent cores and remove the last one
736 for (std::size_t d=i+1; d<core.size()-1; ++d) core[d]=core[d+1];
737 core.pop_back();
738
739 }
740
741 /// Returns new view/tensor splitting dimension \c i as \c dimi0*dimi1
742 /// to produce conforming d+1 dimension tensor
743
744 /// @param[in] idim the dimension to be split
745 /// @param[in] k1 new first dimension of idim
746 /// @param[in] k2 new second dimension of idim
747 /// @param[in] eps threshold for SVD (choose negative to keep all terms)
748 /// @return new deep copy of this with split dimensions
749 TensorTrain<T> splitdim(long idim, long k1, long k2, const double eps) const {
750 // core_new = left * right
751 // (r1, k1*k2, r3) = sum_r2 (r1, k1, r2) * (r2, k2, r3)
752
753 // check for consistency
754 MADNESS_ASSERT(k1*k2==dim(idim));
755
756 if (zero_rank) {
757 std::vector<long> newdims(this->ndim()+1);
758 for (long i=0; i<idim; ++i) newdims[i]=this->dim(i);
759 newdims[idim]=k1;
760 newdims[idim+1]=k2;
761 for (long i=idim+1; i<ndim(); ++i) newdims[i+1]=dim(i);
762 return TensorTrain(newdims);
763 }
764
765 TensorTrain<T> result(ndim(),dims());
766 result.splitdim_inplace(idim,k1,k2);
767
768 long r1= (idim==0) ? 1 : ranks(idim-1); // left-side rank
769 long r2= (idim==ndim()-1) ? 1 : ranks(idim); // right-side rank
770 long k12=dim(idim);
771
772 Tensor<T> A=core[idim].reshape(r1*k1,r2*k2);
773 Tensor<T> U,VT;
775 svd(A,U,s,VT);
776
777 // this is the new interior rank
778 long r=SRConf<T>::max_sigma(eps,std::min(A.dim(0),A.dim(1)),s)+1;
779
780 // handle rank=0 explicitly
781 if (r==0) {
782 std::vector<long> newdims(this->ndim()+1);
783 for (long i=0; i<idim; ++i) newdims[i]=this->dim(i);
784 newdims[idim]=k1;
785 newdims[idim+1]=k2;
786 for (long i=idim+1; i<ndim(); ++i) newdims[i+1]=dim(i);
787 return TensorTrain(newdims);
788 } else {
789
790 // convolve the singular values into the right singular vectors
791 for (int ii=0; ii<r; ++ii) {
792 for (int j=0; j<VT.dim(1); ++j) {
793 VT(ii,j)*=s(ii);
794 }
795 }
796
797 for (long ii=0; ii<idim; ++ii) result.core.push_back(copy(core[ii]));
798 result.core.push_back(copy(U(_,Slice(0,r-1))).reshape(r1,k1,r));
799 result.core.push_back(copy(VT(Slice(0,r-1),_)).reshape(r,k2,r2));
800 for (long ii=idim+1; ii<ndim(); ++ii) result.core.push_back(core[ii]);
801
802 // post-processing
803 if (result.core.front().ndim()==3) result.core.front()=result.core.front().fusedim(0);
804 if (result.core.back().ndim()==3) result.core.back()=result.core.back().fusedim(1);
805 result.zero_rank=false;
806 }
807 return result;
808
809 }
810
811 /// reconstruct this to a full representation
812
813 /// @param[in] flat return this in flat representation
814 /// @return this in full rank representation
815 Tensor<T> reconstruct(const bool flat=false) const {
816
817 if (zero_rank) {
818 if (flat) {
819 long size=1;
820 for (int i=1; i<this->ndim(); ++i) size*=core[i].dim(1);
821 return Tensor<T>(size);
822 } else {
824 return Tensor<T>(ndim(),dims());
825 }
826 }
827
828 Tensor<T> result=core.front();
829 typename std::vector<Tensor<T> >::const_iterator it;
830
831 for (it=++core.begin(); it!=core.end(); ++it) {
832 result=inner(result,*it);
833 if (flat) result=result.fusedim(0);
834 }
835 return result;
836 }
837
838 /// construct a two-mode representation (aka unnormalized SVD)
839
840 /// @param[out] U The left singular vectors, ({i},rank)
841 /// @param[out] VT The right singular vectors, (rank,{i})
842 /// @param[out] s Vector holding 1's, (rank)
844 Tensor< typename Tensor<T>::scalar_type >& s) const {
845
846 // number of dimensions needs to be even
847 MADNESS_ASSERT(ndim()%2==0);
848
849 if (not zero_rank) {
850 typename std::vector<Tensor<T> >::const_iterator it1, it2;
851 U=core.front();
852 VT=core.back();
853 for (it1=++core.begin(), it2=--(--core.end()); it1<it2; ++it1, --it2) {
854 U=inner(U,*it1);
855 VT=inner(*it2,VT);
856 }
858 s=1.0;
859 }
860 else {
861 if (ndim()==2) {
862 U = Tensor<T>(dim(0),long(0));
863 VT = Tensor<T>(long(0),dim(1));
864 } else if (ndim()==4) {
865 U = Tensor<T>(dim(0),dim(1),long(0));
866 VT = Tensor<T>(long(0),dim(2),dim(3));
867 } else if (ndim()==6) {
868 U = Tensor<T>(dim(0),dim(1),dim(2),long(0));
869 VT = Tensor<T>(long(0),dim(3),dim(4),dim(5));
870 } else {
871 MADNESS_EXCEPTION("ndim>6 in tensortrain::two_mode_representation",1);
872 }
874 }
875 }
876
877 /// recompress and truncate this TT representation
878
879 /// this in recompressed TT form with optimal rank
880 /// @param[in] eps the truncation threshold
881 template<typename R=T>
882 typename std::enable_if<!std::is_arithmetic<R>::value, void>::type
883 truncate(double eps) {
884 MADNESS_EXCEPTION("no complex truncate in TensorTrain",1);
885 }
886
887
888 /// recompress and truncate this TT representation
889
890 /// this in recompressed TT form with optimal rank
891 /// @param[in] eps the truncation threshold
892 template<typename R=T>
893 typename std::enable_if<std::is_arithmetic<R>::value, void>::type
894 truncate(double eps) {
895
896 // fast return
897 if (zero_rank) return;
898
899 for (long i=0; i<core.size()-1; ++i) if (ranks(i)==0) zero_rank=true;
900
901 if (zero_rank) {
902 zero_me();
903 return;
904 }
905
906 eps=eps/sqrt(this->ndim());
907 if (not verify()) MADNESS_EXCEPTION("ranks in TensorTrain inconsistent",1);
908
909 // right-to-left orthogonalization (line 4)
910 // get maximum rank and maximum k-value
911 // cores are (k0,r0), (r0,k1,r1), (r1,k2,r2), ... (rd-1,kd)
912 long rmax = core[0].dim(1);
913 long kmax = core[0].dim(0);
914 for(size_t i=1;i<core.size();i++){
915 rmax = std::max(rmax,core[i].dim(0));
916 kmax = std::max(kmax,core[i].dim(1));
917 }
918
919 Tensor<T> L;//L_buffer(rmax,rmax*kmax);
920 Tensor<T> lq_tau(rmax);
921 long max_rk = std::max(rmax,kmax);
922 long lq_work_dim = 2*max_rk+(max_rk+1)*64;
923 Tensor<T> lq_work(lq_work_dim);
924 Tensor<T> L_buffer(max_rk,max_rk);
925 long dimensions[TENSOR_MAXDIM];
926 // last tensor differs in dimension (first tensor also, but we dont reach it in the loop)
927 if(core.size()>1){
928 const long n_dim = core.back().ndim();
929 for (int i=0; i<n_dim; ++i) dimensions[i]=core.back().dim(i);
930
931 const long r0 = core.back().dim(0);
932 const long r1 = core.back().size()/r0;
933 core.back()=core.back().reshape(r0,r1);
934
935 // assignement of L with the L_buffer tensor
936 // works only if the bool for lq_result is set to false
937 {
938 long r_rows= (core.back().dim(1)>=core.back().dim(0)) ? core.back().dim(0) : core.back().dim(1);
939 long r_cols=core.back().dim(0);
940 L = L_buffer(Slice(0,r_cols-1),Slice(0,r_rows-1));
941 L = 0.0;
942 }
943 lq_result(core.back(),L,lq_tau,lq_work,false);
944 //Tensor<T> L = L_buffer(Slice(0,r0-1),Slice(0,r1-1));
945
946 dimensions[0]=std::min(dimensions[0],core.back().dim(0));
947 core.back()=core.back().reshape(n_dim,dimensions);
948 // multiply to the left (line 6)
949 core[core.size()-2]=inner(core[core.size()-2],L);
950
951 }
952
953
954 for (std::size_t d=core.size()-2; d>0; --d) {
955
956 // save tensor structure
957 const long ndim=core[d].ndim();
958 for (int i=0; i<ndim; ++i) dimensions[i]=core[d].dim(i);
959
960 // G(r0, k*r1)
961 const long r0=core[d].dim(0);
962 const long r1=core[d].size()/r0;
963 core[d]=core[d].reshape(r0,r1);
964
965 // decompose the core tensor (line 5)
966 //lq(core[d],L_buffer); // might shrink the core
967
968 // assignement of L with the inner_buffer tensor
969 // works only if the bool for lq_result is set to false
970 {
971 long r_rows= (core[d].dim(1)>=core[d].dim(0)) ? core[d].dim(0) : core[d].dim(1);
972 long r_cols=core[d].dim(0);
973 L = L_buffer(Slice(0,r_cols-1),Slice(0,r_rows-1));
974 L = 0.0;
975 }
976
977 // workaround for LQ decomposition to avoid reallocations
978 //L_buffer = 0.0;
979 lq_tau = 0.0;
980 lq_work = 0.0;
981 lq_result(core[d],L,lq_tau,lq_work,false);
982 // slice L to the right size
983 //Tensor<T> L = L_buffer(Slice(0,r0-1),Slice(0,r1-1));
984
985 dimensions[0]=std::min(r0,core[d].dim(0));
986 core[d]=core[d].reshape(ndim,dimensions);
987
988 // multiply to the left (line 6)
989 core[d-1]=inner(core[d-1],L);
990 }
991
992 // svd buffer tensor (see svd_results in lapack.cc)
993 long m =rmax*kmax;
994 long n =rmax;
995 long k =std::min<long>(m,n);
996 long svd_buffer_size = std::max<long>(3*std::min<long>(m,n)+std::max<long>(m,n),5*std::min<long>(m,n)-4)*32;
997 Tensor<T> svd_buffer(svd_buffer_size);
998 Tensor<T> U_buffer(m,k);
999 Tensor<T> dummy;
1001
1002 // left-to-right SVD (line 9)
1003 for (std::size_t d=0; d<core.size()-1; ++d) {
1004
1005 // save tensor structure
1006 const long ndim=core[d].ndim();
1007 for (int i=0; i<ndim; ++i) dimensions[i]=core[d].dim(i);
1008
1009 // reshape the core tensor (r0*k, r1)
1010 long r1=core[d].dim(core[d].ndim()-1);
1011 // long r1=core[d].dim(1);
1012 core[d]=core[d].reshape(core[d].size()/r1,r1);
1013
1014 Tensor<T> U,VT;
1015 long ds=std::min(core[d].dim(0),core[d].dim(1));
1016 s_buffer = 0.0;
1017 //Tensor< typename Tensor<T>::scalar_type > s(ds)
1018 Tensor< typename Tensor<T>::scalar_type > s = s_buffer(Slice(0,ds-1));
1019
1020 // decompose (line 10)
1021 //svd(core[d],U,s,VT);
1022 // get the dimensions of U and V
1023 long du = core[d].dim(0);
1024 long dv = core[d].dim(1);
1025 U_buffer = 0.0;
1026 svd_buffer = 0.0;
1027 U_buffer = U_buffer.flat();
1028 // VT is written on core[d] input
1029 svd_result(core[d],U_buffer,s,dummy,svd_buffer);
1030
1031 // truncate the SVD
1032 int r_truncate=SRConf<T>::max_sigma(eps,ds,s)+1;
1033 if (r_truncate==0) {
1034 zero_me();
1035 return;
1036 }
1037
1038 // make tensors contiguous
1039 U=madness::copy(U_buffer(Slice(0,(du*ds)-1)).reshape(du,ds)(_,Slice(0,r_truncate-1)));
1040 VT=madness::copy(core[d](Slice(0,r_truncate-1),Slice(0,dv-1)));
1041
1042 dimensions[ndim-1]=r_truncate;
1043 core[d]=U.reshape(ndim,dimensions);
1044
1045 for (int i=0; i<VT.dim(0); ++i) {
1046 for (int j=0; j<VT.dim(1); ++j) {
1047 VT(i,j)*=s(i);
1048 }
1049 }
1050
1051 // multiply to the right (line 11)
1052 core[d+1]=inner(VT,core[d+1]);
1053
1054 }
1055
1056 if (not verify()) MADNESS_EXCEPTION("ranks in TensorTrain inconsistent",1);
1057 }
1058
1059// /// return the number of dimensions
1060// long ndim() const {return core.size();}
1061
1062 /// return the number of coefficients in all core tensors
1063 long size() const {
1064 if (zero_rank) return 0;
1065 long n=0;
1066 typename std::vector<Tensor<T> >::const_iterator it;
1067 for (it=core.begin(); it!=core.end(); ++it) n+=it->size();
1068 return n;
1069 }
1070
1071 /// return the size of this instance, including static memory for vectors and such
1072 long real_size() const {
1073 long n=this->size()*sizeof(T);
1074 n+=core.size() * sizeof(Tensor<T>);
1075 n+=sizeof(*this);
1076 return n;
1077 }
1078
1079// /// return the number of entries in dimension i
1080// long dim(const int i) const {
1081// if (i==0) return core[0].dim(0);
1082// return core[i].dim(1);
1083// }
1084
1085 /// return the dimensions of this tensor
1086// std::vector<long> dims() const {
1087// std::vector<long> d(ndim());
1088// d[0]=core[0].dim(0); // dim,rank
1089// for (long i=1; i<ndim(); ++i) d[i]=core[i].dim(1); // rank,dim,rank
1090// return d;
1091// }
1092
1093 /// check that the ranks of all core tensors are consistent
1094 bool verify() const {
1095 if (core.size()<2) return true; // no ranks
1096 if (core[0].dim(1)!=core[1].dim(0)) return false;
1097 for (int d=2; d<ndim(); ++d) {
1098 if (core[d-1].dim(2)!=core[d].dim(0)) return false;
1099 }
1100 for (const Tensor<T>& c : core) {
1101 int size=1;
1102 for (int i=0; i<c.ndim(); ++i) size*=c.dim(i);
1103 if (size!=c.size()) return false;
1104 if (not c.iscontiguous()) return false;
1105 }
1106 return true;
1107 }
1108
1109 /// if rank is zero
1110 bool is_zero_rank() const {return zero_rank;}
1111
1112 /// return the TT ranks
1113 std::vector<long> ranks() const {
1114 if (ndim()==0) return std::vector<long>(1,0);
1115 if (zero_rank) return std::vector<long>(ndim()-1,0);
1116// MADNESS_ASSERT(is_tensor());
1117 std::vector<long> r(core.size()-1);
1118 for (std::size_t i=0; i<r.size(); ++i) r[i]=core[i+1].dim(0);
1119 return r;
1120 }
1121
1122 /// return the TT ranks for dimension i (to i+1)
1123 long ranks(const int i) const {
1124 if (zero_rank) return 0;
1125 if (i<core.size()-1) {
1126 return core[i].dim(core[i].ndim()-1);
1127 } else {
1128 print("ndim ",ndim());
1129 print("i ",i);
1130 MADNESS_EXCEPTION("requested invalid rank in TensorTrain",1);
1131 return 0;
1132 }
1133 }
1134
1135 /// returns the Frobenius norm
1137 return sqrt(float_scalar_type(std::abs(trace(*this))));
1138 };
1139
1140 /// scale this by a number
1141
1142 /// @param[in] fac the factor to multiply
1143 /// @return *this * fac
1144 void scale(T fac) {
1145 if (not zero_rank and (core.size()>0)) core.front().scale(fac);
1146 }
1147
1148 /// Returns a pointer to the internal data
1149
1150 /// @param[in] ivec index of core vector to which the return values points
1151 T* ptr(const int ivec=0) {
1152 if (core.size()) return core[ivec].ptr();
1153 return 0;
1154 }
1155
1156 /// Returns a pointer to the internal data
1157
1158 /// @param[in] ivec index of core vector to which the return values points
1159 const T* ptr(const int ivec=0) const {
1160 if (core.size()) return core[ivec].ptr();
1161 return 0;
1162 }
1163
1164 /// check if this is a tensor (r,k,r)
1165 bool is_tensor() const {
1166 if (ndim()>0) return (core[0].ndim()==2);
1167 return false;
1168 }
1169
1170 /// check if this is an operator (r,k',k,r)
1171 bool is_operator() const {
1172 return (!is_tensor());
1173 }
1174
1175 /// convert this into a tensor representation (r,k,r)
1177 if (is_tensor()) return *this;
1178
1179 long nd=this->ndim();
1180 core[0]=core[0].fusedim(0); // (k,k,r) -> (k,r)
1181 core[nd-1]=core[nd-1].fusedim(1); // (r,k,k) -> (r,k)
1182 for (int i=1; i<nd-1; ++i) core[i]=core[i].fusedim(1); // (r,k',k,r) -> (r,k,r)
1183 return *this;
1184
1185 }
1186
1187 /// convert this into an operator representation (r,k',k,r)
1189 if (is_operator()) return *this;
1190
1191 long nd=this->ndim();
1192
1193 long k2=core[0].dim(0);
1194 long k0=sqrt(k2);
1196 MADNESS_ASSERT(core[0].ndim()==2);
1197 core[0]=core[0].splitdim(0,k0,k0); // (k*k,r) -> (k,k,r)
1198
1199 k2=core[nd-1].dim(1);
1200 k0=sqrt(k2);
1202 MADNESS_ASSERT(core[nd-1].ndim()==2);
1203 core[nd-1]=core[nd-1].splitdim(1,k0,k0); // (r,k*k) -> (r,k,k)
1204
1205 for (int i=1; i<nd-1; ++i) {
1206 k2=core[i].dim(1);
1207 k0=sqrt(k2);
1209 MADNESS_ASSERT(core[i].ndim()==3);
1210 core[i]=core[i].splitdim(1,k0,k0); // (r,k*k,r) -> (r,k',k,r)
1211 }
1212 return *this;
1213 }
1214
1215
1216 /// reference to the internal core
1217 Tensor<T>& get_core(const int i) {
1218 MADNESS_ASSERT(i<ndim());
1219 return core[i];
1220 }
1221
1222 /// const reference to the internal core
1223 const Tensor<T>& get_core(const int i) const {
1224 MADNESS_ASSERT(i<ndim());
1225 return core[i];
1226 }
1227
1228 template <class Q>
1231 trace(const TensorTrain<Q>& B) const {
1232 MADNESS_EXCEPTION("no complex trace in tensortrain",1);
1233 }
1234 /// Return the trace of two tensors, no complex conjugate involved
1235
1236 /// @return <this | B>
1237 template <class Q>
1238 typename std::enable_if<!(TensorTypeData<T>::iscomplex or TensorTypeData<Q>::iscomplex),
1240 trace(const TensorTrain<Q>& B) const {
1241 if (TensorTypeData<T>::iscomplex) MADNESS_EXCEPTION("no complex trace in TensorTrain, sorry",1);
1242 if (TensorTypeData<Q>::iscomplex) MADNESS_EXCEPTION("no complex trace in TensorTrain, sorry",1);
1243
1244 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
1245
1246 // alias
1247 const TensorTrain<T>& A=*this;
1248
1249 MADNESS_ASSERT(A.ndim()==B.ndim()); // number of dimensions
1250
1251 // fast return
1252 if (A.zero_rank or B.zero_rank) return resultT(0.0);
1253 if (A.ndim()==1) {
1254 return A.core[0].trace(B.core[0]);
1255 }
1256
1257 // set up temporary tensors for intermediates
1258 long size1=A.ranks(0)*B.ranks(0); // first contraction
1259 long size2=B.ranks(ndim()-2)*A.dim(ndim()-1); // last contraction
1260
1261 for (int d=1; d<A.ndim()-1; ++d) {
1262 size1=std::max(size1,A.ranks(d)*B.ranks(d));
1263 size2=std::max(size2,A.ranks(d)*B.ranks(d-1)*A.dim(d));
1264 }
1265 Tensor<resultT> tmp1(size1), tmp2(size2); // scratch
1266 Tensor<resultT> Aprime, AB; // for flat views of tmp
1267
1268 // loop over all dimensions but the last one
1269 for (int d=0; d<A.ndim()-1; ++d) {
1270
1271 // contract cores to matrix AB(rank(A), rank(B))
1272 // AB(ra1,rb1) = sum_(r0,i0) A(ra0,i0,ra1) B(rb0,i0,rb1)
1273 // index dimension is the first one: core((r * i), r)
1274
1275 // calculate dimensions
1276 long rA= (d==0) ? A.core[d].dim(1) : Aprime.dim(2); // r_d (A)
1277 long rB= (d==0) ? B.core[d].dim(1) : B.core[d].dim(2); // r_d (B)
1278 MADNESS_ASSERT(rA*rB<=size1);
1279 if (d>0) tmp1(Slice(0,rA*rB-1))=0.0; // zero out old stuff
1280
1281// Tensor<resultT> AB;
1282 if (d==0) {
1283// AB=inner(A.core[d],B.core[d],0,0);
1284 inner_result(A.core[d],B.core[d],0,0,tmp1);
1285 } else {
1286// AB=inner(Aprime.fusedim(0),B.core[d].fusedim(0),0,0);
1287 inner_result(Aprime.fusedim(0),B.core[d].fusedim(0),0,0,tmp1);
1288 }
1289 AB=tmp1(Slice(0,rA*rB-1)).reshape(rA,rB);
1290
1291 // contract temp matrix AB into core of A of dimension i+1
1292 // into temp core A_i+1
1293 // Atmp(rank(B1), i(2)) = sum_ rank(A1) ABi(rank(A1),rank(B1)) A.core[1](rank(A1),i(2),rank(A2))
1294
1295 // calculate dimensions
1296 long d1=AB.dim(1); // r_d
1297 long d2=A.core[d+1].dim(1); // i_d
1298 long d3=A.core[d+1].dim(2); // r_{d+1}
1299 MADNESS_ASSERT(d1*d2*d3<=size2);
1300
1301 // repeated zero-ing probably much faster than reallocation
1302 // Aprime=inner(AB,A.core[d+1],0,0);
1303 if (d>0) tmp2(Slice(0,d1*d2*d3-1))=0.0;
1304 inner_result(AB,A.core[d+1],0,0,tmp2);
1305 Aprime=tmp2(Slice(0,d1*d2*d3-1)).reshape(d1,d2,d3);
1306
1307 }
1308
1309 // special treatment for the last dimension
1310 resultT result=Aprime.trace(B.core[ndim()-1]);
1311 return result;
1312 }
1313
1314public:
1315 template <typename R, typename Q>
1317 const TensorTrain<R>& t, const Tensor<Q>& c);
1318
1319 template <typename R, typename Q>
1321 const TensorTrain<R>& t, const Tensor<Q> c[]);
1322
1323 template <typename R, typename Q>
1325 const TensorTrain<R>& t, const Tensor<Q>& c, const int axis);
1326
1327 template <typename R, typename Q>
1329 const TensorTrain<R>& t1, const TensorTrain<Q>& t2);
1330
1331 };
1332
1333
1334 /// transform each dimension with the same operator matrix
1335
1336 /// result(i,j,k...) <-- sum(i',j', k',...) t(i',j',k',...) c(i',i) c(j',j) c(k',k) ...
1337 /// TODO: merge this with general_transform
1338 template <class T, class Q>
1340 const Tensor<Q>& c) {
1341
1342 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
1343
1344 // fast return if possible
1345 if (t.zero_rank or (t.ndim()==0)) return TensorTrain<resultT>(t.ndim(),t.dims());
1346
1347 const long ndim=t.ndim();
1348
1349 TensorTrain<resultT> result;
1350 result.zero_rank=false;
1351 result.core.resize(ndim);
1352 // special treatment for first core(i1,r1) and last core (rd-1, id)
1353 result.core[0]=inner(c,t.core[0],0,0);
1354 if (ndim>1) result.core[ndim-1]=inner(t.core[ndim-1],c,1,0);
1355
1356 // other cores have dimensions core(r1,i2,r2);
1357
1358 // set up scratch tensor
1359 long size=0;
1360 for (int d=1; d<ndim-1; ++d) size=std::max(size,t.core[d].size());
1361 Tensor<resultT> tmp(size);
1362
1363 for (int d=1; d<ndim-1; ++d) {
1364 long r1=t.core[d].dim(0);
1365 long i2=t.core[d].dim(1);
1366 long r2=t.core[d].dim(2);
1367
1368 // zero out old stuff from the scratch tensor
1369 if (d>1) tmp(Slice(0,r1*i2*r2-1))=0.0;
1370 inner_result(t.core[d],c,1,0,tmp);
1371 result.core[d]=copy(tmp(Slice(0,r1*i2*r2-1)).reshape(r1,r2,i2).swapdim(1,2));
1372 }
1373 return result;
1374 }
1375
1376
1377 /// Transform all dimensions of the tensor t by distinct matrices c
1378
1379 /// \ingroup tensor
1380 /// Similar to transform but each dimension is transformed with a
1381 /// distinct matrix.
1382 /// \code
1383 /// result(i,j,k...) <-- sum(i',j', k',...) t(i',j',k',...) c[0](i',i) c[1](j',j) c[2](k',k) ...
1384 /// \endcode
1385 /// The first dimension of the matrices c must match the corresponding
1386 /// dimension of t.
1387 template <class T, class Q>
1389 const Tensor<Q> c[]) {
1390
1391 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
1392
1393 // fast return if possible
1394 if (t.zero_rank or (t.ndim()==0)) return TensorTrain<resultT>(t.ndim(),t.dims());
1395
1396 const long ndim=t.ndim();
1397
1398 TensorTrain<resultT> result;
1399 result.zero_rank=false;
1400 result.core.resize(ndim);
1401 // special treatment for first core(i1,r1) and last core (rd-1, id)
1402 result.core[0]=inner(c[0],t.core[0],0,0);
1403 if (ndim>1) result.core[ndim-1]=inner(t.core[ndim-1],c[ndim-1],1,0);
1404
1405 // other cores have dimensions core(r1,i2,r2);
1406
1407 // set up scratch tensor
1408 long size=0;
1409 for (int d=1; d<ndim-1; ++d) size=std::max(size,t.core[d].size());
1410 Tensor<resultT> tmp(size);
1411
1412 for (int d=1; d<ndim-1; ++d) {
1413 long r1=t.core[d].dim(0);
1414 long i2=t.core[d].dim(1);
1415 long r2=t.core[d].dim(2);
1416
1417 // zero out old stuff from the scratch tensor
1418 if (d>1) tmp(Slice(0,r1*i2*r2-1))=0.0;
1419 inner_result(t.core[d],c[d],1,0,tmp);
1420 result.core[d]=copy(tmp(Slice(0,r1*i2*r2-1)).reshape(r1,r2,i2).swapdim(1,2));
1421 }
1422 return result;
1423 }
1424
1425 /// Transforms one dimension of the tensor t by the matrix c, returns new contiguous tensor
1426
1427 /// \ingroup tensor
1428 /// \code
1429 /// transform_dir(t,c,1) = r(i,j,k,...) = sum(j') t(i,j',k,...) * c(j',j)
1430 /// \endcode
1431 /// @param[in] t Tensor to transform (size of dimension to be transformed must match size of first dimension of \c c )
1432 /// @param[in] c Matrix used for the transformation
1433 /// @param[in] axis Dimension (or axis) to be transformed
1434 /// @result Returns a new tensor train
1435 template <class T, class Q>
1437 const Tensor<Q>& c, const int axis) {
1438
1439 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
1440
1441 // fast return if possible
1442 if (t.zero_rank or (t.ndim()==0)) return TensorTrain<resultT>(t.ndim(),t.dims());
1443
1444 const long ndim=t.ndim();
1445 MADNESS_ASSERT(axis<ndim and axis>=0);
1446 MADNESS_ASSERT(c.ndim()==2);
1447
1448 TensorTrain<resultT> result=copy(t);
1449
1450 if (axis==0) {
1451 result.core[0]=inner(c,t.core[0],0,0);
1452 } else if (axis==ndim-1) {
1453 result.core[ndim-1]=inner(t.core[ndim-1],c,1,0);
1454 } else {
1455 Tensor<resultT> tmp=inner(t.core[axis],c,1,0); // G~(r1,r2,i')
1456 result.core[axis]=copy(tmp.swapdim(1,2));
1457 }
1458 return result;
1459
1460 }
1461
1462
1463 /// apply an operator in TT format on a tensor in TT format
1464
1465 /// @param[in] op operator in TT format ..(r_1,k',k,r_2)..
1466 /// @param[in] t tensor in TT format ..(r_1,k',r_2)..
1467 /// the result tensor will be
1468 /// .. (r_1,k,r_2) = \sum_k' ..(r_1,k',k,r_2).. ..(r_1,k',r_2)..
1469 /// during the apply a rank reduction will be performed
1470 /// 2*ndim allocates are needed
1471 template <class T, class Q>
1473 const TensorTrain<Q>& t, const double thresh) {
1474
1475 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
1476 MADNESS_ASSERT(op.ndim()==t.ndim());
1477
1478 const long nd=t.ndim();
1479
1480 // need to add special cases for low dimensions
1481 MADNESS_ASSERT(nd>2);
1482
1483 std::vector<Tensor<resultT> > B(t.ndim()); // will be the result cores
1484
1485 // set up scratch tensors
1486 long maxk=0; // max dimension of the tensor t
1487 long maxr_t=1; // max rank of the input tensor t
1488 long maxr_op=1; // max rank of the operator op
1489 for (int i=0; i<nd-1; ++i) {
1490 maxk=std::max(maxk,t.dim(i));
1491 maxr_t=std::max(t.ranks(i),maxr_t);
1492 maxr_op=std::max(op.ranks(i),maxr_op);
1493 }
1494
1495 long maxr_r=1; // max rank of the result tensor
1496 for (int i=0, j=nd-1; i<j; ++i, --j) {
1497 maxr_r*=t.dim(i);
1498 }
1499
1500 long maxn=0, maxm=0;
1501// {
1502 long R11=t.dim(0); // max final ranks
1503 long rR=R11*t.ranks(1); // R1 r2
1504 long maxR=R11, maxr=t.ranks(1); //
1505 for (int i=1; i<nd-2; ++i) {
1506 long k=t.dim(i);
1507 R11*=k; // max final ranks
1508 R11=std::min(R11,maxr_r);
1509 long r2=t.ranks(i+1); // initial ranks
1510 if (rR<R11*r2) {
1511 maxR=std::max(maxR,R11);
1512 maxr=std::max(maxr,r2);
1513 rR=R11*r2;
1514 }
1515 }
1516 // max matrix dimensions to be svd'ed
1517 maxn=maxR*maxk;
1518 maxm=maxr_op*maxr;
1519// }
1520
1521
1522 if (maxm*maxn>5e7) {
1523 print("huge scratch spaces!! ",maxn*maxm/1024/1024,"MByte");
1524 }
1525 long lscr=std::max(3*std::min(maxm,maxn)+std::max(maxm,maxn),
1526 5*std::min(maxm,maxn)) + maxn*maxm;
1527 Tensor<resultT> scr(lscr); // scratch
1528 Tensor< typename Tensor<T>::scalar_type > s(std::min(maxn,maxm));
1529
1530 // scratch space for contractions
1531 Tensor<resultT> scr3(2*maxn*maxm);
1532 Tensor<resultT> scr1=scr3(Slice(0,maxn*maxm-1));
1533 Tensor<resultT> scr2=scr3(Slice(maxn*maxm,-1));
1534
1535
1536 // contract first core
1537 const long r0=t.ranks(0l);
1538 const long q0=op.get_core(0).dim(2);
1539 const long k0=t.dim(0);
1540 inner_result(op.get_core(0),t.get_core(0),0,0,scr2);
1541 Tensor<resultT> AC=scr2(Slice(0,r0*q0*k0-1)).reshape(k0,r0*q0);
1542
1543 // SVD on first core, skip small singular values
1544 long R=rank_revealing_decompose(AC,B[0],thresh,s,scr1);
1545 if (R==0) return TensorTrain<resultT>(t.ndim(),t.dims()); // fast return for zero ranks
1546 B[0]=B[0].reshape(k0,R);
1547
1548 // AC has dimensions R1,(q1,r1)
1549 Tensor<resultT> VT=AC.reshape(R,q0,r0);
1550
1551 // loop over all dimensions 1,..,nd-1
1552 for (int d=1; d<nd; ++d) {
1553
1554 // alias
1555 Tensor<T> C=t.get_core(d);
1556 Tensor<T> A=op.get_core(d);
1557
1558 // alias dimensions
1559 long R1=VT.dim(0); // left rank of the result tensor
1560 long q1=A.dim(0); // left rank of the operator
1561 long k=C.dim(1); // true for d>0
1562 long r2=1; // right rank of the input tensor, true for d=nd-1
1563 long q2=1; // right rank of the operator, true for d=nd-1
1564 if (d<nd-1) {
1565 r2=C.dim(2); // right rank of the input tensor, true for d<nd-1
1566 q2=A.dim(3); // right rank of the operator
1567 }
1568
1569 // contract VT into the next core
1570 Tensor<resultT> VC=scr1(Slice(0,R1*q1*k*r2-1));
1571 VC(Slice(0,R1*q1*k*r2-1))=resultT(0.0); // zero out result tensor
1572 inner_result(VT,C,-1,0,VC); // VT(R1,q1,r1) * C(r1,k',r2)
1573
1574 // contract A into VC (R,q,k,r2)
1575 VC=VC.reshape(R1,q1*k,r2); // VC(R,(q,k),r2)
1576 A=A.fusedim(0); // A((q1,k),k,q2);
1577 Tensor<resultT> AVC=scr2(Slice(0,R1*q2*k*r2-1));
1578 AVC(Slice(0,R1*q2*k*r2-1))=resultT(0.0); // zero out result tensor
1579 inner_result(VC,A,1,0,AVC); // AVC(R1,r2,k,q2)
1580 AVC=AVC.reshape(R1,r2,k,q2);
1581
1582 // AVC is the final core if we have reached the end of the tensor train
1583 if (d==nd-1) {
1584 B[d]=copy(AVC.reshape(R1,k));
1585 break;
1586 }
1587
1588 // SVD on current core
1589 AVC=copy(AVC.cycledim(2,1,3)); // AVC(R,k,q2,r2); deep copy necessary
1590
1591 MADNESS_ASSERT(AVC.dim(0)==R1);
1592 MADNESS_ASSERT(AVC.dim(1)==k);
1593 MADNESS_ASSERT(AVC.dim(2)==q2);
1594
1595 AVC=AVC.reshape(R1*k,q2*r2);
1596 long R2=rank_revealing_decompose(AVC,B[d],thresh,s,scr1);
1597 if (R2==0) return TensorTrain<resultT>(t.ndim(),t.dims()); // fast return for zero ranks
1598 B[d]=B[d].reshape(R1,k,R2);
1599 VT=AVC.reshape(R2,q2,r2);
1600 }
1601
1602 TensorTrain<T> result(B);
1603 return result;
1604
1605 }
1606
1607
1608 /// compute the n-D identity operator with k elements per dimension
1609 template<typename T>
1610 TensorTrain<T> tt_identity(const long ndim, const long k) {
1611 Tensor<T> id(k,k);
1612 for (int i=0; i<k; ++i) id(i,i)=1.0;
1613 id=id.reshape(1,k,k,1);
1614 std::vector<Tensor<T> > cores(ndim,id);
1615 TensorTrain<T> result(cores);
1616 if (ndim>1) {
1617 result.get_core(0)=result.get_core(0).reshape(k,k,1);
1618 result.get_core(ndim-1)=result.get_core(ndim-1).reshape(1,k,k);
1619 } else {
1620 result.get_core(0)=result.get_core(0).reshape(k,k);
1621 }
1622 return result;
1623 }
1624
1625
1626 /// computes the outer product of two tensors
1627
1628 /// result(i,j,...,p,q,...) = left(i,k,...)*right(p,q,...)
1629 /// @result Returns a new tensor train
1630 template <class T, class Q>
1632 const TensorTrain<Q>& t2) {
1633
1634 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
1635
1636
1637 // fast return if possible
1638 if (t1.zero_rank or t2.zero_rank) {
1639 // compute new dimensions
1640 std::vector<long> dims(t1.ndim()+t2.ndim());
1641 for (int i=0; i<t1.ndim(); ++i) dims[i]=t1.dim(i);
1642 for (int i=0; i<t2.ndim(); ++i) dims[t1.ndim()+i]=t2.dim(i);
1643
1644 return TensorTrain<resultT>(dims);
1645 }
1646
1647 TensorTrain<resultT> result;
1648 for (int i=0; i<t1.ndim(); ++i) result.core.push_back(copy(t1.core[i]));
1649 for (int i=0; i<t2.ndim(); ++i) result.core.push_back(copy(t2.core[i]));
1650
1651 // reshape the new interior cores
1652 long core_dim=t1.core.back().ndim(); // 2 for tensors, 3 for operators
1653 long k1=t1.core.back().dim(core_dim-1); // (r,k) for tensors, (r,k',k) for operators
1654 long k2=t2.core.front().dim(0);
1655 result.core[t1.ndim()-1]=result.core[t1.ndim()-1].splitdim(core_dim-1,k1,1);
1656 result.core[t1.ndim()]=result.core[t1.ndim()].splitdim(0,1,k2);
1657 result.zero_rank=false;
1658
1659 return result;
1660
1661 }
1662
1663}
1664
1665#endif /* TENSORTRAIN_H_ */
Interface templates for the archives (serialization).
C++ interface to LAPACK, either directly via Fortran API (see clapack_fortran.h) or via LAPACKE (see ...
Definition test_ar.cc:118
Definition test_ar.cc:141
Definition test_ar.cc:170
Definition AC.h:427
The base class for tensors defines generic capabilities.
Definition basetensor.h:85
long dim(int i) const
Returns the size of dimension i.
Definition basetensor.h:147
const long * dims() const
Returns the array of tensor dimensions.
Definition basetensor.h:153
long _stride[TENSOR_MAXDIM]
Increment between elements in each dimension.
Definition basetensor.h:97
long _size
Number of elements in the tensor.
Definition basetensor.h:93
void set_dims_and_size(long nd, const long d[])
Definition basetensor.h:99
long _id
Id from TensorTypeData<T> in type_data.h.
Definition basetensor.h:95
void splitdim_inplace(long i, long dimi0, long dimi1)
Splits dimension i.
Definition basetensor.cc:88
void fusedim_inplace(long i)
Fuses dimensions i and i+1.
Definition basetensor.cc:107
long _dim[TENSOR_MAXDIM]
Size of each dimension.
Definition basetensor.h:96
long ndim() const
Returns the number of dimensions in the tensor.
Definition basetensor.h:144
long size() const
Returns the number of elements in the tensor.
Definition basetensor.h:138
long _ndim
Number of dimensions (-1=invalid; 0=no supported; >0=tensor)
Definition basetensor.h:94
static int max_sigma(const double &thresh, const long &rank, const Tensor< double > &w)
Definition srconf.h:109
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
Definition tensortrain.h:123
void zero_me()
turn this into an empty tensor with all cores properly shaped
Definition tensortrain.h:470
TensorTrain(const TensorTrain &other)
copy constructor, shallow
Definition tensortrain.h:238
const Tensor< T > & get_core(const int i) const
const reference to the internal core
Definition tensortrain.h:1223
bool verify() const
return the dimensions of this tensor
Definition tensortrain.h:1094
TensorTrain(const long &ndims, const long *dims)
ctor for a TensorTrain, set up only the dimensions, no data
Definition tensortrain.h:198
friend TensorTrain< TENSOR_RESULT_TYPE(R, Q)> outer(const TensorTrain< R > &t1, const TensorTrain< Q > &t2)
std::enable_if< std::is_arithmetic< R >::value, void >::type truncate(double eps)
recompress and truncate this TT representation
Definition tensortrain.h:894
TensorTrain< T > operator*(const T &factor) const
return this multiplied by a scalar
Definition tensortrain.h:497
friend class TensorTrain
Definition tensortrain.h:127
void set_size_and_dim(std::vector< long > dims)
Definition tensortrain.h:265
TensorTrain< T > splitdim(long idim, long k1, long k2, const double eps) const
Definition tensortrain.h:749
void scale(T fac)
scale this by a number
Definition tensortrain.h:1144
void two_mode_representation(Tensor< T > &U, Tensor< T > &VT, Tensor< typename Tensor< T >::scalar_type > &s) const
construct a two-mode representation (aka unnormalized SVD)
Definition tensortrain.h:843
std::vector< Tensor< T > > core
Definition tensortrain.h:140
TensorTypeData< T >::scalar_type scalar_type
C++ typename of the real type associated with a complex type.
Definition tensortrain.h:133
long real_size() const
return the size of this instance, including static memory for vectors and such
Definition tensortrain.h:1072
std::enable_if<!std::is_arithmetic< R >::value, void >::type truncate(double eps)
recompress and truncate this TT representation
Definition tensortrain.h:883
void zero_me(const std::vector< long > &dim)
turn this into an empty tensor with all cores properly shaped
Definition tensortrain.h:475
float_scalar_type normf() const
returns the Frobenius norm
Definition tensortrain.h:1136
TensorTrain< T > & operator-=(const TensorTrain< T > &rhs)
inplace subtraction of two Tensortrains; will increase ranks of this
Definition tensortrain.h:518
friend TensorTrain< TENSOR_RESULT_TYPE(R, Q)> transform_dir(const TensorTrain< R > &t, const Tensor< Q > &c, const int axis)
Tensor< T > reconstruct(const bool flat=false) const
reconstruct this to a full representation
Definition tensortrain.h:815
Tensor< T > & get_core(const int i)
reference to the internal core
Definition tensortrain.h:1217
long size() const
return the number of coefficients in all core tensors
Definition tensortrain.h:1063
void fusedim(const long i)
merge two dimensions into one
Definition tensortrain.h:716
TensorTrain(const Tensor< T > &t, double eps, const std::vector< long > dims)
ctor for a TensorTrain, with the tolerance eps
Definition tensortrain.h:188
TensorTrain(const std::vector< Tensor< T > > &c)
ctor for a TensorTrain, with core tensors explicitly given
Definition tensortrain.h:225
T type
C++ typename of this tensor.
Definition tensortrain.h:130
TensorTrain< T > & operator=(const T &number)
assign a number to this tensor
Definition tensortrain.h:480
TensorTrain(const std::vector< long > &dims)
ctor for a TensorTrain, set up only the dimensions, no data
Definition tensortrain.h:204
std::vector< long > ranks() const
return the TT ranks
Definition tensortrain.h:1113
TensorTrain< T > & make_operator()
convert this into an operator representation (r,k',k,r)
Definition tensortrain.h:1188
TensorTrain< T > & gaxpy(const std::array< Slice, TENSOR_MAXDIM > &s1, const TensorTrain< T > &rhs, T beta, const std::array< Slice, TENSOR_MAXDIM > &s2)
Inplace generalized saxpy with slices and without alpha.
Definition tensortrain.h:583
const T * ptr(const int ivec=0) const
Returns a pointer to the internal data.
Definition tensortrain.h:1159
void decompose(const Tensor< T > &t, double eps, const std::vector< long > &dims)
decompose the input tensor into a TT representation
Definition tensortrain.h:335
TensorTrain< T > & operator+=(const TensorTrain< T > &rhs)
inplace addition of two Tensortrains; will increase ranks of this
Definition tensortrain.h:508
friend TensorTrain copy(const TensorTrain &other)
deep copy of the whole tensor
Definition tensortrain.h:288
bool is_operator() const
check if this is an operator (r,k',k,r)
Definition tensortrain.h:1171
friend TensorTrain copy(const TensorTrain &other, const std::array< Slice, TENSOR_MAXDIM > &s)
deep copy of a slice of the tensor
Definition tensortrain.h:308
bool zero_rank
true if rank is zero
Definition tensortrain.h:142
friend TensorTrain< TENSOR_RESULT_TYPE(R, Q)> general_transform(const TensorTrain< R > &t, const Tensor< Q > c[])
friend TensorTrain< TENSOR_RESULT_TYPE(R, Q)> transform(const TensorTrain< R > &t, const Tensor< Q > &c)
bool is_tensor() const
check if this is a tensor (r,k,r)
Definition tensortrain.h:1165
TensorTrain< T > & gaxpy(T alpha, const TensorTrain< T > &rhs, T beta)
Inplace generalized saxpy ... this = this*alpha + other*beta.
Definition tensortrain.h:524
bool is_zero_rank() const
if rank is zero
Definition tensortrain.h:1110
TensorTrain< T > & emul(const TensorTrain< T > &other)
compute the Hadamard product of two TensorTrains
Definition tensortrain.h:641
std::enable_if<!(TensorTypeData< T >::iscomplexorTensorTypeData< Q >::iscomplex), TENSOR_RESULT_TYPE(T, Q)>::type trace(const TensorTrain< Q > &B) const
Return the trace of two tensors, no complex conjugate involved.
Definition tensortrain.h:1240
long ranks(const int i) const
return the TT ranks for dimension i (to i+1)
Definition tensortrain.h:1123
TensorTrain()
empty constructor
Definition tensortrain.h:147
std::enable_if<(TensorTypeData< T >::iscomplexorTensorTypeData< Q >::iscomplex), TENSOR_RESULT_TYPE(T, Q)>::type trace(const TensorTrain< Q > &B) const
Definition tensortrain.h:1231
T * ptr(const int ivec=0)
Returns a pointer to the internal data.
Definition tensortrain.h:1151
void serialize(Archive &ar)
serialize this
Definition tensortrain.h:271
TensorTypeData< T >::float_scalar_type float_scalar_type
C++ typename of the floating point type associated with scalar real type.
Definition tensortrain.h:136
TensorTrain< T > & make_tensor()
convert this into a tensor representation (r,k,r)
Definition tensortrain.h:1176
TensorTrain(const Tensor< T > &t, double eps)
ctor for a TensorTrain, with the tolerance eps
Definition tensortrain.h:162
TensorTrain & operator=(const TensorTrain &other)
assigment operator
Definition tensortrain.h:245
Traits class to specify support of numeric types.
Definition type_data.h:56
A tensor is a multidimension array.
Definition tensor.h:317
Tensor< T > swapdim(long idim, long jdim)
Returns new view/tensor swaping dimensions i and j.
Definition tensor.h:1605
Tensor< T > reshape(int ndimnew, const long *d)
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1384
TensorTypeData< T >::scalar_type scalar_type
C++ typename of the real type associated with a complex type.
Definition tensor.h:409
Tensor< T > cycledim(long nshift, long start, long end)
Returns new view/tensor cycling the sub-dimensions (start,...,end) with shift steps.
Definition tensor.h:1641
T trace(const Tensor< T > &t) const
Return the trace of two tensors (no complex conjugate invoked)
Definition tensor.h:1776
Tensor< T > fusedim(long i)
Returns new view/tensor fusing contiguous dimensions i and i+1.
Definition tensor.h:1587
Tensor< T > flat()
Returns new view/tensor rehshaping to flat (1-d) tensor.
Definition tensor.h:1555
static const double R
Definition csqrt.cc:46
Correspondence between C++ and Fortran types.
auto T(World &world, response_space &f) -> response_space
Definition global_functions.cc:34
archive_array< T > wrap(const T *, unsigned int)
Factory function to wrap a dynamically allocated pointer as a typed archive_array.
Definition archive.h:913
void inner_result(const Tensor< T > &left, const Tensor< Q > &right, long k0, long k1, Tensor< TENSOR_RESULT_TYPE(T, Q) > &result)
Accumulate inner product into user provided, contiguous, correctly sized result tensor.
Definition tensor.h:2295
const double beta
Definition gygi_soltion.cc:62
static double u(double r, double c)
Definition he.cc:20
Tensor< double > op(const Tensor< double > &x)
Definition kain.cc:508
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_ASSERT(condition)
Assert a condition that should be free of side-effects since in release builds this might be a no-op.
Definition madness_exception.h:134
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
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:773
GenTensor< TENSOR_RESULT_TYPE(R, Q)> general_transform(const GenTensor< R > &t, const Tensor< Q > c[])
Definition gentensor.h:274
void lq_result(Tensor< T > &A, Tensor< T > &R, Tensor< T > &tau, Tensor< T > &work, bool do_qr)
compute the LQ decomposition of the matrix A = L Q
Definition lapack.cc:1320
TensorTrain< T > tt_identity(const long ndim, const long k)
compute the n-D identity operator with k elements per dimension
Definition tensortrain.h:1610
long rank_revealing_decompose(Tensor< T > &A, Tensor< T > &U, const double thresh, Tensor< typename Tensor< T >::scalar_type > &s, Tensor< T > &scr)
Definition tensortrain.h:66
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > transform(World &world, const std::vector< Function< T, NDIM > > &v, const Tensor< R > &c, bool fence=true)
Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i].
Definition vmra.h:689
static double r2(const coord_3d &x)
Definition smooth.h:45
static const Slice _(0,-1, 1)
std::enable_if< std::is_base_of< ProjectorBase, projT >::value, OuterProjector< projT, projQ > >::type outer(const projT &p0, const projQ &p1)
Definition projector.h:457
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:739
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:225
response_space apply(World &world, std::vector< std::vector< std::shared_ptr< real_convolution_3d > > > &op, response_space &f)
Definition basic_operators.cc:39
static const int kmax
Definition twoscale.cc:52
double inner(response_space &a, response_space &b)
Definition response_functions.h:442
GenTensor< TENSOR_RESULT_TYPE(R, Q)> transform_dir(const GenTensor< R > &t, const Tensor< Q > &c, const int axis)
Definition lowranktensor.h:1099
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:2002
static long abs(long a)
Definition tensor.h:218
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
double Q(double a)
Definition relops.cc:20
static const double c
Definition relops.cc:10
static const double m
Definition relops.cc:9
static const double L
Definition rk.cc:46
static const double thresh
Definition rk.cc:45
static const long k
Definition rk.cc:44
Defines and implements most of Tensor.
Prototypes for a partial interface from Tensor to LAPACK.
#define TENSOR_MAXDIM
Definition tensor_macros.h:194
double aa
Definition testbsh.cc:68
static const double alpha
Definition testcosine.cc:10
std::size_t axis
Definition testpdiff.cc:59
double k0
Definition testperiodic.cc:66
double k2
Definition testperiodic.cc:68
double k1
Definition testperiodic.cc:67
static const int maxR
Definition testperiodicdft.cc:33
#define TENSOR_RESULT_TYPE(L, R)
This macro simplifies access to TensorResultType.
Definition type_data.h:205
const double R1
Definition vnucso.cc:83
const double R2
Definition vnucso.cc:84
const double a1
Definition vnucso.cc:85