MADNESS 0.10.1
tensor.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
32#ifndef MADNESS_TENSOR_TENSOR_H__INCLUDED
33#define MADNESS_TENSOR_TENSOR_H__INCLUDED
34
36#include <madness/misc/ran.h>
39
40#include <memory>
41#include <complex>
42#include <vector>
43#include <array>
44#include <cmath>
45#include <cstdlib>
46#include <cstddef>
47
49// #include <madness/world/print.h>
50//
51// typedef std::complex<float> float_complex;
52// typedef std::complex<double> double_complex;
53//
54// // These probably have to be included in this order
55// #include <madness/tensor/tensor_macros.h>
56// #include <madness/tensor/type_data.h>
57// #include <madness/tensor/slice.h>
58// #include <madness/tensor/vector_factory.h>
61#include <madness/tensor/mxm.h>
64
65#ifdef ENABLE_GENTENSOR
66#define HAVE_GENTENSOR 1
67#else
68#define HAVE_GENTENSOR 0
69#endif
70
71
72/*!
73 \file tensor.h
74 \brief Defines and implements most of Tensor
75 \ingroup tensor
76 \addtogroup tensor
77
78 \par Introduction
79
80 A tensor is a multi-dimensional array and does not incorporate any concepts
81 of covariance and contravariance.
82
83 When a new tensor is created, the underlying data is also allocated.
84 E.g.,
85 \code
86 Tensor<double> a(3,4,5)
87 \endcode
88 creates a new 3-dimensional tensor and allocates a contiguous
89 block of 60 doubles which are initialized to zero. The dimensions
90 (numbered from the left starting at 0) are in C or row-major
91 order. Thus, for the tensor \c a , the stride between successive
92 elements of the right-most dimension is 1. For the middle
93 dimension it is 5. For the left-most dimension it is 20. Thus,
94 the loops
95 \code
96 for (i=0; i<3; ++i)
97 for (j=0; j<4; ++j)
98 for (k=0; k<5; ++k)
99 a(i,j,k) = ...
100 \endcode
101 will go sequentially (and thus efficiently) through memory.
102 If the dimensions have been reordered (e.g., with \c swapdim()
103 or \c map() ), or if the tensor is actually a slice of another
104 tensor, then the layout in memory may be more complex and
105 may not reflect a contiguous block of memory.
106
107 Multiple tensors may be used to provide multiple identical or
108 distinct views of the same data. E.g., in the following
109 \code
110 Tensor<double> a(2,3); // A new tensor initialized to zero
111 Tensor<double> b = a;
112 \endcode
113 \c a and \c b provide identical views of the same data, thus
114 \code
115 b(1,2) = 99;
116 cout << a(1,2) << endl; // Outputs 99
117 cout << b(1,2) << endl; // Outputs 99
118 \endcode
119
120 \par Shallow copy and assignment
121
122 It is important to appreciate that the views and the data are
123 quite independent. In particular, the default copy constructor
124 and assignment operations only copy the tensor (the view) and not
125 the data --- <em> i.e., the copy constructor and assigment operations
126 only take shallow copies</em>. This is for both consistency and
127 efficiency. Thus, assigning one tensor to another generates another
128 view of the same data, replacing any previous view and not moving
129 or copying any of the data.
130 E.g.,
131 \code
132 Tensor<double> a(2,3); // A new tensor initialized to zero
133 Tensor<double> c(3,3,3); // Another new tensor
134 Tensor<double> b = a; // b is a view of the same data as a
135 a = c; // a is now a view of c's data
136 b = c // b is now also a view of c's data and the
137 // data allocated originally for a is freed
138 \endcode
139 The above example also illustrates how reference counting is used
140 to keep track of the underlying data. Once there are no views
141 of the data, it is automatically freed.
142
143 There are only two ways to actually copy the underlying data. A
144 new, complete, and contiguous copy of a tensor and its data may be
145 generated with the \c copy() function. Or, to copy data from one tensor
146 into the data viewed by another tensor, you must use a Slice.
147
148 \par Indexing
149
150 One dimensional tensors (i.e., vectors) may be indexed using
151 either square brackets (e.g., \c v[i] ) or round brackets (e.g.,
152 \c v(i) ). All higher-dimensional tensors must use only round
153 brackets (e.g., \c t(i,j,k) ). This is due to C++'s restriction
154 that the indexing operator (\c [] ) can only have one argument.
155 The indexing operation should generate efficient code.
156
157 For the sake of efficiency, no bounds checking is performed by
158 default by most single element indexing operations. Checking can
159 be enabled at compile time by defining \c -DTENSOR_BOUNDS_CHECKING for
160 application files including \c tensor.h. The MADNESS configure script
161 has the option \c --enable-tensor-bound-checking to define the macro
162 in \c madness_config.h . The general indexing
163 operation that takes a \c std::vector<long> index and all slicing
164 operations always perform bounds checking. To make indexing with
165 checking a bit easier, a factory function has been provided for
166 vectors ... but note you need to explicitly use longs as the
167 index.
168 \code
169 Tensor<long> a(7,7,7);
170 a(3,4,5) += 1; // OK ... adds 1 to element (3,4,5)
171 a(3,4,9) += 1; // BAD ... undetected out-of-bounds access
172 a(vector_factory(3L,4L,9L)) += 1; // OK ... out-bounds access will
173 // be detected at runtime.
174 \endcode
175
176 \par Slicing
177
178 Slices generate sub-tensors --- i.e., views of patches of the
179 data. E.g., to refer to all but the first and last elements in
180 each dimension of a matrix use
181 \code
182 a(Slice(1,-2),Slice(1,-2))
183 \endcode
184 Or to view odd elements in each dimension
185 \code
186 a(Slice(0,-1,2),Slice(0,-1,2))
187 \endcode
188 A slice or patch of a
189 tensor behaves exactly like a tensor \em except for assignment.
190 When a slice is assigned to, the data is copied with the
191 requirement that the source and destinations agree in size and
192 shape (i.e., they conform). Thus, to copy the all of the data
193 from a to b,
194 \code
195 Tensor<double> a(3,4), b(3,4);
196 a = 1; // Set all elements of a to 1
197 b = 2; // Set all elements of b to 2
198 a(Slice(0,-1,1),Slice(0,-1,1)) = b; // Copy all data from b to a
199 a(_,_) = b(_,_); // Copy all data from b to a
200 a(___) = b(___); // Copy all data from b to a
201 a(Slice(1,2),Slice(1,2)) = b; // Error, do not conform
202 \endcode
203 Special slice values \c _ ,\c _reverse, and \c ___ have
204 been defined to refer to all elements in a dimension, all
205 elements in a dimension but reversed, and all elements in all
206 dimensions, respectively.
207
208 \par Iteration and algorithms
209
210 See tensor_macros.h for documentation on the easiest mechanisms for iteration over
211 elements of tensors and tips for optimization. See \c TensorIterator for
212 the most general form of iteration.
213*/
214
215
216#ifndef HAVE_STD_ABS_LONG
217#ifndef HAVE_STD_LABS
218namespace std {
219 static long abs(long a) {
220 return a>=0 ? a : -a;
221 }
222}
223#else
224namespace std {
225 static long abs(long a) {
226 return std::labs(a);
227 }
228}
229#endif
230#endif
231
232
233namespace madness {
234#define IS_ODD(n) ((n)&0x1)
235#define IS_UNALIGNED(p) (((unsigned long)(p))&0x7)
236
237
238 /// For real types return value, for complex return conjugate
239 template <typename Q, bool iscomplex>
241 static Q op(const Q& coeff) {
242 return coeff;
243 }
244 };
245
246 /// For real types return value, for complex return conjugate
247 template <typename Q>
249 static Q op(const Q& coeff) {
250 return conj(coeff);
251 }
252 };
253
254 /// For real types return value, for complex return conjugate
255 template <typename Q>
259
260 namespace detail {
261 template <typename T> T mynorm(T t) {
262 return t*t;
263 }
264 template <typename T=int> double mynorm(int t) {
265 return double(t)*double(t);
266 }
267
268 template <typename T> T mynorm(std::complex<T> t) {
269 return std::norm(t);
270 }
271 }
272
273 template <class T> class SliceTensor;
274
275
276
277 //#define TENSOR_USE_SHARED_ALIGNED_ARRAY
278#ifdef TENSOR_USE_SHARED_ALIGNED_ARRAY
279#define TENSOR_SHARED_PTR detail::SharedAlignedArray
280 // this code has been tested and seems to work correctly on all
281 // tests and moldft ... however initial testing indicated a hard
282 // to measure improvement in thread scaling (from 20 to 60 threads
283 // on cn-mem) and no change in the modest thread count (20)
284 // execution time with tbballoc. hence we are not presently using
285 // it but will test more in the future with different allocators
286 namespace detail {
287 // Minimal ref-counted array with data+counter in one alloc
288 template <typename T> class SharedAlignedArray {
289 T* volatile p;
290 AtomicInt* cnt;
291 void dec() {if (p && ((*cnt)-- == 1)) {free(p); p = 0;}}
292 void inc() {if (p) (*cnt)++;}
293 public:
294 SharedAlignedArray() : p(0), cnt(0) {}
295 T* allocate(std::size_t size, unsigned int alignment) {
296 std::size_t offset = (size*sizeof(T)-1)/sizeof(AtomicInt) + 1; // Where the counter will be
297 std::size_t nbyte = (offset+1)*sizeof(AtomicInt);
298 if (posix_memalign((void **) &p, alignment, nbyte)) throw 1;
299 cnt = (AtomicInt*)(p) + offset;
300 *cnt = 1;
301 return p;
302 }
303 SharedAlignedArray<T>& operator=(const SharedAlignedArray<T>& other) {
304 if (this != &other) {dec(); p = other.p; cnt = other.cnt; inc();}
305 return *this;
306 }
307 void reset() {dec(); p = 0;}
308 ~SharedAlignedArray() {dec();}
309 };
310 }
311#else
312#define TENSOR_SHARED_PTR std::shared_ptr
313#endif
314
315 /// A tensor is a multidimensional array
316
317 /// \ingroup tensor
318 template <class T> class Tensor : public BaseTensor {
319 template <class U> friend class SliceTensor;
320
321 protected:
324
325 void allocate(long nd, const long d[], bool dozero) {
327 if (nd < 0) {
328 _p = 0;
329 _shptr.reset();
330 _size = 0;
331 _ndim = -1;
332 return;
333 }
334
335 TENSOR_ASSERT(nd>0 && nd <= TENSOR_MAXDIM,"invalid ndim in new tensor", nd, 0);
336 // sanity check ... 2GB in doubles
337 for (int i=0; i<nd; ++i) {
338 TENSOR_ASSERT(d[i]>=0 && d[i]<268435456, "invalid dimension size in new tensor",d[i],0);
339 }
341 if (_size) {
342 TENSOR_ASSERT(_size>=0 && _size<268435456, "invalid size in new tensor",_size,0);
343 try {
344#if HAVE_IBMBGP
345#define TENSOR_ALIGNMENT 16
346#elif HAVE_IBMBGQ
347#define TENSOR_ALIGNMENT 32
348#elif MADNESS_HAVE_AVX2
349/* 32B alignment is best for performance according to
350 * http://www.nas.nasa.gov/hecc/support/kb/haswell-processors_492.html */
351#define TENSOR_ALIGNMENT 32
352#elif MADNESS_HAVE_AVX512
353/* One can infer from the AVX2 case that 64B alignment helps with 512b SIMD. */
354#define TENSOR_ALIGNMENT 64
355#else
356// Typical cache line size
357#define TENSOR_ALIGNMENT 64
358#endif
359
360#ifdef TENSOR_USE_SHARED_ALIGNED_ARRAY
361 _p = _shptr.allocate(_size, TENSOR_ALIGNMENT);
362#elif defined WORLD_GATHER_MEM_STATS
363 _p = new T[_size];
364 _shptr = std::shared_ptr<T>(_p);
365#else
366 if (posix_memalign((void **) &_p, TENSOR_ALIGNMENT, sizeof(T)*_size)) throw 1;
367 _shptr.reset(_p, &free);
368#endif
369 }
370 catch (...) {
371 // Ideally use if constexpr here but want headers C++14 for cuda compatibility
373MADNESS_PRAGMA_GCC(diagnostic ignored "-Warray-bounds")
374 std::printf("new failed nd=%ld type=%ld size=%ld\n", nd, id(), _size);
375 std::printf(" %ld %ld %ld %ld %ld %ld\n",
376 d[0], d[1], d[2], d[3], d[4], d[5]);
378 TENSOR_EXCEPTION("new failed",_size,this);
379 }
380 //std::printf("allocated %p [%ld] %ld\n", _p, size, p.use_count());
381 if (dozero) {
382 //T zero = 0; for (long i=0; i<_size; ++i) _p[i] = zero;
383 // or
384#ifdef HAVE_MEMSET
385 memset((void *) _p, 0, _size*sizeof(T));
386#else
388#endif
389 }
390 }
391 else {
392 _p = 0;
393 _shptr.reset();
394 }
395 }
396
397 // Free memory and restore default constructor state
398 void deallocate() {
399 _p = 0;
400 _shptr.reset();
401 _size = 0;
402 _ndim = -1;
403 }
404
405 public:
406 /// C++ typename of this tensor.
407 typedef T type;
408
409 /// C++ typename of the real type associated with a complex type.
411
412 /// C++ typename of the floating point type associated with scalar real type
414
415 /// Default constructor does not allocate any data and sets ndim=-1, size=0, _p=0, and id.
416 Tensor() : _p(0) {
418 }
419
420 /// Copy constructor is shallow (same as assignment)
421
422 /// \em Caveat \em emptor: The shallow copy constructor has many virtues but
423 /// enables you to violate constness with simple code such as
424 /// \code
425 /// const Tensor<double> a(5);
426 /// Tensor<double> b(a);
427 /// b[1] = 3; // a[1] is now also 3
428 /// \endcode
429 Tensor(const Tensor<T>& t) {
431 *this = t;
432 }
433
434 /// Assignment is shallow (same as copy constructor)
435
436 /// \em Caveat \em emptor: The shallow assignment has many virtues but
437 /// enables you to violate constness with simple code such as
438 /// \code
439 /// const Tensor<double> a(5);
440 /// Tensor<double> b;
441 /// b = a;
442 /// b[1] = 3; // a[1] is now also 3
443 /// \endcode
445 if (this != &t) {
446 _p = t._p;
447 _shptr = t._shptr;
448 _size = t._size;
449 _ndim = t._ndim;
451MADNESS_PRAGMA_GCC(diagnostic ignored "-Wmaybe-uninitialized")
452 for (int i=0; i<TENSOR_MAXDIM; ++i) {
453 _dim[i] = t._dim[i];
454 _stride[i] = t._stride[i];
455 }
457 }
458 return *this;
459 }
460
461
462 /// Type conversion makes a deep copy
463 template <class Q> operator Tensor<Q>() const { // type conv => deep copy
464 Tensor<Q> result = Tensor<Q>(this->_ndim,this->_dim,false);
465 BINARY_OPTIMIZED_ITERATOR(Q, result, const T, (*this), *_p0 = (Q)(*_p1));
466 return result;
467 }
468
469
470 /// Create and zero new 1-d tensor
471
472 /// @param[in] d0 Size of dimension 0
473 explicit Tensor(long d0) : _p(0) {
474 _dim[0] = d0;
475 allocate(1, _dim, true);
476 }
477
478 /// Create and zero new 2-d tensor
479
480 /// @param[in] d0 Size of dimension 0
481 /// @param[in] d1 Size of dimension 1
482 explicit Tensor(long d0, long d1) : _p(0) {
483 _dim[0] = d0; _dim[1] = d1;
484 allocate(2, _dim, true);
485 }
486
487 /// Create and zero new 3-d tensor
488
489 /// @param[in] d0 Size of dimension 0
490 /// @param[in] d1 Size of dimension 1
491 /// @param[in] d2 Size of dimension 2
492 explicit Tensor(long d0, long d1, long d2) : _p(0) {
493 _dim[0] = d0; _dim[1] = d1; _dim[2] = d2;
494 allocate(3, _dim, true);
495 }
496
497 /// Create and zero new 4-d tensor
498
499 /// @param[in] d0 Size of dimension 0
500 /// @param[in] d1 Size of dimension 1
501 /// @param[in] d2 Size of dimension 2
502 /// @param[in] d3 Size of dimension 3
503 explicit Tensor(long d0, long d1, long d2, long d3) : _p(0) {
504 _dim[0] = d0; _dim[1] = d1; _dim[2] = d2; _dim[3] = d3;
505 allocate(4, _dim, true);
506 }
507
508 /// Create and zero new 5-d tensor
509
510 /// @param[in] d0 Size of dimension 0
511 /// @param[in] d1 Size of dimension 1
512 /// @param[in] d2 Size of dimension 2
513 /// @param[in] d3 Size of dimension 3
514 /// @param[in] d4 Size of dimension 4
515 explicit Tensor(long d0, long d1, long d2, long d3, long d4) : _p(0) {
516 _dim[0] = d0; _dim[1] = d1; _dim[2] = d2; _dim[3] = d3; _dim[4] = d4;
517 allocate(5, _dim, true);
518 }
519
520 /// Create and zero new 6-d tensor
521
522 /// @param[in] d0 Size of dimension 0
523 /// @param[in] d1 Size of dimension 1
524 /// @param[in] d2 Size of dimension 2
525 /// @param[in] d3 Size of dimension 3
526 /// @param[in] d4 Size of dimension 4
527 /// @param[in] d5 Size of dimension 5
528 explicit Tensor(long d0, long d1, long d2, long d3, long d4, long d5) {
529 _dim[0] = d0; _dim[1] = d1; _dim[2] = d2; _dim[3] = d3; _dim[4] = d4; _dim[5] = d5;
530 allocate(6, _dim, true);
531 }
532
533 /// Create and optionally zero new n-d tensor. This is the most general constructor.
534
535 /// @param[in] d Vector containing size of each dimension, number of dimensions inferred from vector size.
536 /// @param[in] dozero If true (default) the tensor is initialized to zero
537 explicit Tensor(const std::vector<long>& d, bool dozero=true) : _p(0) {
538 allocate(d.size(), d.size() ? &(d[0]) : 0, dozero);
539 }
540
541 /// Politically incorrect general constructor.
542
543 /// @param[in] nd Number of dimensions
544 /// @param[in] d Size of each dimension
545 /// @param[in] dozero If true (default) the tensor is initialized to zero
546 explicit Tensor(long nd, const long d[], bool dozero=true) : _p(0) {
548 }
549
550 /// Inplace fill tensor with scalar
551
552 /// @param[in] x Value used to fill tensor via assigment
553 /// @return %Reference to this tensor
555 UNARY_OPTIMIZED_ITERATOR(T,(*this),*_p0 = x);
556 return *this;
557 }
558
559 /// Inplace fill with a scalar (legacy name)
560
561 /// @param[in] x Value used to fill tensor via assigment
562 /// @return %Reference to this tensor
564 *this = x;
565 return *this;
566 }
567
568 /// Inplace addition of two tensors
569
570 /// @param[in] t Conforming tensor to be added in-place to this tensor
571 /// @return %Reference to this tensor
572 template <typename Q>
574 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, t, *_p0 += *_p1);
575 return *this;
576 }
577
578 /// Inplace subtraction of two tensors
579
580 /// @param[in] t Conforming tensor to be subtracted in-place from this tensor
581 /// @return %Reference to this tensor
582 template <typename Q>
584 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, t, *_p0 -= *_p1);
585 return *this;
586 }
587
588 /// Addition of two tensors to produce a new tensor
589
590 /// @param[in] t Conforming tensor to be added out-of-place to this tensor
591 /// @return New tensor
592 template <typename Q>
594 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
595 Tensor<resultT> result(_ndim,_dim,false);
596 TERNARY_OPTIMIZED_ITERATOR(resultT, result, const T, (*this), const Q, t, *_p0 = *_p1 + *_p2);
597 return result;
598 }
599
600 /// Subtraction of two tensors to produce a new tensor
601
602 /// @param[in] t Conforming tensor to be subtracted out-of-place from this tensor
603 /// @return New tensor
604 template <typename Q>
606 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
607 Tensor<resultT> result(_ndim,_dim,false);
608 TERNARY_OPTIMIZED_ITERATOR(resultT, result, const T, (*this), const Q, t, *_p0 = *_p1 - *_p2);
609 return result;
610 }
611
612 /// Multiplication of tensor by a scalar of a supported type to produce a new tensor
613
614 /// @param[in] x Scalar value
615 /// @return New tensor
616 template <typename Q>
618 operator*(const Q& x) const {
619 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
620 Tensor<resultT> result(_ndim,_dim,false);
621 BINARY_OPTIMIZED_ITERATOR(resultT, result, const T, (*this), *_p0 = *_p1 * x);
622 return result;
623 }
624
625 /// Divide tensor by a scalar of a supported type to produce a new tensor
626
627 /// @param[in] x Scalar value
628 /// @return New tensor
629 template <typename Q>
631 operator/(const Q& x) const {
632 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
633 Tensor<resultT> result(_ndim,_dim);
634 BINARY_OPTIMIZED_ITERATOR(resultT, result, const T, (*this), *_p0 = *_p1 / x);
635 return result;
636 }
637
638 /// Add a scalar of the same type to all elements of a tensor producing a new tensor
639
640 /// @param[in] x Scalar value
641 /// @return New tensor
642 template <typename Q>
644 operator+(const Q& x) const {
645 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
646 Tensor<resultT> result(_ndim,_dim);
647 BINARY_OPTIMIZED_ITERATOR(resultT, result, const T, (*this), *_p0 = *_p1 + x);
648 return result;
649 }
650
651 /// Subtract a scalar of the same type from all elements producing a new tensor
652
653 /// @param[in] x Scalar value
654 /// @return New tensor
655 template <typename Q>
657 operator-(const Q& x) const {
658 return (*this) + (-x);
659 }
660
661 /// Unary negation producing a new tensor
662
663 /// @return New tensor
665 Tensor<T> result = Tensor<T>(_ndim,_dim,false);
666 BINARY_OPTIMIZED_ITERATOR(T, result, const T, (*this), *(_p0) = - (*_p1));
667 return result;
668 }
669
670 /// Inplace multiplication by scalar of supported type
671
672 /// @param[in] x Scalar value
673 /// @return %Reference to this tensor
674 template <typename Q>
676 operator*=(const Q& x) {
677 UNARY_OPTIMIZED_ITERATOR(T, (*this), *_p0 *= x);
678 return *this;
679 }
680
681 /// Inplace multiplication by scalar of supported type (legacy name)
682
683 /// @param[in] x Scalar value
684 /// @return %Reference to this tensor
685 template <typename Q>
687 scale(Q x) {
688 return (*this)*=x;
689 }
690
691 /// Inplace increment by scalar of supported type
692
693 /// @param[in] x Scalar value
694 /// @return %Reference to this tensor
695 template <typename Q>
697 operator+=(const Q& x) {
698 UNARY_OPTIMIZED_ITERATOR(T, (*this), *_p0 += x);
699 return *this;
700 }
701
702 /// Inplace decrement by scalar of supported type
703
704 /// @param[in] x Scalar value
705 /// @return %Reference to this tensor
706 template <typename Q>
708 operator-=(const Q& x) {
709 UNARY_OPTIMIZED_ITERATOR(T, (*this), *_p0 -= x);
710 return *this;
711 }
712
713
714 /// Inplace complex conjugate
715
716 /// @return %Reference to this tensor
718 UNARY_OPTIMIZED_ITERATOR(T, (*this), *_p0 = conditional_conj(*_p0));
719 return *this;
720 }
721
722 /// Inplace fill with random values ( \c [0,1] for floats, \c [0,MAXSIZE] for integers)
723
724 /// @return %Reference to this tensor
726 if (iscontiguous()) {
728 }
729 else {
731 }
732 return *this;
733 }
734
735 /// Inplace fill with the index of each element
736
737 /// Each element is assigned it's logical index according to this loop structure
738 /// \code
739 /// Tensor<float> t(5,6,7,...)
740 /// long index=0;
741 /// for (long i=0; i<_dim[0]; ++i)
742 /// for (long j=0; j<_dim[1]; ++j)
743 /// for (long k=0; k<_dim[2]; ++k)
744 /// ...
745 /// tensor(i,j,k,...) = index++
746 /// \endcode
747 ///
748 /// @return %Reference to this tensor
750 long count = 0;
751 UNARY_UNOPTIMIZED_ITERATOR(T,(*this), *_p0 = count++); // Fusedim would be OK
752 return *this;
753 }
754
755 /// Inplace set elements of \c *this less than \c x in absolute magnitude to zero.
756
757 /// @param[in] x Scalar value
758 /// @return %Reference to this tensor
759 Tensor<T>& screen(double x) {
760 T zero = 0;
761 UNARY_OPTIMIZED_ITERATOR(T,(*this), if (std::abs(*_p0)<x) *_p0=zero);
762 return *this;
763 }
764
765
766 /// Return true if bounds checking was enabled at compile time
767
768 /// @return True if bounds checking was enabled at compile time
769 static bool bounds_checking() {
770#ifdef TENSOR_BOUNDS_CHECKING
771 return true;
772#else
773 return false;
774#endif
775 }
776
777 /// 1-d indexing operation using \c [] \em without bounds checking.
778
779 /// @param[in] i index for dimension 0
780 /// @return %Reference to element
781 T& operator[](long i) {
782#ifdef TENSOR_BOUNDS_CHECKING
783 TENSOR_ASSERT(i>=0 && i<_dim[0],"1d bounds check failed dim=0",i,this);
784#endif
785 return _p[i*_stride[0]];
786 }
787
788 /// 1-d indexing operation using \c [] \em without bounds checking.
789
790 /// @param[in] i index for dimension 0
791 /// @return %Reference to element
792 const T& operator[](long i) const {
793#ifdef TENSOR_BOUNDS_CHECKING
794 TENSOR_ASSERT(i>=0 && i<_dim[0],"1d bounds check failed dim=0",i,this);
795#endif
796 return _p[i*_stride[0]];
797 }
798
799 /// 1-d indexing operation \em without bounds checking.
800
801 /// @param[in] i index for dimension 0
802 /// @return %Reference to element
803 T& operator()(long i) {
804#ifdef TENSOR_BOUNDS_CHECKING
805 TENSOR_ASSERT(i>=0 && i<_dim[0],"1d bounds check failed dim=0",i,this);
806#endif
807 return _p[i*_stride[0]];
808 }
809
810 /// 1-d indexing operation \em without bounds checking.
811
812 /// @param[in] i index for dimension 0
813 /// @return %Reference to element
814 const T& operator()(long i) const {
815#ifdef TENSOR_BOUNDS_CHECKING
816 TENSOR_ASSERT(i>=0 && i<_dim[0],"1d bounds check failed dim=0",i,this);
817#endif
818 return _p[i*_stride[0]];
819 }
820
821 /// 2-d indexing operation \em without bounds checking.
822
823 /// @param[in] i index for dimension 0
824 /// @param[in] j index for dimension 1
825 /// @return %Reference to element
826 T& operator()(long i, long j) {
827#ifdef TENSOR_BOUNDS_CHECKING
828 TENSOR_ASSERT(i>=0 && i<_dim[0],"2d bounds check failed dim=0",i,this);
829 TENSOR_ASSERT(j>=0 && j<_dim[1],"2d bounds check failed dim=1",j,this);
830#endif
831 return _p[i*_stride[0]+j*_stride[1]];
832 }
833
834 /// 2-d indexing operation \em without bounds checking.
835
836 /// @param[in] i index for dimension 0
837 /// @param[in] j index for dimension 1
838 /// @return %Reference to element
839 const T& operator()(long i, long j) const {
840#ifdef TENSOR_BOUNDS_CHECKING
841 TENSOR_ASSERT(i>=0 && i<_dim[0],"2d bounds check failed dim=0",i,this);
842 TENSOR_ASSERT(j>=0 && j<_dim[1],"2d bounds check failed dim=1",j,this);
843#endif
844 return _p[i*_stride[0]+j*_stride[1]];
845 }
846
847 /// 3-d indexing operation \em without bounds checking.
848
849 /// @param[in] i index for dimension 0
850 /// @param[in] j index for dimension 1
851 /// @param[in] k index for dimension 2
852 /// @return %Reference to element
853 T& operator()(long i, long j, long k) {
854#ifdef TENSOR_BOUNDS_CHECKING
855 TENSOR_ASSERT(i>=0 && i<_dim[0],"3d bounds check failed dim=0",i,this);
856 TENSOR_ASSERT(j>=0 && j<_dim[1],"3d bounds check failed dim=1",j,this);
857 TENSOR_ASSERT(k>=0 && k<_dim[2],"3d bounds check failed dim=2",k,this);
858#endif
859 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]];
860 }
861
862 /// 3-d indexing operation \em without bounds checking.
863
864 /// @param[in] i index for dimension 0
865 /// @param[in] j index for dimension 1
866 /// @param[in] k index for dimension 2
867 /// @return %Reference to element
868 const T& operator()(long i, long j, long k) const {
869#ifdef TENSOR_BOUNDS_CHECKING
870 TENSOR_ASSERT(i>=0 && i<_dim[0],"3d bounds check failed dim=0",i,this);
871 TENSOR_ASSERT(j>=0 && j<_dim[1],"3d bounds check failed dim=1",j,this);
872 TENSOR_ASSERT(k>=0 && k<_dim[2],"3d bounds check failed dim=2",k,this);
873#endif
874 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]];
875 }
876
877 /// 4-d indexing operation \em without bounds checking.
878
879 /// @param[in] i index for dimension 0
880 /// @param[in] j index for dimension 1
881 /// @param[in] k index for dimension 2
882 /// @param[in] l index for dimension 3
883 /// @return %Reference to element
884 T& operator()(long i, long j, long k, long l) {
885#ifdef TENSOR_BOUNDS_CHECKING
886 TENSOR_ASSERT(i>=0 && i<_dim[0],"4d bounds check failed dim=0",i,this);
887 TENSOR_ASSERT(j>=0 && j<_dim[1],"4d bounds check failed dim=1",j,this);
888 TENSOR_ASSERT(k>=0 && k<_dim[2],"4d bounds check failed dim=2",k,this);
889 TENSOR_ASSERT(l>=0 && l<_dim[3],"4d bounds check failed dim=3",l,this);
890#endif
891 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]+
892 l*_stride[3]];
893 }
894
895 /// 4-d indexing operation \em without bounds checking.
896
897 /// @param[in] i index for dimension 0
898 /// @param[in] j index for dimension 1
899 /// @param[in] k index for dimension 2
900 /// @param[in] l index for dimension 3
901 /// @return %Reference to element
902 const T& operator()(long i, long j, long k, long l) const {
903#ifdef TENSOR_BOUNDS_CHECKING
904 TENSOR_ASSERT(i>=0 && i<_dim[0],"4d bounds check failed dim=0",i,this);
905 TENSOR_ASSERT(j>=0 && j<_dim[1],"4d bounds check failed dim=1",j,this);
906 TENSOR_ASSERT(k>=0 && k<_dim[2],"4d bounds check failed dim=2",k,this);
907 TENSOR_ASSERT(l>=0 && l<_dim[3],"4d bounds check failed dim=3",l,this);
908#endif
909 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]+
910 l*_stride[3]];
911 }
912
913 /// 5-d indexing operation \em without bounds checking.
914
915 /// @param[in] i index for dimension 0
916 /// @param[in] j index for dimension 1
917 /// @param[in] k index for dimension 2
918 /// @param[in] l index for dimension 3
919 /// @param[in] m index for dimension 4
920 /// @return %Reference to element
921 T& operator()(long i, long j, long k, long l, long m) {
922#ifdef TENSOR_BOUNDS_CHECKING
923 TENSOR_ASSERT(i>=0 && i<_dim[0],"5d bounds check failed dim=0",i,this);
924 TENSOR_ASSERT(j>=0 && j<_dim[1],"5d bounds check failed dim=1",j,this);
925 TENSOR_ASSERT(k>=0 && k<_dim[2],"5d bounds check failed dim=2",k,this);
926 TENSOR_ASSERT(l>=0 && l<_dim[3],"5d bounds check failed dim=3",l,this);
927 TENSOR_ASSERT(m>=0 && m<_dim[4],"5d bounds check failed dim=4",m,this);
928#endif
929 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]+
930 l*_stride[3]+m*_stride[4]];
931 }
932
933 /// 5-d indexing operation \em without bounds checking.
934
935 /// @param[in] i index for dimension 0
936 /// @param[in] j index for dimension 1
937 /// @param[in] k index for dimension 2
938 /// @param[in] l index for dimension 3
939 /// @param[in] m index for dimension 4
940 /// @return %Reference to element
941 const T& operator()(long i, long j, long k, long l, long m) const {
942#ifdef TENSOR_BOUNDS_CHECKING
943 TENSOR_ASSERT(i>=0 && i<_dim[0],"5d bounds check failed dim=0",i,this);
944 TENSOR_ASSERT(j>=0 && j<_dim[1],"5d bounds check failed dim=1",j,this);
945 TENSOR_ASSERT(k>=0 && k<_dim[2],"5d bounds check failed dim=2",k,this);
946 TENSOR_ASSERT(l>=0 && l<_dim[3],"5d bounds check failed dim=3",l,this);
947 TENSOR_ASSERT(m>=0 && m<_dim[4],"5d bounds check failed dim=4",m,this);
948#endif
949 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]+
950 l*_stride[3]+m*_stride[4]];
951 }
952
953 /// 6-d indexing operation \em without bounds checking.
954
955 /// @param[in] i index for dimension 0
956 /// @param[in] j index for dimension 1
957 /// @param[in] k index for dimension 2
958 /// @param[in] l index for dimension 3
959 /// @param[in] m index for dimension 4
960 /// @param[in] n index for dimension 5
961 /// @return %Reference to element
962 T& operator()(long i, long j, long k, long l, long m, long n) {
963#ifdef TENSOR_BOUNDS_CHECKING
964 TENSOR_ASSERT(i>=0 && i<_dim[0],"6d bounds check failed dim=0",i,this);
965 TENSOR_ASSERT(j>=0 && j<_dim[1],"6d bounds check failed dim=1",j,this);
966 TENSOR_ASSERT(k>=0 && k<_dim[2],"6d bounds check failed dim=2",k,this);
967 TENSOR_ASSERT(l>=0 && l<_dim[3],"6d bounds check failed dim=3",l,this);
968 TENSOR_ASSERT(m>=0 && m<_dim[4],"6d bounds check failed dim=4",m,this);
969 TENSOR_ASSERT(n>=0 && n<_dim[5],"6d bounds check failed dim=5",n,this);
970#endif
971 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]+
972 l*_stride[3]+m*_stride[4]+n*_stride[5]];
973 }
974
975 /// 6-d indexing operation \em without bounds checking.
976
977 /// @param[in] i index for dimension 0
978 /// @param[in] j index for dimension 1
979 /// @param[in] k index for dimension 2
980 /// @param[in] l index for dimension 3
981 /// @param[in] m index for dimension 4
982 /// @param[in] n index for dimension 5
983 /// @return %Reference to element
984 const T& operator()(long i, long j, long k, long l, long m, long n) const {
985#ifdef TENSOR_BOUNDS_CHECKING
986 TENSOR_ASSERT(i>=0 && i<_dim[0],"6d bounds check failed dim=0",i,this);
987 TENSOR_ASSERT(j>=0 && j<_dim[1],"6d bounds check failed dim=1",j,this);
988 TENSOR_ASSERT(k>=0 && k<_dim[2],"6d bounds check failed dim=2",k,this);
989 TENSOR_ASSERT(l>=0 && l<_dim[3],"6d bounds check failed dim=3",l,this);
990 TENSOR_ASSERT(m>=0 && m<_dim[4],"6d bounds check failed dim=4",m,this);
991 TENSOR_ASSERT(n>=0 && n<_dim[5],"6d bounds check failed dim=5",n,this);
992#endif
993 return _p[i*_stride[0]+j*_stride[1]+k*_stride[2]+
994 l*_stride[3]+m*_stride[4]+n*_stride[5]];
995 }
996
997 /// Politically incorrect general indexing operation \em without bounds checking.
998
999 /// @param[in] ind Array containing index for each dimension
1000 /// @return %Reference to element
1001 T& operator()(const long ind[]) {
1002 long offset = 0;
1003 for (int d=0; d<_ndim; ++d) {
1004 long i = ind[d];
1005#ifdef TENSOR_BOUNDS_CHECKING
1006 TENSOR_ASSERT(i>=0 && i<_dim[0],"non-PC general indexing bounds check failed dim=",d,this);
1007#endif
1008 offset += i*_stride[d];
1009 }
1010 return _p[offset];
1011 }
1012
1013 /// Politically incorrect general indexing operation \em without bounds checking.
1014
1015 /// @param[in] ind Array containing index for each dimension
1016 /// @return %Reference to element
1017 const T& operator()(const long ind[]) const {
1018 long offset = 0;
1019 for (int d=0; d<_ndim; ++d) {
1020 long i = ind[d];
1021#ifdef TENSOR_BOUNDS_CHECKING
1022 TENSOR_ASSERT(i>=0 && i<_dim[0],"non-PC general indexing bounds check failed dim=",d,this);
1023#endif
1024 offset += i*_stride[d];
1025 }
1026 return _p[offset];
1027 }
1028
1029 /// General indexing operation \em with bounds checking.
1030
1031 /// @param[in] ind Vector containing index for each dimension
1032 /// @return %Reference to element
1033 T& operator()(const std::vector<long> ind) {
1034 TENSOR_ASSERT(ind.size()>=(unsigned int) _ndim,"invalid number of dimensions",ind.size(),this);
1035 long index=0;
1036 for (long d=0; d<_ndim; ++d) {
1037 TENSOR_ASSERT(ind[d]>=0 && ind[d]<_dim[d],"out-of-bounds access",ind[d],this);
1038 index += ind[d]*_stride[d];
1039 }
1040 return _p[index];
1041 }
1042
1043 /// General indexing operation \em with bounds checking.
1044
1045 /// @param[in] ind Vector containing index for each dimension
1046 /// @return %Reference to element
1047 const T& operator()(const std::vector<long> ind) const {
1048 TENSOR_ASSERT(ind.size()>=(unsigned int) _ndim,"invalid number of dimensions",ind.size(),this);
1049 long index=0;
1050 for (long d=0; d<_ndim; ++d) {
1051 TENSOR_ASSERT(ind[d]>=0 && ind[d]<_dim[d],"out-of-bounds access",ind[d],this);
1052 index += ind[d]*_stride[d];
1053 }
1054 return _p[index];
1055 }
1056
1057 /// General slicing operation
1058
1059 /// @param[in] s Vector containing slice for each dimension
1060 /// @return SliceTensor viewing patch of original tensor
1061 SliceTensor<T> operator()(const std::vector<Slice>& s) {
1062 TENSOR_ASSERT(s.size()>=(unsigned)(this->ndim()), "invalid number of dimensions",
1063 this->ndim(),this);
1064 return SliceTensor<T>(*this,&(s[0]));
1065 }
1066
1067 /// General slicing operation (const)
1068
1069 /// @param[in] s Vector containing slice for each dimension
1070 /// @return Constant Tensor viewing patch of original tensor
1071 const Tensor<T> operator()(const std::vector<Slice>& s) const {
1072 TENSOR_ASSERT(s.size()>=(unsigned)(this->ndim()), "invalid number of dimensions",
1073 this->ndim(),this);
1074 return SliceTensor<T>(*this,&(s[0]));
1075 }
1076
1077 /// General slicing operation
1078
1079 /// @param[in] s array containing slice for each dimension
1080 /// @return SliceTensor viewing patch of original tensor
1081 SliceTensor<T> operator()(const std::array<Slice,TENSOR_MAXDIM>& s) {
1082 return SliceTensor<T>(*this,s);
1083 }
1084
1085 /// General slicing operation (const)
1086
1087 /// @param[in] s array containing slice for each dimension
1088 /// @return Constant Tensor viewing patch of original tensor
1089 const Tensor<T> operator()(const std::array<Slice,TENSOR_MAXDIM>& s) const {
1090 return SliceTensor<T>(*this,s);
1091 }
1092
1093 /// Return a 1d SliceTensor that views the specified range of the 1d Tensor
1094
1095 /// @return SliceTensor viewing patch of original tensor
1097 TENSOR_ASSERT(this->ndim()==1,"invalid number of dimensions",
1098 this->ndim(),this);
1099 Slice s[1] = {s0};
1100 return SliceTensor<T>(*this,s);
1101 }
1102
1103 /// Return a 1d SliceTensor that views the specified range of the 1d Tensor
1104
1105 /// @return Constant Tensor viewing patch of original tensor: \f$ R(*,*,\ldots) \rightarrow I(*,*,\ldots) \f$
1106 const Tensor<T> operator()(const Slice& s0) const {
1107 TENSOR_ASSERT(this->ndim()==1,"invalid number of dimensions",
1108 this->ndim(),this);
1109 Slice s[1] = {s0};
1110 return SliceTensor<T>(*this,s);
1111 }
1112
1113 /// Return a 1d SliceTensor that views the specified range of the 2d Tensor
1114
1115 /// @return SliceTensor viewing patch of original tensor: \f$ R(*) \rightarrow I(i,*) \f$
1116 SliceTensor<T> operator()(long i, const Slice& s1) {
1117 TENSOR_ASSERT(this->ndim()==2,"invalid number of dimensions",
1118 this->ndim(),this);
1119 Slice s[2] = {Slice(i,i,0),s1};
1120 return SliceTensor<T>(*this,s);
1121 }
1122
1123 /// Return a 1d SliceTensor that views the specified range of the 2d Tensor
1124
1125 /// @return Constant Tensor viewing patch of original tensor
1126 const Tensor<T> operator()(long i, const Slice& s1) const {
1127 TENSOR_ASSERT(this->ndim()==2,"invalid number of dimensions",
1128 this->ndim(),this);
1129 Slice s[2] = {Slice(i,i,0),s1};
1130 return SliceTensor<T>(*this,s);
1131 }
1132
1133 /// Return a 1d SliceTensor that views the specified range of the 2d Tensor
1134
1135 /// @return SliceTensor viewing patch of original tensor
1137 TENSOR_ASSERT(this->ndim()==2,"invalid number of dimensions",
1138 this->ndim(),this);
1139 Slice s[2] = {s0,Slice(j,j,0)};
1140 return SliceTensor<T>(*this,s);
1141 }
1142
1143 /// Return a 1d constant Tensor that views the specified range of the 2d Tensor
1144
1145 /// @return Constant Tensor viewing patch of original tensor
1146 const Tensor<T> operator()(const Slice& s0, long j) const {
1147 TENSOR_ASSERT(this->ndim()==2,"invalid number of dimensions",
1148 this->ndim(),this);
1149 Slice s[2] = {s0,Slice(j,j,0)};
1150 return SliceTensor<T>(*this,s);
1151 }
1152
1153 /// Return a 2d SliceTensor that views the specified range of the 2d Tensor
1154
1155 /// @return SliceTensor viewing patch of original tensor
1157 TENSOR_ASSERT(this->ndim()==2,"invalid number of dimensions",
1158 this->ndim(),this);
1159 Slice s[2] = {s0,s1};
1160 return SliceTensor<T>(*this,s);
1161 }
1162
1163 /// Return a 2d constant Tensor that views the specified range of the 2d Tensor
1164
1165 /// @return Constant Tensor viewing patch of original tensor
1166 const Tensor<T> operator()(const Slice& s0, const Slice& s1) const {
1167 TENSOR_ASSERT(this->ndim()==2,"invalid number of dimensions",
1168 this->ndim(),this);
1169 Slice s[2] = {s0,s1};
1170 return SliceTensor<T>(*this,s);
1171 }
1172
1173 /// Return a 3d SliceTensor that views the specified range of the 3d Tensor
1174
1175 /// @return SliceTensor viewing patch of original tensor
1176 SliceTensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2) {
1177 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1178 this->ndim(),this);
1179 Slice s[3] = {s0,s1,s2};
1180 return SliceTensor<T>(*this,s);
1181 }
1182
1183 /// Return a 3d constant Tensor that views the specified range of the 3d Tensor
1184
1185 /// @return Constant Tensor viewing patch of original tensor
1186 const Tensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2) const {
1187 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1188 this->ndim(),this);
1189 Slice s[3] = {s0,s1,s2};
1190 return SliceTensor<T>(*this,s);
1191 }
1192
1193 /// Return a 2d SliceTensor that views the specified range of the 3d Tensor
1194
1195 /// @return SliceTensor viewing patch of original tensor
1196 SliceTensor<T> operator()(long i, const Slice& s1, const Slice& s2) {
1197 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1198 this->ndim(),this);
1199 Slice s[3] = {Slice(i,i,0),s1,s2};
1200 return SliceTensor<T>(*this,s);
1201 }
1202
1203 /// Return a 2d constant Tensor that views the specified range of the 3d Tensor
1204
1205 /// @return Constant Tensor viewing patch of original tensor
1206 const Tensor<T> operator()(long i, const Slice& s1, const Slice& s2) const {
1207 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1208 this->ndim(),this);
1209 Slice s[3] = {Slice(i,i,0),s1,s2};
1210 return SliceTensor<T>(*this,s);
1211 }
1212
1213 /// Return a 2d SliceTensor that views the specified range of the 3d Tensor
1214
1215 /// @return SliceTensor viewing patch of original tensor
1216 SliceTensor<T> operator()(const Slice& s0, long j, const Slice& s2) {
1217 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1218 this->ndim(),this);
1219 Slice s[3] = {s0,Slice(j,j,0),s2};
1220 return SliceTensor<T>(*this,s);
1221 }
1222
1223 /// Return a 2d constant Tensor that views the specified range of the 3d Tensor
1224
1225 /// @return Constant Tensor viewing patch of original tensor
1226 const Tensor<T> operator()(const Slice& s0, long j, const Slice& s2) const {
1227 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1228 this->ndim(),this);
1229 Slice s[3] = {s0,Slice(j,j,0),s2};
1230 return SliceTensor<T>(*this,s);
1231 }
1232
1233 /// Return a 2d SliceTensor that views the specified range of the 3d Tensor
1234
1235 /// @return SliceTensor viewing patch of original tensor
1236 SliceTensor<T> operator()(const Slice& s0, const Slice& s1, long k) {
1237 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1238 this->ndim(),this);
1239 Slice s[3] = {s0,s1,Slice(k,k,0)};
1240 return SliceTensor<T>(*this,s);
1241 }
1242
1243 /// Return a 2d constant Tensor that views the specified range of the 3d Tensor
1244
1245 /// @return Constant Tensor viewing patch of original tensor
1246 const Tensor<T> operator()(const Slice& s0, const Slice& s1, long k) const {
1247 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1248 this->ndim(),this);
1249 Slice s[3] = {s0,s1,Slice(k,k,0)};
1250 return SliceTensor<T>(*this,s);
1251 }
1252
1253 /// Return a 1d SliceTensor that views the specified range of the 3d Tensor
1254
1255 /// @return SliceTensor viewing patch of original tensor
1256 SliceTensor<T> operator()(long i, long j, const Slice& s2) {
1257 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1258 this->ndim(),this);
1259 Slice s[3] = {Slice(i,i,0),Slice(j,j,0),s2};
1260 return SliceTensor<T>(*this,s);
1261 }
1262
1263 /// Return a 1d constant Tensor that views the specified range of the 3d Tensor
1264
1265 /// @return Constant Tensor viewing patch of original tensor
1266 const Tensor<T> operator()(long i, long j, const Slice& s2) const {
1267 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1268 this->ndim(),this);
1269 Slice s[3] = {Slice(i,i,0),Slice(j,j,0),s2};
1270 return SliceTensor<T>(*this,s);
1271 }
1272
1273 /// Return a 1d SliceTensor that views the specified range of the 3d Tensor
1274
1275 /// @return SliceTensor viewing patch of original tensor
1276 SliceTensor<T> operator()(long i, const Slice& s1, long k) {
1277 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1278 this->ndim(),this);
1279 Slice s[3] = {Slice(i,i,0),s1,Slice(k,k,0)};
1280 return SliceTensor<T>(*this,s);
1281 }
1282
1283 /// Return a 1d constant Tensor that views the specified range of the 3d Tensor
1284
1285 /// @return Constant Tensor viewing patch of original tensor
1286 const Tensor<T> operator()(long i, const Slice& s1, long k) const {
1287 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1288 this->ndim(),this);
1289 Slice s[3] = {Slice(i,i,0),s1,Slice(k,k,0)};
1290 return SliceTensor<T>(*this,s);
1291 }
1292
1293 /// Return a 1d SliceTensor that views the specified range of the 3d Tensor
1294
1295 /// @return SliceTensor viewing patch of original tensor
1296 SliceTensor<T> operator()(const Slice& s0, long j, long k) {
1297 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1298 this->ndim(),this);
1299 Slice s[3] = {s0,Slice(j,j,0),Slice(k,k,0)};
1300 return SliceTensor<T>(*this,s);
1301 }
1302
1303 /// Return a 1d constant Tensor that views the specified range of the 3d Tensor
1304
1305 /// @return Constant Tensor viewing patch of original tensor
1306 const Tensor<T> operator()(const Slice& s0, long j, long k) const {
1307 TENSOR_ASSERT(this->ndim()==3,"invalid number of dimensions",
1308 this->ndim(),this);
1309 Slice s[3] = {s0,Slice(j,j,0),Slice(k,k,0)};
1310 return SliceTensor<T>(*this,s);
1311 }
1312
1313 /// Return a 1-4d SliceTensor that views the specified range of the 4d Tensor
1314
1315 /// @return SliceTensor viewing patch of original tensor
1316 SliceTensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2,
1317 const Slice& s3) {
1318 TENSOR_ASSERT(this->ndim()==4,"invalid number of dimensions",
1319 this->ndim(),this);
1320 Slice s[4] = {s0,s1,s2,s3};
1321 return SliceTensor<T>(*this,s);
1322 }
1323
1324 /// Return a 1-4d constant Tensor that views the specified range of the 4d Tensor
1325
1326 /// @return Constant Tensor viewing patch of original tensor
1327 const Tensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2,
1328 const Slice& s3) const {
1329 TENSOR_ASSERT(this->ndim()==4,"invalid number of dimensions",
1330 this->ndim(),this);
1331 Slice s[4] = {s0,s1,s2,s3};
1332 return SliceTensor<T>(*this,s);
1333 }
1334
1335 /// Return a 1-5d SliceTensor that views the specified range of the 5d Tensor
1336
1337 /// @return SliceTensor viewing patch of original tensor
1338 SliceTensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2,
1339 const Slice& s3, const Slice& s4) {
1340 TENSOR_ASSERT(this->ndim()==5,"invalid number of dimensions",
1341 this->ndim(),this);
1342 Slice s[5] = {s0,s1,s2,s3,s4};
1343 return SliceTensor<T>(*this,s);
1344 }
1345
1346 /// Return a 1-5d constant Tensor that views the specified range of the 5d Tensor
1347
1348 /// @return Constant Tensor viewing patch of original tensor
1349 const Tensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2,
1350 const Slice& s3, const Slice& s4) const {
1351 TENSOR_ASSERT(this->ndim()==5,"invalid number of dimensions",
1352 this->ndim(),this);
1353 Slice s[5] = {s0,s1,s2,s3,s4};
1354 return SliceTensor<T>(*this,s);
1355 }
1356
1357 /// Return a 1-6d SliceTensor that views the specified range of the 6d Tensor
1358
1359 /// @return SliceTensor viewing patch of original tensor
1360 SliceTensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2,
1361 const Slice& s3, const Slice& s4, const Slice& s5) {
1362 TENSOR_ASSERT(this->ndim()==6,"invalid number of dimensions",
1363 this->ndim(),this);
1364 Slice s[6] = {s0,s1,s2,s3,s4,s5};
1365 return SliceTensor<T>(*this,s);
1366 }
1367
1368
1369 /// Return a 1-6d constant Tensor that views the specified range of the 6d Tensor
1370
1371 /// @return Constant Tensor viewing patch of original tensor
1372 const Tensor<T> operator()(const Slice& s0, const Slice& s1, const Slice& s2,
1373 const Slice& s3, const Slice& s4, const Slice& s5) const {
1374 TENSOR_ASSERT(this->ndim()==6,"invalid number of dimensions",
1375 this->ndim(),this);
1376 Slice s[6] = {s0,s1,s2,s3,s4,s5};
1377 return SliceTensor<T>(*this,s);
1378 }
1379
1380 /// Returns new view/tensor reshaping size/number of dimensions to conforming tensor
1381
1382 /// @param[in] ndimnew Number of dimensions in the result
1383 /// @param[in] d Array containing size of each new dimension
1384 /// @return New tensor (viewing same underlying data as the original but with different shape)
1385 Tensor<T> reshape(int ndimnew, const long* d) {
1386 Tensor<T> result(*this);
1387 result.reshape_inplace(ndimnew,d);
1388 return result;
1389 }
1390
1391 /// Returns new view/tensor reshaping size/number of dimensions to conforming tensor
1392
1393 /// @param[in] ndimnew Number of dimensions in the result
1394 /// @param[in] d Array containing size of each new dimension
1395 /// @return New tensor (viewing same underlying data as the original but with different shape)
1396 const Tensor<T> reshape(int ndimnew, const long* d) const {
1397 Tensor<T> result(*const_cast<Tensor<T>*>(this));
1398 result.reshape_inplace(ndimnew,d);
1399 return result;
1400 }
1401
1402 /// Returns new view/tensor reshaping size/number of dimensions to conforming tensor
1403
1404 /// @param[in] d Array containing size of each new dimension
1405 /// @return New tensor (viewing same underlying data as the original but with different shape)
1406 Tensor<T> reshape(const std::vector<long>& d) {
1407 return reshape(d.size(), d.size() ? &d[0] : 0);
1408 }
1409
1410 /// Returns new view/tensor reshaping size/number of dimensions to conforming tensor
1411
1412 /// @param[in] d Array containing size of each new dimension
1413 /// @return New tensor (viewing same underlying data as the original but with different shape)
1414 const Tensor<T> reshape(const std::vector<long>& d) const {
1415 return reshape(d.size(), d.size() ? &d[0] : 0);
1416 }
1417
1418 /// Returns new view/tensor rehapings to conforming 1-d tensor with given dimension
1419
1420 /// @param[in] dim0 Size of new dimension 0
1421 /// @return New tensor (viewing same underlying data as the original but with different shape)
1423 long d[1] = {dim0};
1424 return reshape(1,d);
1425 }
1426 /// Returns new view/tensor rehapings to conforming 1-d tensor with given dimension
1427
1428 /// @param[in] dim0 Size of new dimension 0
1429 /// @return New tensor (viewing same underlying data as the original but with different shape)
1430 const Tensor<T> reshape(long dim0) const {
1431 long d[1] = {dim0};
1432 return reshape(1,d);
1433 }
1434
1435 /// Returns new view/tensor rehaping to conforming 2-d tensor with given dimensions
1436
1437 /// @param[in] dim0 Size of new dimension 0
1438 /// @param[in] dim1 Size of new dimension 1
1439 /// @return New tensor (viewing same underlying data as the original but with different shape)
1441 long d[2] = {dim0,dim1};
1442 return reshape(2,d);
1443 }
1444
1445 /// Returns new view/tensor rehaping to conforming 2-d tensor with given dimensions
1446
1447 /// @param[in] dim0 Size of new dimension 0
1448 /// @param[in] dim1 Size of new dimension 1
1449 /// @return New tensor (viewing same underlying data as the original but with different shape)
1450 const Tensor<T> reshape(long dim0, long dim1) const {
1451 long d[2] = {dim0,dim1};
1452 return reshape(2,d);
1453 }
1454
1455 /// Returns new view/tensor rehaping to conforming 3-d tensor with given dimensions
1456
1457 /// @param[in] dim0 Size of new dimension 0
1458 /// @param[in] dim1 Size of new dimension 1
1459 /// @param[in] dim2 Size of new dimension 2
1460 /// @return New tensor (viewing same underlying data as the original but with different shape)
1461 Tensor<T> reshape(long dim0, long dim1, long dim2) {
1462 long d[3] = {dim0,dim1,dim2};
1463 return reshape(3,d);
1464 }
1465
1466 /// Returns new view/tensor rehaping to conforming 3-d tensor with given dimensions
1467
1468 /// @param[in] dim0 Size of new dimension 0
1469 /// @param[in] dim1 Size of new dimension 1
1470 /// @param[in] dim2 Size of new dimension 2
1471 /// @return New tensor (viewing same underlying data as the original but with different shape)
1472 const Tensor<T> reshape(long dim0, long dim1, long dim2) const {
1473 long d[3] = {dim0,dim1,dim2};
1474 return reshape(3,d);
1475 }
1476
1477 /// Returns new view/tensor rehaping to conforming 4-d tensor with given dimensions
1478
1479 /// @param[in] dim0 Size of new dimension 0
1480 /// @param[in] dim1 Size of new dimension 1
1481 /// @param[in] dim2 Size of new dimension 2
1482 /// @param[in] dim3 Size of new dimension 3
1483 /// @return New tensor (viewing same underlying data as the original but with different shape)
1484 Tensor<T> reshape(long dim0, long dim1, long dim2, long dim3) {
1485 long d[4] = {dim0,dim1,dim2,dim3};
1486 return reshape(4,d);
1487 }
1488
1489 /// Returns new view/tensor rehaping to conforming 4-d tensor with given dimensions
1490
1491 /// @param[in] dim0 Size of new dimension 0
1492 /// @param[in] dim1 Size of new dimension 1
1493 /// @param[in] dim2 Size of new dimension 2
1494 /// @param[in] dim3 Size of new dimension 3
1495 /// @return New tensor (viewing same underlying data as the original but with different shape)
1496 const Tensor<T> reshape(long dim0, long dim1, long dim2, long dim3) const {
1497 long d[4] = {dim0,dim1,dim2,dim3};
1498 return reshape(4,d);
1499 }
1500
1501 /// Returns new view/tensor rehaping to conforming 5-d tensor with given dimensions
1502
1503 /// @param[in] dim0 Size of new dimension 0
1504 /// @param[in] dim1 Size of new dimension 1
1505 /// @param[in] dim2 Size of new dimension 2
1506 /// @param[in] dim3 Size of new dimension 3
1507 /// @param[in] dim4 Size of new dimension 4
1508 /// @return New tensor (viewing same underlying data as the original but with different shape)
1509 Tensor<T> reshape(long dim0, long dim1, long dim2, long dim3, long dim4) {
1510 long d[5] = {dim0,dim1,dim2,dim3,dim4};
1511 return reshape(5,d);
1512 }
1513
1514 /// Returns new view/tensor rehaping to conforming 5-d tensor with given dimensions
1515
1516 /// @param[in] dim0 Size of new dimension 0
1517 /// @param[in] dim1 Size of new dimension 1
1518 /// @param[in] dim2 Size of new dimension 2
1519 /// @param[in] dim3 Size of new dimension 3
1520 /// @param[in] dim4 Size of new dimension 4
1521 /// @return New tensor (viewing same underlying data as the original but with different shape)
1522 const Tensor<T> reshape(long dim0, long dim1, long dim2, long dim3, long dim4) const {
1523 long d[5] = {dim0,dim1,dim2,dim3,dim4};
1524 return reshape(5,d);
1525 }
1526
1527 /// Returns new view/tensor rehaping to conforming 6-d tensor with given dimensions
1528
1529 /// @param[in] dim0 Size of new dimension 0
1530 /// @param[in] dim1 Size of new dimension 1
1531 /// @param[in] dim2 Size of new dimension 2
1532 /// @param[in] dim3 Size of new dimension 3
1533 /// @param[in] dim4 Size of new dimension 4
1534 /// @param[in] dim5 Size of new dimension 5
1535 /// @return New tensor (viewing same underlying data as the original but with different shape)
1536 Tensor<T> reshape(long dim0, long dim1, long dim2, long dim3, long dim4, long dim5) {
1537 long d[6] = {dim0,dim1,dim2,dim3,dim4,dim5};
1538 return reshape(6,d);
1539 }
1540
1541 /// Returns new view/tensor rehaping to conforming 6-d tensor with given dimensions
1542
1543 /// @param[in] dim0 Size of new dimension 0
1544 /// @param[in] dim1 Size of new dimension 1
1545 /// @param[in] dim2 Size of new dimension 2
1546 /// @param[in] dim3 Size of new dimension 3
1547 /// @param[in] dim4 Size of new dimension 4
1548 /// @param[in] dim5 Size of new dimension 5
1549 /// @return New tensor (viewing same underlying data as the original but with different shape)
1550 const Tensor<T> reshape(long dim0, long dim1, long dim2, long dim3, long dim4, long dim5) const {
1551 long d[6] = {dim0,dim1,dim2,dim3,dim4,dim5};
1552 return reshape(6,d);
1553 }
1554
1555 /// Returns new view/tensor rehshaping to flat (1-d) tensor
1557 long d[1] = {_size};
1558 return reshape(1,d);
1559 }
1560
1561 /// Returns new view/tensor rehshaping to flat (1-d) tensor
1562 const Tensor<T> flat() const {
1563 long d[1] = {_size};
1564 return reshape(1,d);
1565 }
1566
1567 /// Returns new view/tensor splitting dimension \c i as \c dimi0*dimi1 to produce conforming d+1 dimension tensor
1568
1569 /// @return New tensor (viewing same underlying data as the original but with additional dimensions)
1570 Tensor<T> splitdim(long i, long dimi0, long dimi1) {
1571 Tensor<T> result(*this);
1572 result.splitdim_inplace(i, dimi0, dimi1);
1573 return result;
1574 }
1575
1576 /// Returns new view/tensor splitting dimension \c i as \c dimi0*dimi1 to produce conforming d+1 dimension tensor
1577
1578 /// @return New tensor (viewing same underlying data as the original but with additional dimensions)
1579 const Tensor<T> splitdim(long i, long dimi0, long dimi1) const {
1580 Tensor<T> result(*const_cast<Tensor<T>*>(this));
1581 result.splitdim_inplace(i, dimi0, dimi1);
1582 return result;
1583 }
1584
1585 /// Returns new view/tensor fusing contiguous dimensions \c i and \c i+1
1586
1587 /// @return New tensor (viewing same underlying data as the original but with fewer dimensions)
1589 Tensor<T> result(*this);
1590 result.fusedim_inplace(i);
1591 return result;
1592 }
1593
1594 /// Returns new view/tensor fusing contiguous dimensions \c i and \c i+1
1595
1596 /// @return New tensor (viewing same underlying data as the original but with fewer dimensions)
1597 const Tensor<T> fusedim(long i) const {
1598 Tensor<T> result(*const_cast<Tensor<T>*>(this));
1599 result.fusedim_inplace(i);
1600 return result;
1601 }
1602
1603 /// Returns new view/tensor swaping dimensions \c i and \c j
1604
1605 /// @return New tensor (viewing same underlying data as the original but with reordered dimensions)
1606 Tensor<T> swapdim(long idim, long jdim) {
1607 Tensor<T> result(*this);
1608 result.swapdim_inplace(idim, jdim);
1609 return result;
1610 }
1611
1612 /// Returns new view/tensor swaping dimensions \c i and \c j
1613
1614 /// @return New tensor (viewing same underlying data as the original but with reordered dimensions)
1615 const Tensor<T> swapdim(long idim, long jdim) const {
1616 Tensor<T> result(*const_cast<Tensor<T>*>(this));
1617 result.swapdim_inplace(idim, jdim);
1618 return result;
1619 }
1620
1621 /// Returns new view/tensor permuting the dimensions
1622
1623 /// @param[in] map Old dimension i becomes new dimension \c map[i]
1624 /// @return New tensor (viewing same underlying data as the original but with reordered dimensions)
1625 Tensor<T> mapdim(const std::vector<long>& map) {
1626 Tensor<T> result(*this);
1627 result.mapdim_inplace(map);
1628 return result;
1629 }
1630
1631 /// Returns new view/tensor permuting the dimensions
1632
1633 /// @return New tensor (viewing same underlying data as the original but with reordered dimensions)
1634 const Tensor<T> mapdim(const std::vector<long>& map) const {
1635 Tensor<T> result(*const_cast<Tensor<T>*>(this));
1636 result.mapdim_inplace(map);
1637 return result;
1638 }
1639
1640
1641 /// Returns new view/tensor cycling the sub-dimensions `(start,...,end)` with `shift` steps
1642 Tensor<T> cycledim(long nshift, long start, long end) {
1643 Tensor<T> result(*this);
1644 result.cycledim_inplace(nshift, start, end);
1645 return result;
1646 }
1647
1648
1649 /// Returns new view/tensor cycling the sub-dimensions `(start,...,end)` with `shift` steps
1650 const Tensor<T> cycledim(long nshift, long start, long end) const {
1651 Tensor<T> result(*const_cast<Tensor<T>*>(this));
1652 result.cycledim_inplace(nshift, start, end);
1653 return result;
1654 }
1655
1656
1657 /// Test if \c *this and \c t conform.
1658 template <class Q> bool conforms(const Tensor<Q>& t) const {
1659 return BaseTensor::conforms(&t);
1660 }
1661
1662 /// Returns the sum of all elements of the tensor
1663 T sum() const {
1664 T result = 0;
1665 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result += *_p0);
1666 return result;
1667 }
1668
1669 /// Returns the sum of the squares of the elements
1670 T sumsq() const {
1671 T result = 0;
1672 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result += (*_p0) * (*_p0));
1673 return result;
1674 }
1675
1676 /// Return the product of all elements of the tensor
1677 T product() const {
1678 T result = 1;
1679 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result *= *_p0);
1680 return result;
1681 }
1682
1683 /// Return the minimum value (and if ind is non-null, its index) in the Tensor
1684 T min(long* ind=0) const {
1685 T result = *(this->_p);
1686 if (ind) {
1687 for (long i=0; i<_ndim; ++i) ind[i]=0;
1688 long nd = _ndim-1;
1689 UNARY_UNOPTIMIZED_ITERATOR(const T,(*this),
1690 if (result > *_p0) {
1691 result = *_p0;
1692 for (long i=0; i<nd; ++i) ind[i]=iter.ind[i];
1693 ind[nd] = _j;
1694 }
1695 );
1696 }
1697 else {
1698 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result=std::min<T>(result,*_p0));
1699 }
1700 return result;
1701 }
1702
1703 /// Return the maximum value (and if ind is non-null, its index) in the Tensor
1704 T max(long* ind=0) const {
1705 T result = *(this->_p);
1706 if (ind) {
1707 for (long i=0; i<_ndim; ++i) ind[i]=0;
1708 long nd = _ndim-1;
1709 UNARY_UNOPTIMIZED_ITERATOR(const T,(*this),
1710 if (result < *_p0) {
1711 result = *_p0;
1712 for (long i=0; i<nd; ++i) ind[i]=iter.ind[i];
1713 ind[nd] = _j;
1714 }
1715 );
1716 }
1717 else {
1718 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result=std::max<T>(result,*_p0));
1719 }
1720 return result;
1721 }
1722
1723 // For complex types, this next group returns the appropriate real type
1724 // For real types, the same type as T is returned (type_data.h)
1725
1726 /// Returns the Frobenius norm of the tensor
1728 float_scalar_type result = 0;
1729 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result += ::madness::detail::mynorm(*_p0));
1730 return (float_scalar_type) std::sqrt(result);
1731 }
1732
1733 /// Return the absolute minimum value (and if ind is non-null, its index) in the Tensor
1734 scalar_type absmin(long *ind = 0) const {
1735 scalar_type result = std::abs(*(this->_p));
1736 if (ind) {
1737 for (long i=0; i<_ndim; ++i) ind[i]=0;
1738 long nd = _ndim-1;
1739 UNARY_UNOPTIMIZED_ITERATOR(const T,(*this),
1740 scalar_type absval = std::abs(*_p0);
1741 if (result > absval) {
1742 result = absval;
1743 for (long i=0; i<nd; ++i) ind[i]=iter.ind[i];
1744 ind[nd] = _j;
1745 }
1746 );
1747 }
1748 else {
1749 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result=std::min<scalar_type>(result,std::abs(*_p0)));
1750 }
1751 return result;
1752 }
1753
1754 /// Return the absolute maximum value (and if ind is non-null, its index) in the Tensor
1755 scalar_type absmax(long *ind = 0) const {
1756 scalar_type result = std::abs(*(this->_p));
1757 if (ind) {
1758 for (long i=0; i<_ndim; ++i) ind[i]=0;
1759 long nd = _ndim-1;
1761 scalar_type absval = std::abs(*_p0);
1762 if (result < absval) {
1763 result = absval;
1764 for (long i=0; i<nd; ++i) ind[i]=iter.ind[i];
1765 ind[nd] = _j;
1766 }
1767 );
1768 }
1769 else {
1770 UNARY_OPTIMIZED_ITERATOR(const T,(*this),result=std::max<scalar_type>(result,std::abs(*_p0)));
1771 }
1772 return result;
1773 }
1774
1775
1776 /// Return the trace of two tensors (no complex conjugate invoked)
1777 template <class Q>
1778 TENSOR_RESULT_TYPE(T,Q) trace(const Tensor<Q>& t) const {
1779 TENSOR_RESULT_TYPE(T,Q) result = 0;
1780 BINARY_OPTIMIZED_ITERATOR(const T,(*this),const Q,t,result += (*_p0)*(*_p1));
1781 return result;
1782 }
1783
1784 /// Return the trace of two tensors with complex conjugate of the leftmost (i.e., this)
1785 template <class Q>
1786 TENSOR_RESULT_TYPE(T,Q) trace_conj(const Tensor<Q>& t) const {
1787 TENSOR_RESULT_TYPE(T,Q) result = 0;
1788 BINARY_OPTIMIZED_ITERATOR(const T,(*this),const Q,t,result += conditional_conj(*_p0)*(*_p1));
1789 return result;
1790 }
1791
1792 /// Inplace apply a unary function to each element of the tensor
1793 template <typename opT>
1795 UNARY_OPTIMIZED_ITERATOR(T,(*this),*_p0=op(*_p0));
1796 return *this;
1797 }
1798
1799 /// Inplace multiply by corresponding elements of argument Tensor
1801 BINARY_OPTIMIZED_ITERATOR(T,(*this),const T,t,*_p0 *= *_p1);
1802 return *this;
1803 }
1804
1805 /// Inplace generalized saxpy ... this = this*alpha + other*beta
1806 Tensor<T>& gaxpy(T alpha, const Tensor<T>& other, T beta) {
1807 if (alpha == T(1)) {
1808 if (beta == T(1)) {
1809 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) += (*_p1));
1810 }
1811 else if (beta == T(0)) {
1812 // noop
1813 }
1814 else {
1815 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) += beta * (*_p1));
1816 }
1817 }
1818 else if (alpha == T(0)) {
1819 if (beta == T(1)) {
1820 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) = (*_p1));
1821 }
1822 else if (beta == T(0)) {
1823 *this = T(0);
1824 }
1825 else {
1826 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) = beta * (*_p1));
1827 }
1828 } else {
1829 if (beta == T(1)) {
1830 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) = alpha * (*_p0) + (*_p1));
1831 } else if (beta == T(0)) {
1832 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) = alpha * (*_p0));
1833 } else {
1834 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, other, (*_p0) = alpha * (*_p0) + beta * (*_p1));
1835 }
1836 }
1837 return *this;
1838 }
1839
1840 /// Returns a pointer to the internal data
1841 T* ptr() {
1842 return _p;
1843 }
1844
1845 /// Returns a pointer to the internal data
1846 const T* ptr() const {
1847 return _p;
1848 }
1849
1850 /// Returns a pointer to the base class
1852 return static_cast<BaseTensor*>(this);
1853 }
1854
1855 /// Returns a pointer to the base class
1856 const BaseTensor* base() const {
1857 return static_cast<const BaseTensor*>(this);
1858 }
1859
1860 /// Return iterator over single tensor
1862 bool optimize=true,
1863 bool fusedim=true,
1864 long jdim=default_jdim) const {
1865 return TensorIterator<T>(this,(const Tensor<T>*) 0, (const Tensor<T>*) 0,
1866 iterlevel, optimize, fusedim, jdim);
1867 }
1868
1869 /// Return iterator over two tensors
1870 template <class Q>
1872 long iterlevel=0,
1873 bool optimize=true,
1874 bool fusedim=true,
1875 long jdim=default_jdim) const {
1876 return TensorIterator<T,Q>(this,&q,(const Tensor<T>*) 0,
1877 iterlevel, optimize, fusedim, jdim);
1878 }
1879
1880 /// Return iterator over three tensors
1881 template <class Q, class R>
1883 const Tensor<R>& r,
1884 long iterlevel=0,
1885 bool optimize=true,
1886 bool fusedim=true,
1887 long jdim=default_jdim) const {
1888 return TensorIterator<T,Q,R>(this,&q,&r,
1889 iterlevel, optimize, fusedim, jdim);
1890 }
1891
1892 /// End point for forward iteration
1893 const TensorIterator<T>& end() const {
1894 static TensorIterator<T> theend(0,0,0,0,0,0);
1895 return theend;
1896 }
1897
1898 virtual ~Tensor() {}
1899
1900 /// Frees all memory and resests to state of default constructor
1901 void clear() {deallocate();}
1902
1903 bool has_data() const {return size()!=0;};
1904
1905 };
1906
1907 template <class T>
1908 std::ostream& operator << (std::ostream& out, const Tensor<T>& t);
1909
1910
1911 namespace archive {
1912 /// Serialize a tensor
1913 template <class Archive, typename T>
1915 static void store(const Archive& s, const Tensor<T>& t) {
1916 if (t.iscontiguous()) {
1917 s & t.size() & t.id();
1918 if (t.size()) s & t.ndim() & wrap(t.dims(),TENSOR_MAXDIM) & wrap(t.ptr(),t.size());
1919 }
1920 else {
1921 s & copy(t);
1922 }
1923 };
1924 };
1925
1926
1927 /// Deserialize a tensor ... existing tensor is replaced
1928 template <class Archive, typename T>
1930 static void load(const Archive& s, Tensor<T>& t) {
1931 long sz = 0l, id = 0l;
1932 s & sz & id;
1933 if (id != t.id()) throw "type mismatch deserializing a tensor";
1934 if (sz) {
1935 long _ndim = 0l, _dim[TENSOR_MAXDIM];
1936 s & _ndim & wrap(_dim,TENSOR_MAXDIM);
1937 t = Tensor<T>(_ndim, _dim, false);
1938 if (sz != t.size()) throw "size mismatch deserializing a tensor";
1939 s & wrap(t.ptr(), t.size());
1940 }
1941 else {
1942 t = Tensor<T>();
1943 }
1944 };
1945 };
1946
1947 }
1948
1949 /// The class defines tensor op scalar ... here define scalar op tensor.
1950
1951 /// \ingroup tensor
1952 template <typename T, typename Q>
1954 operator+(Q x, const Tensor<T>& t) {
1955 return t+x;
1956 }
1957
1958 /// The class defines tensor op scalar ... here define scalar op tensor.
1959
1960 /// \ingroup tensor
1961 template <typename T, typename Q>
1963 operator*(const Q& x, const Tensor<T>& t) {
1964 return t*x;
1965 }
1966
1967 /// The class defines tensor op scalar ... here define scalar op tensor.
1968
1969 /// \ingroup tensor
1970 template <typename T, typename Q>
1972 operator-(Q x, const Tensor<T>& t) {
1973 return (-t)+=x;
1974 }
1975
1976 /// Returns a new contiguous tensor that is a deep copy of the input
1977
1978 /// \ingroup tensor
1979 /// @result Returns a new contiguous tensor that is a deep copy of the input
1980 template <class T> Tensor<T> copy(const Tensor<T>& t) {
1981 if (t.size()) {
1982 Tensor<T> result = Tensor<T>(t.ndim(),t.dims(),false);
1983 BINARY_OPTIMIZED_ITERATOR(T, result, const T, t, *_p0 = *_p1);
1984 return result;
1985 }
1986 else {
1987 return Tensor<T>();
1988 }
1989 }
1990
1991 /// Returns a new contiguous tensor of type Q that is a deep copy of the input
1992
1993 /// \ingroup tensor
1994 /// @result Returns a new contiguous tensor that is a deep copy of the input
1995 template <class Q, class T>
1997 if (t.size()) {
1998 Tensor<Q> result = Tensor<Q>(t.ndim(),t.dims(),false);
1999 BINARY_OPTIMIZED_ITERATOR(Q, result, const T, t, *_p0 = *_p1);
2000 return result;
2001 }
2002 else {
2003 return Tensor<Q>();
2004 }
2005 }
2006
2007
2008 /// Transforms one dimension of the tensor t by the matrix c, returns new contiguous tensor
2009
2010 /// \ingroup tensor
2011 /// \code
2012 /// transform_dir(t,c,1) = r(i,j,k,...) = sum(j') t(i,j',k,...) * c(j',j)
2013 /// \endcode
2014 /// @param[in] t Tensor to transform (size of dimension to be transformed must match size of first dimension of \c c )
2015 /// @param[in] c Matrix used for the transformation
2016 /// @param[in] axis Dimension (or axis) to be transformed
2017 /// @result Returns a new, contiguous tensor
2018 template <class T, class Q>
2020 if (axis == 0) {
2021 return inner(c,t,0,axis);
2022 }
2023 else if (axis == t.ndim()-1) {
2024 return inner(t,c,axis,0);
2025 }
2026 else {
2027 return copy(inner(t,c,axis,0).cycledim(1,axis, -1)); // Copy to make contiguous
2028 }
2029 }
2030
2031 /// Returns a new deep copy of the transpose of the input tensor
2032
2033 /// \ingroup tensor
2034 template <class T>
2036 TENSOR_ASSERT(t.ndim() == 2, "transpose requires a matrix", t.ndim(), &t);
2037 return copy(t.swapdim(0,1));
2038 }
2039
2040 /// Returns a new deep copy of the complex conjugate transpose of the input tensor
2041
2042 /// \ingroup tensor
2043 template <class T>
2045 TENSOR_ASSERT(t.ndim() == 2, "conj_transpose requires a matrix", t.ndim(), &t);
2046 return conj(t.swapdim(0,1));
2047 }
2048
2049 /// Indexing a non-constant tensor with slices returns a SliceTensor
2050
2051 /// \ingroup tensor
2052 /// A slice tensor differs from a tensor only in that assignment
2053 /// causes the data to be copied rather than entire new copy
2054 /// generated. You will usually not instantiate one except as a
2055 /// temporary produced by indexing a tensor with slice and then
2056 /// assigning it back to a tensor, or performing some other
2057 /// operation and discarding.
2058 template <class T> class SliceTensor : public Tensor<T> {
2059 private:
2061
2062 public:
2063
2064 // delegating constructor
2065 SliceTensor(const Tensor<T>& t, const std::array<Slice,TENSOR_MAXDIM> s)
2066 : SliceTensor(t,s.data()) {}
2067
2068
2069 SliceTensor(const Tensor<T>& t, const Slice s[])
2070 : Tensor<T>(const_cast<Tensor<T>&>(t)) //!!!!!!!!!!!
2071 {
2072 // C++ standard says class derived from parameterized base class cannot
2073 // directly access the base class elements ... must explicitly reference.
2074
2075 long nd = 0, size=1;
2076 for (long i=0; i<t._ndim; ++i) {
2077 long start=s[i].start, end=s[i].end, step=s[i].step;
2078 //std::printf("%ld input start=%ld end=%ld step=%ld\n",
2079 //i, start, end, step);
2080 if (start < 0) start += this->_dim[i];
2081 if (end < 0) end += this->_dim[i];
2082 long len = end-start+1;
2083 if (step) len /= step; // Rounds len towards zero
2084
2085 // if input length is not exact multiple of step, round end towards start
2086 // for the same behaviour of for (i=start; i<=end; i+=step);
2087 end = start + (len-1)*step;
2088
2089 //std::printf("%ld munged start=%ld end=%ld step=%ld len=%ld _dim=%ld\n",
2090 // i, start, end, step, len, this->_dim[i]);
2091
2092 TENSOR_ASSERT(start>=0 && start<this->_dim[i],"slice start invalid",start,this);
2093 TENSOR_ASSERT(end>=0 && end<this->_dim[i],"slice end invalid",end,this);
2094 TENSOR_ASSERT(len>0,"slice length must be non-zero",len,this);
2095
2096 this->_p += start * t._stride[i];
2097
2098 if (step) {
2099 size *= len;
2100 this->_dim[nd] = len;
2101 this->_stride[nd] = step * t._stride[i];
2102 ++nd;
2103 }
2104 }
2105 //For Python interface need to be able to return a scalar inside a tensor with nd=0
2106 //TENSOR_ASSERT(nd>0,"slicing produced a scalar, but cannot return one",nd,this);
2107 for (long i=nd; i<TENSOR_MAXDIM; ++i) { // So can iterate over missing dimensions
2108 this->_dim[i] = 1;
2109 this->_stride[i] = 0;
2110 }
2111
2112 this->_ndim = nd;
2113 this->_size = size;
2114 }
2115
2117 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, t, *_p0 = (T)(*_p1));
2118 return *this;
2119 }
2120
2121 template <class Q>
2123 BINARY_OPTIMIZED_ITERATOR(T, (*this), const Q, t, *_p0 = (T)(*_p1));
2124 return *this;
2125 }
2126
2128 BINARY_OPTIMIZED_ITERATOR(T, (*this), const T, t, *_p0 = (T)(*_p1));
2129 return *this;
2130 }
2131
2132 template <class Q>
2134 BINARY_OPTIMIZED_ITERATOR(T, (*this), const Q, t, *_p0 = (T)(*_p1));
2135 return *this;
2136 }
2137
2139 UNARY_OPTIMIZED_ITERATOR(T, (*this), *_p0 = t);
2140 return *this;
2141 }
2142
2143 virtual ~SliceTensor() {}; // Tensor<T> destructor does enough
2144 };
2145
2146
2147 // Specializations for complex types
2148 template<> float_complex Tensor<float_complex>::min(long* ind) const ;
2149 template<> double_complex Tensor<double_complex>::min(long* ind) const ;
2150 template<> float_complex Tensor<float_complex>::max(long* ind) const ;
2151 template<> double_complex Tensor<double_complex>::max(long* ind) const ;
2152
2153 // Stream stuff
2154
2155 /// Print (for human consumption) a tensor to the stream
2156
2157 /// \ingroup tensor
2158 template <class T>
2159 std::ostream& operator << (std::ostream& s, const Tensor<T>& t) {
2160 if (t.size() == 0) {
2161 s << "[empty tensor]\n";
2162 return s;
2163 }
2164
2165 long maxdim = 0;
2166 long index_width = 0;
2167 for (int i = 0; i<(t.ndim()-1); ++i) {
2168 if (maxdim < t.dim(i)) maxdim = t.dim(i);
2169 }
2170 if (maxdim < 10)
2171 index_width = 1;
2172 else if (maxdim < 100)
2173 index_width = 2;
2174 else if (maxdim < 1000)
2175 index_width = 3;
2176 else if (maxdim < 10000)
2177 index_width = 4;
2178 else
2179 index_width = 6;
2180
2181 std::ios::fmtflags oldflags = s.setf(std::ios::scientific);
2182 long oldprec = s.precision();
2183 long oldwidth = s.width();
2184
2185 // C++ formatted IO is worse than Fortran !!
2186 for (TensorIterator<T> iter=t.unary_iterator(1,false,false); iter!=t.end(); ++iter) {
2187 const T* p = iter._p0;
2188 long inc = iter._s0;
2189 long dimj = iter.dimj;
2190 s.unsetf(std::ios::scientific);
2191 s << '[';
2192 for (long i=0; i<iter.ndim; ++i) {
2193 s.width(index_width);
2194 s << iter.ind[i];
2195 //if (i != iter.ndim)
2196 s << ",";
2197 }
2198 s << "*]";
2199//flo s.setf(std::ios::scientific);
2200 s.setf(std::ios::fixed);
2201 for (long j=0; j<dimj; ++j, p+=inc) {
2202//flo s.precision(4);
2203 s << " ";
2204 s.precision(8);
2205 s.width(12);
2206 s << *p;
2207 }
2208 s.unsetf(std::ios::scientific);
2209 s << std::endl;
2210 }
2211 s.setf(oldflags,std::ios::floatfield);
2212 s.precision(oldprec);
2213 s.width(oldwidth);
2214
2215 return s;
2216 }
2217
2218
2219 /// Outer product ... result(i,j,...,p,q,...) = left(i,k,...)*right(p,q,...)
2220
2221 /// \ingroup tensor
2222 template <class T>
2223 Tensor<T> outer(const Tensor<T>& left, const Tensor<T>& right) {
2224 long nd = left.ndim() + right.ndim();
2225 TENSOR_ASSERT(nd <= TENSOR_MAXDIM, "too many dimensions in result",
2226 nd, 0);
2227 long d[TENSOR_MAXDIM];
2228 for (long i = 0; i < left.ndim(); ++i) d[i] = left.dim(i);
2229 for (long i = 0; i < right.ndim(); ++i) d[i + left.ndim()] = right.dim(i);
2230 Tensor<T> result(nd, d, false);
2231 outer_result(left,right,result);
2232 return result;
2233 }
2234
2235 /// Outer product ... result(i,j,...,p,q,...) = left(i,k,...)*right(p,q,...)
2236
2237 /// accumulate into result, no allocation is performed
2238 template<class T>
2239 void outer_result(const Tensor<T>& left, const Tensor<T>& right, Tensor<T>& result) {
2240 TENSOR_ASSERT(left.ndim() + right.ndim() == result.ndim(),"inconsistent dimension in outer_result",
2241 result.ndim(),0);
2242 T *ptr = result.ptr();
2243 TensorIterator<T> iter=right.unary_iterator(1,false,true);
2244 for (TensorIterator<T> p=left.unary_iterator(); p!=left.end(); ++p) {
2245 T val1 = *p;
2246 // Cannot reorder dimensions, but can fuse contiguous dimensions
2247 for (iter.reset(); iter._p0; ++iter) {
2248 long dimj = iter.dimj;
2249 T* _p0 = iter._p0;
2250 long Tstride = iter._s0;
2251 for (long _j=0; _j<dimj; ++_j, _p0+=Tstride) {
2252 *ptr++ = val1 * (*_p0);
2253 }
2254 }
2255 }
2256 }
2257
2258
2259 /// Inner product ... result(i,j,...,p,q,...) = sum(z) left(i,j,...,z)*right(z,p,q,...)
2260
2261 /// \ingroup tensor
2262 /// By default it contracts the last dimension of the left tensor and
2263 /// the first dimension of the right tensor. These defaults can be
2264 /// changed by specifying \c k0 and \c k1 , the index to contract in
2265 /// the left and right side tensors, respectively. The defaults
2266 /// correspond to (\c k0=-1 and \c k1=0 ).
2267 template <class T, class Q>
2268 Tensor<TENSOR_RESULT_TYPE(T,Q)> inner(const Tensor<T>& left, const Tensor<Q>& right,
2269 long k0=-1, long k1=0) {
2270 if (k0 < 0) k0 += left.ndim();
2271 if (k1 < 0) k1 += right.ndim();
2272 long nd = left.ndim() + right.ndim() - 2;
2273 TENSOR_ASSERT(nd!=0, "result is a scalar but cannot return one ... use dot",
2274 nd, &left);
2275
2276
2277 TENSOR_ASSERT(left.dim(k0) == right.dim(k1),"common index must be same length",
2278 right.dim(k1), &left);
2279
2280 TENSOR_ASSERT(nd > 0 && nd <= TENSOR_MAXDIM,
2281 "invalid number of dimensions in the result", nd,0);
2282
2283 long d[TENSOR_MAXDIM];
2284
2285 long base=0;
2286 for (long i=0; i<k0; ++i) d[i] = left.dim(i);
2287 for (long i=k0+1; i<left.ndim(); ++i) d[i-1] = left.dim(i);
2288 base = left.ndim()-1;
2289 for (long i=0; i<k1; ++i) d[i+base] = right.dim(i);
2290 base--;
2291 for (long i=k1+1; i<right.ndim(); ++i) d[i+base] = right.dim(i);
2292
2293 Tensor<TENSOR_RESULT_TYPE(T,Q)> result(nd,d);
2294
2295 inner_result(left,right,k0,k1,result);
2296
2297 return result;
2298 }
2299
2300 /// Accumulate inner product into user provided, contiguous, correctly sized result tensor
2301
2302 /// \ingroup tensor
2303 /// This routine may be used to optimize away the tensor constructor
2304 /// of the result tensor in inner loops when the result tensor may be
2305 /// reused or accumulated into. If the user calls this routine
2306 /// directly very little checking is done since it is intended as an
2307 /// optimization for small tensors. As far as the result goes, the
2308 /// caller is completely responsible for providing a contiguous tensor
2309 /// that has the correct dimensions and is appropriately initialized.
2310 /// The inner product is accumulated into result.
2311 template <class T, class Q>
2312 void inner_result(const Tensor<T>& left, const Tensor<Q>& right,
2313 long k0, long k1, Tensor< TENSOR_RESULT_TYPE(T,Q) >& result) {
2314
2315 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
2316 // Need to include explicit optimizations for common special cases
2317 // E.g., contiguous, matrix-matrix, and 3d-tensor*matrix
2318
2319 resultT* ptr = result.ptr();
2320
2321 if (k0 < 0) k0 += left.ndim();
2322 if (k1 < 0) k1 += right.ndim();
2323
2324 if (left.iscontiguous() && right.iscontiguous()) {
2325 if (k0==0 && k1==0) {
2326 // c[i,j] = a[k,i]*b[k,j] ... collapsing extra indices to i & j
2327 long dimk = left.dim(k0);
2328 long dimj = right.stride(0);
2329 long dimi = left.stride(0);
2330 mTxm(dimi,dimj,dimk,ptr,left.ptr(),right.ptr());
2331 return;
2332 }
2333 else if (k0==(left.ndim()-1) && k1==(right.ndim()-1)) {
2334 // c[i,j] = a[i,k]*b[j,k] ... collapsing extra indices to i & j
2335 long dimk = left.dim(k0);
2336 long dimi = left.size()/dimk;
2337 long dimj = right.size()/dimk;
2338 mxmT(dimi,dimj,dimk,ptr,left.ptr(),right.ptr());
2339 return;
2340 }
2341 else if (k0==0 && k1==(right.ndim()-1)) {
2342 // c[i,j] = a[k,i]*b[j,k] ... collapsing extra indices to i & j
2343 long dimk = left.dim(k0);
2344 long dimi = left.stride(0);
2345 long dimj = right.size()/dimk;
2346 mTxmT(dimi,dimj,dimk,ptr,left.ptr(),right.ptr());
2347 return;
2348 }
2349 else if (k0==(left.ndim()-1) && k1==0) {
2350 // c[i,j] = a[i,k]*b[k,j] ... collapsing extra indices to i & j
2351 long dimk = left.dim(k0);
2352 long dimi = left.size()/dimk;
2353 long dimj = right.stride(0);
2354 mxm(dimi,dimj,dimk,ptr,left.ptr(),right.ptr());
2355 return;
2356 }
2357 }
2358
2359 long dimj = left.dim(k0);
2360 TensorIterator<Q> iter1=right.unary_iterator(1,false,false,k1);
2361
2362 for (TensorIterator<T> iter0=left.unary_iterator(1,false,false,k0);
2363 iter0._p0; ++iter0) {
2364 T* MADNESS_RESTRICT xp0 = iter0._p0;
2365 long s0 = iter0._s0;
2366 for (iter1.reset(); iter1._p0; ++iter1) {
2368 Q* MADNESS_RESTRICT p1 = iter1._p0;
2369 long s1 = iter1._s0;
2370 resultT sum = 0;
2371 for (long j=0; j<dimj; ++j,p0+=s0,p1+=s1) {
2372 sum += (*p0) * (*p1);
2373 }
2374 *ptr++ += sum;
2375 }
2376 }
2377 }
2378
2379 /// Transform all dimensions of the tensor t by the matrix c
2380
2381 /// \ingroup tensor
2382 /// Often used to transform all dimensions from one basis to another
2383 /// \code
2384 /// result(i,j,k...) <-- sum(i',j', k',...) t(i',j',k',...) c(i',i) c(j',j) c(k',k) ...
2385 /// \endcode
2386 /// The input dimensions of \c t must all be the same and agree with
2387 /// the first dimension of \c c . The dimensions of \c c may differ in
2388 /// size. If the dimensions of \c c are the same, and the operation
2389 /// is being performed repeatedly, then you might consider calling \c
2390 /// fast_transform instead which enables additional optimizations and
2391 /// can eliminate all constructor overhead and improve cache locality.
2392 ///
2393 template <class T, class Q>
2395 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
2396 TENSOR_ASSERT(c.ndim() == 2,"second argument must be a matrix",c.ndim(),&c);
2397 if (c.dim(0)==c.dim(1) && t.iscontiguous() && c.iscontiguous()) {
2398 Tensor<resultT> result(t.ndim(),t.dims(),false);
2399 Tensor<resultT> work(t.ndim(),t.dims(),false);
2400 return fast_transform(t, c, result, work);
2401 }
2402 else {
2403 Tensor<resultT> result = t;
2404 for (long i=0; i<t.ndim(); ++i) {
2405 result = inner(result,c,0,0);
2406 }
2407 return result;
2408 }
2409 }
2410
2411 /// Transform all dimensions of the tensor t by distinct matrices c
2412
2413 /// \ingroup tensor
2414 /// Similar to transform but each dimension is transformed with a
2415 /// distinct matrix.
2416 /// \code
2417 /// result(i,j,k...) <-- sum(i',j', k',...) t(i',j',k',...) c[0](i',i) c[1](j',j) c[2](k',k) ...
2418 /// \endcode
2419 /// The first dimension of the matrices c must match the corresponding
2420 /// dimension of t.
2421 template <class T, class Q>
2423 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
2424 Tensor<resultT> result = t;
2425 for (long i=0; i<t.ndim(); ++i) {
2426 result = inner(result,c[i],0,0);
2427 }
2428 return result;
2429 }
2430
2431 /// Restricted but heavily optimized form of transform()
2432
2433 /// \ingroup tensor
2434 /// Both dimensions of \c c must be the same and match all dimensions
2435 /// of the input tensor \c t. All tensors must be contiguous.
2436 ///
2437 /// Performs the same operation as \c transform but it requires
2438 /// that the caller pass in workspace and a preallocated result,
2439 /// hoping that that both can be reused. If the result and
2440 /// workspace are reused between calls, then no tensor
2441 /// constructors need be called and cache locality should be
2442 /// improved. By passing in the workspace, this routine is kept
2443 /// thread safe.
2444 ///
2445 /// The input, result and workspace tensors must be distinct.
2446 ///
2447 /// All input tensors must be contiguous and fastest execution
2448 /// will result if all dimensions are approriately aligned and
2449 /// multiples of the underlying vector length. The workspace and
2450 /// the result must be of the same size as the input \c t . The
2451 /// result tensor need not be initialized before calling
2452 /// fast_transform.
2453 ///
2454 /// \code
2455 /// result(i,j,k,...) <-- sum(i',j', k',...) t(i',j',k',...) c(i',i) c(j',j) c(k',k) ...
2456 /// \endcode
2457 ///
2458 /// The input dimensions of \c t must all be the same .
2459 template <class T, class Q>
2462 typedef TENSOR_RESULT_TYPE(T,Q) resultT;
2463 const Q *pc=c.ptr();
2464 resultT *t0=workspace.ptr(), *t1=result.ptr();
2465 if (t.ndim()&1) {
2466 t0 = result.ptr();
2467 t1 = workspace.ptr();
2468 }
2469
2470 long dimj = c.dim(1);
2471 long dimi = 1;
2472 for (int n=1; n<t.ndim(); ++n) dimi *= dimj;
2473
2474#if HAVE_IBMBGQ
2475 long nij = dimi*dimj;
2476 if (IS_UNALIGNED(pc) || IS_UNALIGNED(t0) || IS_UNALIGNED(t1)) {
2477 for (long i=0; i<nij; ++i) t0[i] = 0.0;
2478 mTxm(dimi, dimj, dimj, t0, t.ptr(), pc);
2479 for (int n=1; n<t.ndim(); ++n) {
2480 for (long i=0; i<nij; ++i) t1[i] = 0.0;
2481 mTxm(dimi, dimj, dimj, t1, t0, pc);
2482 std::swap(t0,t1);
2483 }
2484 }
2485 else {
2486 mTxmq_padding(dimi, dimj, dimj, dimj, t0, t.ptr(), pc);
2487 for (int n=1; n<t.ndim(); ++n) {
2488 mTxmq_padding(dimi, dimj, dimj, dimj, t1, t0, pc);
2489 std::swap(t0,t1);
2490 }
2491 }
2492#else
2493 // Now assume no restriction on the use of mtxmq
2494 mTxmq(dimi, dimj, dimj, t0, t.ptr(), pc);
2495 for (int n=1; n<t.ndim(); ++n) {
2496 mTxmq(dimi, dimj, dimj, t1, t0, pc);
2497 std::swap(t0,t1);
2498 }
2499#endif
2500
2501 return result;
2502 }
2503
2504 /// Separated transform with a distinct matrix per dimension, reusing caller buffers.
2505 ///
2506 /// Computes, with no internal allocation,
2507 /// result(i,j,...) <-- sum(i',j',...) t(i',j',...) c[0](i',i) c[1](j',j) ...
2508 /// like general_transform, but the caller supplies result and workspace so they
2509 /// can be reused across many calls. Both must be contiguous and at least as large
2510 /// as the biggest intermediate the contraction produces. That is t.size() when no
2511 /// axis expands, but an expanding matrix (c[d].dim(1) > c[d].dim(0), e.g.
2512 /// coeffs2values with npt > k) can grow it larger, so size to the running maximum,
2513 /// not t.size(). c[d] may be rectangular; c[d].dim(0) must equal dim d of t.
2514 /// t, result and workspace must be distinct: the contraction ping-pongs between
2515 /// result and workspace and mTxmq cannot alias its source and destination.
2516 /// No communication; safe on any worker thread as long as result and workspace
2517 /// are not shared between concurrent calls.
2518 ///
2519 /// Instantiate only where TENSOR_RESULT_TYPE(T,Q) == T (the matrix type Q does not
2520 /// promote the result). This covers every (T,double) the eval path needs; it
2521 /// excludes real-tensor x complex-matrix pairs like (double,double_complex), which
2522 /// lower to a mixed-type cblas gemm that MKL is missing.
2523 template <class T, class Q>
2524 Tensor<TENSOR_RESULT_TYPE(T,Q)>&
2526 Tensor<TENSOR_RESULT_TYPE(T,Q)>& result,
2528 typedef TENSOR_RESULT_TYPE(T,Q) R;
2529 // Excluded pairs would compile by implicit instantiation and then hit the
2530 // missing mixed-type gemm; fail loudly here instead (see the doc comment).
2531 static_assert(std::is_same<TENSOR_RESULT_TYPE(T,Q), T>::value,
2532 "general_fast_transform requires the matrix type not to "
2533 "promote the result past the tensor type");
2534 MADNESS_CHECK(t.iscontiguous() && result.iscontiguous() && workspace.iscontiguous());
2535 // Catch the realistic accident of reusing one scratch tensor for two
2536 // arguments (exact base-pointer aliasing); arbitrary partial overlap is
2537 // the caller's responsibility.
2538 MADNESS_CHECK(result.ptr() != workspace.ptr());
2539 MADNESS_CHECK(t.ptr() != result.ptr() && t.ptr() != workspace.ptr());
2540
2541 const long D = t.ndim();
2542
2543 // Largest intermediate any buffer must hold = max over steps of the output
2544 // size. The running count starts at t.size() and is rescaled by dimj/dimk
2545 // each step; for expanding matrices (m_d > k) it can exceed t.size().
2546 {
2547 long running = t.size();
2548 long max_running = running;
2549 for (long d = 0; d < D; ++d) {
2550 MADNESS_CHECK(c[d].ndim() == 2 && c[d].dim(0) > 0);
2551 MADNESS_CHECK(running % c[d].dim(0) == 0);
2552 running = (running / c[d].dim(0)) * c[d].dim(1);
2554 }
2555 MADNESS_CHECK(result.size() >= max_running &&
2556 workspace.size() >= max_running);
2557 }
2558
2559 R* buf0 = workspace.ptr();
2560 R* buf1 = result.ptr();
2561 if (D & 1) std::swap(buf0, buf1); // odd # of steps: final write lands in result
2562
2563 R* out = buf0;
2564 R* in_r = nullptr; // R* source for steps d >= 1
2565 long n = t.size();
2566
2567 for (long d = 0; d < D; ++d) {
2568 const long dimk = c[d].dim(0);
2569 const long dimj = c[d].dim(1);
2570 const long dimi = n / dimk;
2571 if (d == 0) {
2572 mTxmq(dimi, dimj, dimk, out, t.ptr(), c[d].ptr());
2573 } else {
2574 mTxmq(dimi, dimj, dimk, out, in_r, c[d].ptr());
2575 }
2576 n = dimi * dimj;
2577 in_r = out;
2578 out = (out == buf0) ? buf1 : buf0;
2579 }
2580 return result;
2581 }
2582
2583 namespace detail {
2584 /// Per-thread grow-on-demand buffer pair for general_fast_transform.
2585 template <typename R>
2586 struct EvalScratch { Tensor<R> a, b; };
2587
2588 /// The function-local static pool backing eval_scratch<R>(). One pool
2589 /// per result type R, shared by every FunctionImpl<T,NDIM> of that type;
2590 /// its lifetime is the process, but eval_scratch_clear<R>() reclaims the
2591 /// buffers on demand.
2592 template <typename R>
2597
2598 /// Grow-on-demand per-thread buffer pair for general_fast_transform.
2599 ///
2600 /// Backed by a reclaimable thread_specific pool rather than a
2601 /// thread_local: the pair can grow to k^NDIM each for 6-D eval, and a
2602 /// thread_local would pin that per worker thread for the life of the
2603 /// thread (≈ the process) with no way to free it — bad under the
2604 /// TBB/PaRSEC backends, whose arenas touch more threads than
2605 /// MAD_NUM_THREADS. eval_scratch_clear<R>() frees them. Each thread
2606 /// first-touches its own pair (NUMA-local); buffers grow monotonically
2607 /// to the largest need seen on that thread. Never shared across threads.
2608 template <typename R>
2609 std::pair<Tensor<R>&, Tensor<R>&> eval_scratch(long need) {
2610 EvalScratch<R>& s = eval_scratch_pool<R>().local();
2611 if (s.a.size() < need) {
2612 s.a = Tensor<R>(need);
2613 s.b = Tensor<R>(need);
2614 }
2615 return {s.a, s.b};
2616 }
2617
2618 /// Free every thread's general_fast_transform scratch buffers. Call
2619 /// only at a quiescent point — no eval may be in flight and no
2620 /// reference returned by eval_scratch<R>() may still be in use (see
2621 /// thread_specific::clear()).
2622 template <typename R>
2624 eval_scratch_pool<R>().clear();
2625 }
2626 } // namespace detail
2627
2628 /// Return a new tensor holding the absolute value of each element of t
2629
2630 /// \ingroup tensor
2631 template <class T>
2633 typedef typename Tensor<T>::scalar_type scalar_type;
2634 Tensor<scalar_type> result(t.ndim(),t.dims(),false);
2635 BINARY_OPTIMIZED_ITERATOR(scalar_type,result,const T,t,*_p0 = std::abs(*_p1));
2636 return result;
2637 }
2638
2639 /// Return a new tensor holding the argument of each element of t (complex types only)
2640
2641 /// \ingroup tensor
2642 template <class T>
2644 typedef typename Tensor<T>::scalar_type scalar_type;
2645 Tensor<scalar_type> result(t.ndim(),t.dims(),false);
2646 BINARY_OPTIMIZED_ITERATOR(scalar_type,result,T,t,*_p0 = std::arg(*_p1));
2647 return result;
2648 }
2649
2650 /// Return a new tensor holding the real part of each element of t (complex types only)
2651
2652 /// \ingroup tensor
2653 template <class T>
2655 typedef typename Tensor<T>::scalar_type scalar_type;
2656 Tensor<scalar_type> result(t.ndim(),t.dims(),false);
2657 BINARY_OPTIMIZED_ITERATOR(scalar_type,result,const T,t,*_p0 = std::real(*_p1));
2658 return result;
2659 }
2660
2661 /// Return a new tensor holding the imaginary part of each element of t (complex types only)
2662
2663 /// \ingroup tensor
2664 template <class T>
2666 typedef typename Tensor<T>::scalar_type scalar_type;
2667 Tensor<scalar_type> result(t.ndim(),t.dims(),false);
2668 BINARY_OPTIMIZED_ITERATOR(scalar_type,result,const T,t,*_p0 = std::imag(*_p1));
2669 return result;
2670 }
2671
2672 /// Returns a new deep copy of the complex conjugate of the input tensor (complex types only)
2673
2674 /// \ingroup tensor
2675 template <class T>
2677 Tensor<T> result(t.ndim(),t.dims(),false);
2678 BINARY_OPTIMIZED_ITERATOR(T,result,const T,t,*_p0 = conditional_conj(*_p1));
2679 return result;
2680 }
2681}
2682
2683#undef TENSOR_SHARED_PTR
2684
2685#endif // MADNESS_TENSOR_TENSOR_H__INCLUDED
double q(double t)
Definition DKops.h:18
Provides routines for internal use optimized for aligned data.
Interface templates for the archives (serialization).
Declares BaseTensor.
std::complex< double > double_complex
Definition cfft.h:14
The base class for tensors defines generic capabilities.
Definition basetensor.h:85
bool conforms(const BaseTensor *t) const
Returns true if this and *t are the same shape and size.
Definition basetensor.h:159
long dim(int i) const
Returns the size of dimension i.
Definition basetensor.h:147
bool iscontiguous() const
Returns true if the tensor refers to contiguous memory locations.
Definition basetensor.h:168
void mapdim_inplace(const std::vector< long > &map)
General permutation of dimensions.
Definition basetensor.cc:156
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 stride(int i) const
Returns the stride associated with dimension i.
Definition basetensor.h:150
long _size
Number of elements in the tensor.
Definition basetensor.h:93
long id() const
Returns the typeid of the tensor (c.f., TensorTypeData<T> )
Definition basetensor.h:141
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 swapdim_inplace(long i, long j)
Swaps the dimensions.
Definition basetensor.cc:124
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
void cycledim_inplace(long shift, long start, long end)
Cyclic shift of dimensions.
Definition basetensor.cc:134
long _ndim
Number of dimensions (-1=invalid; 0=no supported; >0=tensor)
Definition basetensor.h:94
void reshape_inplace(const std::vector< long > &d)
Reshapes the tensor inplace.
Definition basetensor.cc:76
Indexing a non-constant tensor with slices returns a SliceTensor.
Definition tensor.h:2058
virtual ~SliceTensor()
Definition tensor.h:2143
SliceTensor(const Tensor< T > &t, const Slice s[])
Definition tensor.h:2069
SliceTensor< T > & operator=(const SliceTensor< Q > &t)
Definition tensor.h:2122
SliceTensor< T > & operator=(const Tensor< Q > &t)
Definition tensor.h:2133
SliceTensor< T > & operator=(const SliceTensor< T > &t)
Definition tensor.h:2116
SliceTensor(const Tensor< T > &t, const std::array< Slice, TENSOR_MAXDIM > s)
Definition tensor.h:2065
SliceTensor< T > & operator=(const Tensor< T > &t)
Definition tensor.h:2127
SliceTensor< T > & operator=(const T &t)
Definition tensor.h:2138
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
Definition tensoriter.h:61
long dimj
Definition tensoriter.h:70
long _s0
Definition tensoriter.h:71
void reset()
Reset the iterator back to the start ...
Definition tensoriter.h:354
T * _p0
Definition tensoriter.h:66
Traits class to specify support of numeric types.
Definition type_data.h:56
A tensor is a multidimensional array.
Definition tensor.h:318
scalar_type absmax(long *ind=0) const
Return the absolute maximum value (and if ind is non-null, its index) in the Tensor.
Definition tensor.h:1755
void deallocate()
Definition tensor.h:398
T *MADNESS_RESTRICT _p
Definition tensor.h:322
Tensor< T > & operator=(T x)
Inplace fill tensor with scalar.
Definition tensor.h:554
const Tensor< T > swapdim(long idim, long jdim) const
Returns new view/tensor swaping dimensions i and j.
Definition tensor.h:1615
Tensor(const std::vector< long > &d, bool dozero=true)
Create and optionally zero new n-d tensor. This is the most general constructor.
Definition tensor.h:537
SliceTensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2, const Slice &s3, const Slice &s4)
Return a 1-5d SliceTensor that views the specified range of the 5d Tensor.
Definition tensor.h:1338
const Tensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2, const Slice &s3) const
Return a 1-4d constant Tensor that views the specified range of the 4d Tensor.
Definition tensor.h:1327
float_scalar_type normf() const
Returns the Frobenius norm of the tensor.
Definition tensor.h:1727
Tensor< T > & gaxpy(T alpha, const Tensor< T > &other, T beta)
Inplace generalized saxpy ... this = this*alpha + other*beta.
Definition tensor.h:1806
TensorIterator< T, Q > binary_iterator(const Tensor< Q > &q, long iterlevel=0, bool optimize=true, bool fusedim=true, long jdim=default_jdim) const
Return iterator over two tensors.
Definition tensor.h:1871
SliceTensor< T > operator()(long i, const Slice &s1, long k)
Return a 1d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1276
Tensor(long d0, long d1)
Create and zero new 2-d tensor.
Definition tensor.h:482
const T * ptr() const
Returns a pointer to the internal data.
Definition tensor.h:1846
scalar_type absmin(long *ind=0) const
Return the absolute minimum value (and if ind is non-null, its index) in the Tensor.
Definition tensor.h:1734
Tensor< T > & unaryop(opT &op)
Inplace apply a unary function to each element of the tensor.
Definition tensor.h:1794
IsSupported< TensorTypeData< Q >, Tensor< TENSOR_RESULT_TYPE(T, Q)> >::type operator+(const Q &x) const
Add a scalar of the same type to all elements of a tensor producing a new tensor.
Definition tensor.h:644
Tensor< T > & fill(T x)
Inplace fill with a scalar (legacy name)
Definition tensor.h:563
const Tensor< T > operator()(long i, const Slice &s1, const Slice &s2) const
Return a 2d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1206
Tensor< T > & operator+=(const Tensor< Q > &t)
Inplace addition of two tensors.
Definition tensor.h:573
Tensor< T > reshape(long dim0, long dim1, long dim2, long dim3, long dim4)
Returns new view/tensor rehaping to conforming 5-d tensor with given dimensions.
Definition tensor.h:1509
Tensor< T > swapdim(long idim, long jdim)
Returns new view/tensor swaping dimensions i and j.
Definition tensor.h:1606
const Tensor< T > flat() const
Returns new view/tensor rehshaping to flat (1-d) tensor.
Definition tensor.h:1562
T sum() const
Returns the sum of all elements of the tensor.
Definition tensor.h:1663
Tensor< T > reshape(int ndimnew, const long *d)
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1385
const T & operator[](long i) const
1-d indexing operation using [] without bounds checking.
Definition tensor.h:792
TensorIterator< T > unary_iterator(long iterlevel=0, bool optimize=true, bool fusedim=true, long jdim=default_jdim) const
Return iterator over single tensor.
Definition tensor.h:1861
Tensor< T > reshape(long dim0, long dim1)
Returns new view/tensor rehaping to conforming 2-d tensor with given dimensions.
Definition tensor.h:1440
TensorTypeData< T >::scalar_type scalar_type
C++ typename of the real type associated with a complex type.
Definition tensor.h:410
Tensor(long d0, long d1, long d2, long d3, long d4, long d5)
Create and zero new 6-d tensor.
Definition tensor.h:528
T type
C++ typename of this tensor.
Definition tensor.h:407
Tensor< TENSOR_RESULT_TYPE(T, Q) > operator+(const Tensor< Q > &t) const
Addition of two tensors to produce a new tensor.
Definition tensor.h:593
T & operator()(long i)
1-d indexing operation without bounds checking.
Definition tensor.h:803
SliceTensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2, const Slice &s3)
Return a 1-4d SliceTensor that views the specified range of the 4d Tensor.
Definition tensor.h:1316
T & operator()(long i, long j, long k, long l, long m)
5-d indexing operation without bounds checking.
Definition tensor.h:921
T * ptr()
Returns a pointer to the internal data.
Definition tensor.h:1841
bool conforms(const Tensor< Q > &t) const
Test if *this and t conform.
Definition tensor.h:1658
const Tensor< T > mapdim(const std::vector< long > &map) const
Returns new view/tensor permuting the dimensions.
Definition tensor.h:1634
T & operator()(long i, long j)
2-d indexing operation without bounds checking.
Definition tensor.h:826
const Tensor< T > reshape(const std::vector< long > &d) const
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1414
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:1642
const Tensor< T > operator()(const std::vector< Slice > &s) const
General slicing operation (const)
Definition tensor.h:1071
Tensor< T > & operator-=(const Tensor< Q > &t)
Inplace subtraction of two tensors.
Definition tensor.h:583
const Tensor< T > reshape(long dim0, long dim1, long dim2) const
Returns new view/tensor rehaping to conforming 3-d tensor with given dimensions.
Definition tensor.h:1472
const T & operator()(long i, long j, long k) const
3-d indexing operation without bounds checking.
Definition tensor.h:868
SliceTensor< T > operator()(long i, const Slice &s1)
Return a 1d SliceTensor that views the specified range of the 2d Tensor.
Definition tensor.h:1116
const Tensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2, const Slice &s3, const Slice &s4, const Slice &s5) const
Return a 1-6d constant Tensor that views the specified range of the 6d Tensor.
Definition tensor.h:1372
Tensor< T > mapdim(const std::vector< long > &map)
Returns new view/tensor permuting the dimensions.
Definition tensor.h:1625
IsSupported< TensorTypeData< Q >, Tensor< TENSOR_RESULT_TYPE(T, Q)> >::type operator*(const Q &x) const
Multiplication of tensor by a scalar of a supported type to produce a new tensor.
Definition tensor.h:618
const Tensor< T > operator()(const std::array< Slice, TENSOR_MAXDIM > &s) const
General slicing operation (const)
Definition tensor.h:1089
SliceTensor< T > operator()(const Slice &s0, const Slice &s1, long k)
Return a 2d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1236
const Tensor< T > reshape(int ndimnew, const long *d) const
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1396
const Tensor< T > fusedim(long i) const
Returns new view/tensor fusing contiguous dimensions i and i+1.
Definition tensor.h:1597
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type scale(Q x)
Inplace multiplication by scalar of supported type (legacy name)
Definition tensor.h:687
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type operator-=(const Q &x)
Inplace decrement by scalar of supported type.
Definition tensor.h:708
IsSupported< TensorTypeData< Q >, Tensor< TENSOR_RESULT_TYPE(T, Q)> >::type operator/(const Q &x) const
Divide tensor by a scalar of a supported type to produce a new tensor.
Definition tensor.h:631
SliceTensor< T > operator()(const Slice &s0, const Slice &s1)
Return a 2d SliceTensor that views the specified range of the 2d Tensor.
Definition tensor.h:1156
TensorTypeData< T >::float_scalar_type float_scalar_type
C++ typename of the floating point type associated with scalar real type.
Definition tensor.h:413
Tensor(const Tensor< T > &t)
Copy constructor is shallow (same as assignment)
Definition tensor.h:429
T & operator()(long i, long j, long k)
3-d indexing operation without bounds checking.
Definition tensor.h:853
const Tensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2, const Slice &s3, const Slice &s4) const
Return a 1-5d constant Tensor that views the specified range of the 5d Tensor.
Definition tensor.h:1349
const Tensor< T > reshape(long dim0, long dim1, long dim2, long dim3) const
Returns new view/tensor rehaping to conforming 4-d tensor with given dimensions.
Definition tensor.h:1496
Tensor< T > operator-() const
Unary negation producing a new tensor.
Definition tensor.h:664
const T & operator()(long i, long j) const
2-d indexing operation without bounds checking.
Definition tensor.h:839
const Tensor< T > operator()(const Slice &s0, long j, long k) const
Return a 1d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1306
T & operator()(long i, long j, long k, long l, long m, long n)
6-d indexing operation without bounds checking.
Definition tensor.h:962
SliceTensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2, const Slice &s3, const Slice &s4, const Slice &s5)
Return a 1-6d SliceTensor that views the specified range of the 6d Tensor.
Definition tensor.h:1360
SliceTensor< T > operator()(const Slice &s0)
Return a 1d SliceTensor that views the specified range of the 1d Tensor.
Definition tensor.h:1096
SliceTensor< T > operator()(const Slice &s0, long j, long k)
Return a 1d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1296
const T & operator()(const long ind[]) const
Politically incorrect general indexing operation without bounds checking.
Definition tensor.h:1017
Tensor< T > & emul(const Tensor< T > &t)
Inplace multiply by corresponding elements of argument Tensor.
Definition tensor.h:1800
Tensor< T > & operator=(const Tensor< T > &t)
Assignment is shallow (same as copy constructor)
Definition tensor.h:444
SliceTensor< T > operator()(long i, const Slice &s1, const Slice &s2)
Return a 2d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1196
T & operator()(long i, long j, long k, long l)
4-d indexing operation without bounds checking.
Definition tensor.h:884
TENSOR_RESULT_TYPE(T, Q) trace(const Tensor< Q > &t) const
Return the trace of two tensors (no complex conjugate invoked)
Definition tensor.h:1778
SliceTensor< T > operator()(const std::array< Slice, TENSOR_MAXDIM > &s)
General slicing operation.
Definition tensor.h:1081
T max(long *ind=0) const
Return the maximum value (and if ind is non-null, its index) in the Tensor.
Definition tensor.h:1704
Tensor(long d0)
Create and zero new 1-d tensor.
Definition tensor.h:473
const T & operator()(long i, long j, long k, long l) const
4-d indexing operation without bounds checking.
Definition tensor.h:902
T product() const
Return the product of all elements of the tensor.
Definition tensor.h:1677
const T & operator()(long i, long j, long k, long l, long m) const
5-d indexing operation without bounds checking.
Definition tensor.h:941
T min(long *ind=0) const
Return the minimum value (and if ind is non-null, its index) in the Tensor.
Definition tensor.h:1684
Tensor< TENSOR_RESULT_TYPE(T, Q) > operator-(const Tensor< Q > &t) const
Subtraction of two tensors to produce a new tensor.
Definition tensor.h:605
const T & operator()(long i, long j, long k, long l, long m, long n) const
6-d indexing operation without bounds checking.
Definition tensor.h:984
T & operator()(const std::vector< long > ind)
General indexing operation with bounds checking.
Definition tensor.h:1033
Tensor< T > splitdim(long i, long dimi0, long dimi1)
Returns new view/tensor splitting dimension i as dimi0*dimi1 to produce conforming d+1 dimension tens...
Definition tensor.h:1570
virtual ~Tensor()
Definition tensor.h:1898
const Tensor< T > operator()(const Slice &s0) const
Return a 1d SliceTensor that views the specified range of the 1d Tensor.
Definition tensor.h:1106
void allocate(long nd, const long d[], bool dozero)
Definition tensor.h:325
const BaseTensor * base() const
Returns a pointer to the base class.
Definition tensor.h:1856
Tensor< T > reshape(long dim0, long dim1, long dim2, long dim3, long dim4, long dim5)
Returns new view/tensor rehaping to conforming 6-d tensor with given dimensions.
Definition tensor.h:1536
T sumsq() const
Returns the sum of the squares of the elements.
Definition tensor.h:1670
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type operator*=(const Q &x)
Inplace multiplication by scalar of supported type.
Definition tensor.h:676
const Tensor< T > operator()(const Slice &s0, long j) const
Return a 1d constant Tensor that views the specified range of the 2d Tensor.
Definition tensor.h:1146
T & operator[](long i)
1-d indexing operation using [] without bounds checking.
Definition tensor.h:781
Tensor< T > & screen(double x)
Inplace set elements of *this less than x in absolute magnitude to zero.
Definition tensor.h:759
const Tensor< T > cycledim(long nshift, long start, long end) const
Returns new view/tensor cycling the sub-dimensions (start,...,end) with shift steps.
Definition tensor.h:1650
const Tensor< T > splitdim(long i, long dimi0, long dimi1) const
Returns new view/tensor splitting dimension i as dimi0*dimi1 to produce conforming d+1 dimension tens...
Definition tensor.h:1579
Tensor(long d0, long d1, long d2, long d3)
Create and zero new 4-d tensor.
Definition tensor.h:503
Tensor(long d0, long d1, long d2)
Create and zero new 3-d tensor.
Definition tensor.h:492
const Tensor< T > reshape(long dim0, long dim1, long dim2, long dim3, long dim4, long dim5) const
Returns new view/tensor rehaping to conforming 6-d tensor with given dimensions.
Definition tensor.h:1550
const Tensor< T > reshape(long dim0) const
Returns new view/tensor rehapings to conforming 1-d tensor with given dimension.
Definition tensor.h:1430
bool has_data() const
Definition tensor.h:1903
Tensor< T > reshape(const std::vector< long > &d)
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1406
BaseTensor * base()
Returns a pointer to the base class.
Definition tensor.h:1851
const TensorIterator< T > & end() const
End point for forward iteration.
Definition tensor.h:1893
Tensor< T > reshape(long dim0, long dim1, long dim2)
Returns new view/tensor rehaping to conforming 3-d tensor with given dimensions.
Definition tensor.h:1461
Tensor< T > reshape(long dim0, long dim1, long dim2, long dim3)
Returns new view/tensor rehaping to conforming 4-d tensor with given dimensions.
Definition tensor.h:1484
SliceTensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2)
Return a 3d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1176
Tensor< T > reshape(long dim0)
Returns new view/tensor rehapings to conforming 1-d tensor with given dimension.
Definition tensor.h:1422
SliceTensor< T > operator()(const Slice &s0, long j)
Return a 1d SliceTensor that views the specified range of the 2d Tensor.
Definition tensor.h:1136
Tensor< T > fusedim(long i)
Returns new view/tensor fusing contiguous dimensions i and i+1.
Definition tensor.h:1588
const Tensor< T > operator()(long i, long j, const Slice &s2) const
Return a 1d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1266
const Tensor< T > operator()(const Slice &s0, const Slice &s1, const Slice &s2) const
Return a 3d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1186
TENSOR_RESULT_TYPE(T, Q) trace_conj(const Tensor< Q > &t) const
Return the trace of two tensors with complex conjugate of the leftmost (i.e., this)
Definition tensor.h:1786
const Tensor< T > operator()(const Slice &s0, const Slice &s1, long k) const
Return a 2d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1246
const T & operator()(const std::vector< long > ind) const
General indexing operation with bounds checking.
Definition tensor.h:1047
T & operator()(const long ind[])
Politically incorrect general indexing operation without bounds checking.
Definition tensor.h:1001
Tensor< T > flat()
Returns new view/tensor rehshaping to flat (1-d) tensor.
Definition tensor.h:1556
const Tensor< T > reshape(long dim0, long dim1) const
Returns new view/tensor rehaping to conforming 2-d tensor with given dimensions.
Definition tensor.h:1450
Tensor(long d0, long d1, long d2, long d3, long d4)
Create and zero new 5-d tensor.
Definition tensor.h:515
Tensor()
Default constructor does not allocate any data and sets ndim=-1, size=0, _p=0, and id.
Definition tensor.h:416
const Tensor< T > operator()(long i, const Slice &s1, long k) const
Return a 1d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1286
TensorIterator< T, Q, R > ternary_iterator(const Tensor< Q > &q, const Tensor< R > &r, long iterlevel=0, bool optimize=true, bool fusedim=true, long jdim=default_jdim) const
Return iterator over three tensors.
Definition tensor.h:1882
const Tensor< T > operator()(long i, const Slice &s1) const
Return a 1d SliceTensor that views the specified range of the 2d Tensor.
Definition tensor.h:1126
static bool bounds_checking()
Return true if bounds checking was enabled at compile time.
Definition tensor.h:769
const T & operator()(long i) const
1-d indexing operation without bounds checking.
Definition tensor.h:814
void clear()
Frees all memory and resests to state of default constructor.
Definition tensor.h:1901
TENSOR_SHARED_PTR< T > _shptr
Definition tensor.h:323
Tensor< T > & fillrandom()
Inplace fill with random values ( [0,1] for floats, [0,MAXSIZE] for integers)
Definition tensor.h:725
Tensor(long nd, const long d[], bool dozero=true)
Politically incorrect general constructor.
Definition tensor.h:546
const Tensor< T > operator()(const Slice &s0, long j, const Slice &s2) const
Return a 2d constant Tensor that views the specified range of the 3d Tensor.
Definition tensor.h:1226
IsSupported< TensorTypeData< Q >, Tensor< TENSOR_RESULT_TYPE(T, Q)> >::type operator-(const Q &x) const
Subtract a scalar of the same type from all elements producing a new tensor.
Definition tensor.h:657
const Tensor< T > operator()(const Slice &s0, const Slice &s1) const
Return a 2d constant Tensor that views the specified range of the 2d Tensor.
Definition tensor.h:1166
Tensor< T > & fillindex()
Inplace fill with the index of each element.
Definition tensor.h:749
SliceTensor< T > operator()(long i, long j, const Slice &s2)
Return a 1d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1256
const Tensor< T > reshape(long dim0, long dim1, long dim2, long dim3, long dim4) const
Returns new view/tensor rehaping to conforming 5-d tensor with given dimensions.
Definition tensor.h:1522
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type operator+=(const Q &x)
Inplace increment by scalar of supported type.
Definition tensor.h:697
Tensor< T > & conj()
Inplace complex conjugate.
Definition tensor.h:717
SliceTensor< T > operator()(const Slice &s0, long j, const Slice &s2)
Return a 2d SliceTensor that views the specified range of the 3d Tensor.
Definition tensor.h:1216
SliceTensor< T > operator()(const std::vector< Slice > &s)
General slicing operation.
Definition tensor.h:1061
Definition thread_specific.h:82
static const double R
Definition csqrt.cc:46
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
auto T(World &world, response_space &f) -> response_space
Definition global_functions.cc:28
archive_array< T > wrap(const T *, unsigned int)
Factory function to wrap a dynamically allocated pointer as a typed archive_array.
Definition archive.h:914
Tensor< T > conj_transpose(const Tensor< T > &t)
Returns a new deep copy of the complex conjugate transpose of the input tensor.
Definition tensor.h:2044
Tensor< typename Tensor< T >::scalar_type > arg(const Tensor< T > &t)
Return a new tensor holding the argument of each element of t (complex types only)
Definition tensor.h:2643
Tensor< TENSOR_RESULT_TYPE(T, Q) > & fast_transform(const Tensor< T > &t, const Tensor< Q > &c, Tensor< TENSOR_RESULT_TYPE(T, Q) > &result, Tensor< TENSOR_RESULT_TYPE(T, Q) > &workspace)
Restricted but heavily optimized form of transform()
Definition tensor.h:2460
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:2312
const double beta
Definition gygi_soltion.cc:62
Tensor< double > op(const Tensor< double > &x)
Definition kain.cc:508
Macros and tools pertaining to the configuration of MADNESS.
#define MADNESS_PRAGMA_GCC(x)
Definition madness_config.h:205
#define MADNESS_CHECK(condition)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:182
Internal use only.
Definition potentialmanager.cc:41
void eval_scratch_clear()
Definition tensor.h:2623
std::pair< Tensor< R > &, Tensor< R > & > eval_scratch(long need)
Definition tensor.h:2609
thread_specific< EvalScratch< R > > & eval_scratch_pool()
Definition tensor.h:2593
T mynorm(T t)
Definition tensor.h:261
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
std::ostream & operator<<(std::ostream &os, const particle< PDIM > &p)
Definition lowrankfunction.h:401
double abs(double x)
Definition complexfun.h:48
void mTxmT(long dimi, long dimj, long dimk, T *MADNESS_RESTRICT c, const T *a, const T *b)
Matrix += Matrix transpose * matrix transpose ... MKL interface version.
Definition mxm.h:238
void outer_result(const Tensor< T > &left, const Tensor< T > &right, Tensor< T > &result)
Outer product ... result(i,j,...,p,q,...) = left(i,k,...)*right(p,q,...)
Definition tensor.h:2239
Q conditional_conj(const Q &coeff)
For real types return value, for complex return conjugate.
Definition tensor.h:256
GenTensor< TENSOR_RESULT_TYPE(R, Q)> general_transform(const GenTensor< R > &t, const Tensor< Q > c[])
Definition gentensor.h:274
Function< Q, NDIM > convert(const Function< T, NDIM > &f, bool fence=true)
Type conversion implies a deep copy. No communication except for optional fence.
Definition mra.h:2219
void mxm(long dimi, long dimj, long dimk, T *MADNESS_RESTRICT c, const T *a, const T *b)
Matrix += Matrix * matrix ... BLAS/MKL interface version.
Definition mxm.h:199
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:731
Function< T, NDIM > conj(const Function< T, NDIM > &f, bool fence=true)
Return the complex conjugate of the input function with the same distribution and optional fence.
Definition mra.h:2233
void mTxm(long dimi, long dimj, long dimk, T *MADNESS_RESTRICT c, const T *a, const T *b)
Matrix += Matrix transpose * matrix ... MKL interface version.
Definition mxm.h:212
response_space transpose(response_space &f)
Definition basic_operators.cc:10
Tensor< TENSOR_RESULT_TYPE(T, Q)> & general_fast_transform(const Tensor< T > &t, const Tensor< Q > *c, Tensor< TENSOR_RESULT_TYPE(T, Q)> &result, Tensor< TENSOR_RESULT_TYPE(T, Q)> &workspace)
Definition tensor.h:2525
std::vector< CCPairFunction< T, NDIM > > operator*(const double fac, const std::vector< CCPairFunction< T, NDIM > > &arg)
Definition ccpairfunction.h:1089
void mTxmq_padding(long dimi, long dimj, long dimk, long ext_b, cT *c, const aT *a, const bT *b)
Definition mtxmq.h:96
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
std::vector< CCPairFunction< T, NDIM > > operator-(const std::vector< CCPairFunction< T, NDIM > > c1, const std::vector< CCPairFunction< T, NDIM > > &c2)
Definition ccpairfunction.h:1060
static double pop(std::vector< double > &v)
Definition SCF.cc:115
static void aligned_zero(long n, T *a)
Definition aligned.h:55
double inner(response_space &a, response_space &b)
Definition response_functions.h:639
double imag(double x)
Definition complexfun.h:56
GenTensor< TENSOR_RESULT_TYPE(R, Q)> transform_dir(const GenTensor< R > &t, const Tensor< Q > &c, const int axis)
Definition lowranktensor.h:1106
std::string type(const PairType &n)
Definition PNOParameters.h:18
static const long default_jdim
Definition tensoriter.h:57
std::vector< CCPairFunction< T, NDIM > > operator+(const std::vector< CCPairFunction< T, NDIM > > c1, const std::vector< CCPairFunction< T, NDIM > > &c2)
Definition ccpairfunction.h:1052
double real(double x)
Definition complexfun.h:52
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
void mxmT(long dimi, long dimj, long dimk, T *MADNESS_RESTRICT c, const T *a, const T *b)
Matrix += Matrix * matrix transpose ... MKL interface version.
Definition mxm.h:225
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:2172
void mTxmq(long dimi, long dimj, long dimk, T *MADNESS_RESTRICT c, const T *a, const T *b, long ldb=-1)
Matrix = Matrix transpose * matrix ... MKL interface version.
Definition mxm.h:257
Definition mraimpl.h:51
static long abs(long a)
Definition tensor.h:219
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
Implement dummy posix_memalign if it is missing on the system.
int posix_memalign(void **memptr, std::size_t alignment, std::size_t size)
Definition posixmem.h:44
std::complex< float > float_complex
Definition ran.h:39
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 long k
Definition rk.cc:44
Definition test_ar.cc:204
Definition test_ccpairfunction.cc:22
Definition type_data.h:146
static void load(const Archive &s, Tensor< T > &t)
Definition tensor.h:1930
Default load of an object via serialize(ar, t).
Definition archive.h:667
static void store(const Archive &s, const Tensor< T > &t)
Definition tensor.h:1915
Default store of an object via serialize(ar, t).
Definition archive.h:612
static Q op(const Q &coeff)
Definition tensor.h:249
For real types return value, for complex return conjugate.
Definition tensor.h:240
static Q op(const Q &coeff)
Definition tensor.h:241
Per-thread grow-on-demand buffer pair for general_fast_transform.
Definition tensor.h:2586
Tensor< R > b
Definition tensor.h:2586
Tensor< R > a
Definition tensor.h:2586
static const double s0
Definition tdse4.cc:83
#define TENSOR_ALIGNMENT
#define IS_UNALIGNED(p)
Definition tensor.h:235
#define UNARY_UNOPTIMIZED_ITERATOR(X, x, exp)
Definition tensor_macros.h:678
#define BINARY_OPTIMIZED_ITERATOR(X, x, Y, y, exp)
Definition tensor_macros.h:701
#define UNARY_OPTIMIZED_ITERATOR(X, x, exp)
Definition tensor_macros.h:658
#define TERNARY_OPTIMIZED_ITERATOR(X, x, Y, y, Z, z, exp)
Definition tensor_macros.h:719
#define TENSOR_MAXDIM
Definition tensor_macros.h:194
Declares and implements TensorException.
#define TENSOR_ASSERT(condition, msg, value, t)
Definition tensorexcept.h:130
#define TENSOR_EXCEPTION(msg, value, t)
Definition tensorexcept.h:126
Declares TensorIterator.
AtomicInt sum
Definition test_atomicint.cc:46
static const double alpha
Definition testcosine.cc:10
const double offset
Definition testfuns.cc:143
std::size_t axis
Definition testpdiff.cc:59
double k0
Definition testperiodic.cc:66
double k1
Definition testperiodic.cc:67
Reclaimable thread-specific storage (a thread_local you can free).
#define TENSOR_RESULT_TYPE(L, R)
This macro simplifies access to TensorResultType.
Definition type_data.h:205