High Performance Plasticity  0.5.0
tensor.h
Go to the documentation of this file.
1 #ifndef HPP_TENSOR_H
5 #define HPP_TENSOR_H
6 
7 #include <cstddef>
8 #include <iostream>
9 #include <cassert>
10 #include <lapacke.h>
11 #include <vector>
12 #include <limits>
13 #include <stdexcept>
14 #include <algorithm>
15 #include <valarray>
16 #include <random>
17 #include <hdf5/serial/H5Cpp.h>
18 #include <hdf5/openmpi/hdf5.h>
19 #include <hpp/config.h>
20 #include <hpp/mpiUtils.h>
21 #include <hpp/hdfUtilsCpp.h>
22 #include <hpp/hdfUtils.h>
23 #include <unsupported/Eigen/MatrixFunctions>
24 #include "mpi.h"
25 
26 namespace hpp
27 {
28 
29 // SOME OVERLOADS OF STD::VECTOR
30 
37 template <typename T>
38 std::vector<T> operator/(const std::vector<T>& vec, T scalar) {
39  std::vector<T> newvec = vec;
40  for (auto&& v : newvec) {
41  v /= scalar;
42  }
43  return newvec;
44 }
45 
46 template <typename T>
47 std::vector<std::vector<T>> operator/(const std::vector<std::vector<T>>& veclist, T scalar) {
48  std::vector<std::vector<T>> newveclist = veclist;
49  for (auto&& vec : newveclist) {
50  for (auto&& v : vec) {
51  v /= scalar;
52  }
53  }
54  return newveclist;
55 }
56 
57 template <typename T>
58 void operator*=(std::vector<T>& vec, const T scalar) {
59  for (auto&& v : vec) {
60  v *= scalar;
61  }
62 }
63 
64 template <typename T>
65 void operator/=(std::vector<T>& vec, const T scalar) {
66  for (auto&& v : vec) {
67  v /= scalar;
68  }
69 }
70 
71 template <typename T>
72 std::vector<T> operator*(const std::vector<T>& vec, const T scalar) {
73  std::vector<T> newvec = vec;
74  newvec *= scalar;
75  return newvec;
76 }
77 
78 template <typename T>
79 std::vector<T> operator*(const T scalar, const std::vector<T>& vec) {
80  return vec*scalar;
81 }
82 
83 template <typename T>
84 std::vector<std::vector<T>> operator*(T scalar, const std::vector<std::vector<T>>& veclist) {
85  return veclist*scalar;
86 }
87 
88 template <typename T>
89 std::vector<T> abs(const std::vector<T>& vec) {
90  std::vector<T> absVec = vec;
91  for (auto&& v : absVec) {
92  v = std::abs(v);
93  }
94  return absVec;
95 }
96 
97 template <typename T>
98 T min(const std::vector<T>& vec) {
99  T val = *(std::min_element(vec.begin(), vec.end()));
100  return val;
101 }
102 
103 template <typename T>
104 T max(const std::vector<T>& vec) {
105  T val = *(std::max_element(vec.begin(), vec.end()));
106  return val;
107 }
108 
109 template <typename T>
110 std::vector<T> operator-(const std::vector<T>& vec1, const std::vector<T>& vec2) {
111  unsigned int n = vec1.size();
112  if (n != vec2.size()) {
113  throw std::runtime_error("Vector size mismatch.");
114  }
115  std::vector<T> vecout(n);
116  for (unsigned int i=0; i<n; i++) {
117  vecout[i] = vec1[i] - vec2[i];
118  }
119  return vecout;
120 }
121 
127 template <typename T>
128 T prod(const std::vector<T>& vec) {
129  T product = 1.0;
130  for (auto&& v : vec) {
131  product *= v;
132  }
133  return product;
134 }
135 
136 //
137 template <typename T>
138 std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec)
139 {
140  out << "[";
141  for (unsigned int i=0; i<vec.size(); i++) {
142  out << vec[i];
143  if (i != vec.size()-1) {
144  out << ", ";
145  }
146  }
147  out << "]";
148  return out;
149 }
150 template <typename T>
151 std::ostream& operator<<(std::ostream& out, const std::valarray<T>& vec)
152 {
153  out << "[";
154  for (unsigned int i=0; i<vec.size(); i++) {
155  out << vec[i];
156  if (i != vec.size()-1) {
157  out << ", ";
158  }
159  }
160  out << "]";
161  return out;
162 }
163 
164 // TENSORS
165 
166 class TensorError: public std::runtime_error
167 {
168  public:
169  explicit TensorError (const std::string &val) : std::runtime_error::runtime_error(val) {}
170 };
171 
180 struct idx2d{
181  unsigned int i;
182  unsigned int j;
183 };
184 
185 inline idx2d unflat(unsigned int flatIdx, unsigned int n1, unsigned int n2)
186 {
187  idx2d idx;
188  if (HPP_ARRAY_LAYOUT == LAPACK_ROW_MAJOR) {
189  idx.i = flatIdx/n2;
190  idx.j = flatIdx - idx.i*n2;
191  } else if (HPP_ARRAY_LAYOUT == LAPACK_COL_MAJOR) {
192  idx.j = flatIdx/n1;
193  idx.i = flatIdx - idx.j*n1;
194  } else {
195  throw TensorError(std::string("No support for layout ")+std::to_string(HPP_ARRAY_LAYOUT));
196  }
197  return idx;
198 }
199 
212 struct idx4d{
213  unsigned int i;
214  unsigned int j;
215  unsigned int k;
216  unsigned int l;
217 };
218 
219 inline idx4d unflat(unsigned int flatIdx, unsigned int n1, unsigned int n2,
220  unsigned int n3, unsigned int n4)
221 {
222  idx4d idx;
223  if (HPP_ARRAY_LAYOUT == LAPACK_ROW_MAJOR) {
224  idx.i = flatIdx/(n2*n3*n4);
225  idx.j = (flatIdx - idx.i*n2*n3*n4)/(n3*n4);
226  idx.k = (flatIdx - idx.i*n2*n3*n4 - idx.j*n3*n4)/(n4);
227  idx.l = (flatIdx - idx.i*n2*n3*n4 - idx.j*n3*n4 - idx.k*n4);
228  } else if (HPP_ARRAY_LAYOUT == LAPACK_COL_MAJOR) {
229  idx.i = flatIdx/(n3*n2*n1);
230  idx.j = (flatIdx - idx.i*n3*n2*n1)/(n2*n1);
231  idx.k = (flatIdx - idx.i*n3*n2*n1 - idx.j*n2*n1)/(n1);
232  idx.l = (flatIdx - idx.i*n3*n2*n1 - idx.j*n2*n1 - idx.k*n1);
233  }
234  else {
235  throw TensorError(std::string("No support for layout ")+std::to_string(HPP_ARRAY_LAYOUT));
236  }
237  return idx;
238 }
239 
240 template <typename T, typename U>
241 std::vector<T> unflatC(T flatIdx, std::vector<U> dims) {
242  // Dimension of the array
243  unsigned int rank = dims.size();
244 
245  // Array index to be returned
246  std::vector<T> idx(rank);
247 
248  // Remaining total of the flat index to account for
249  T remainingIdx = flatIdx;
250 
251  // The size of the stride at the current level
252  unsigned int stride = 1;
253  for (unsigned int i=0; i<rank; i++) {
254  stride *= dims[i];
255  }
256 
257  // Get the index
258  for (unsigned int i=0; i<rank; i++) {
259  // Stride is smaller at the next level
260  stride /= dims[i];
261 
262  // Get the index
263  idx[i] = remainingIdx/stride;
264 
265  // Subtract from the remaining index
266  remainingIdx -= idx[i]*stride;
267  }
268 
269  // Sanity check
270  if (remainingIdx != 0) {
271  throw std::runtime_error("Something has gone wrong with the index calculation.");
272  }
273 
274  // Return
275  return idx;
276 }
277 
278 template <typename T, typename U>
279 T flatC(std::vector<T> idx, std::vector<U> dims) {
280  unsigned int rank = dims.size();
281  if (idx.size() != rank) throw TensorError("Dimensions mismatch.");
282  T flatIdx = 0;
283  unsigned int stride = 1;
284  for (int i=rank-1; i>=0; i--) {
285  flatIdx += idx[i]*stride;
286  stride *= dims[i];
287  }
288  return flatIdx;
289 }
290 
291 // Additional functions for std::vector
292 template <typename T>
293 std::vector<T> ones(unsigned int n) {
294  std::vector<T> vec(n);
295  for (auto&& v : vec) {
296  v = 1.0;
297  }
298  return vec;
299 }
300 
301 // Forward declarations are necessarry for binary operations
302 template <typename T>
303 class Tensor2;
304 template <typename T>
305 class Tensor4;
306 
307 // CLASSES AND STRUCTS WITH TENSORS AS COMPONENTS
308 template <typename T>
312 };
313 
314 // TENSOR 2D //
316 
322 template <typename T>
323 class Tensor2
324 {
325  friend Tensor4<T>;
326  template<typename U, unsigned int M, unsigned int N> friend class Tensor2CUDA;
327 
328  public:
329  // Default constructor
330  Tensor2();
331 
332  // Basic zeroing constructor
333  Tensor2(const unsigned int n1, const unsigned int n2);
334 
335  // Copy constructor
336  Tensor2(const Tensor2<T> &A);
337 
338  // Getters
339  unsigned int getn1() const {return n1;}
340  unsigned int getn2() const {return n2;}
341  unsigned int getNVals() const {return nVals;}
342 
343  // Get read-only value by two indices
344  T getVal(const unsigned int i, const unsigned int j) const;
345 
346  // Get read-only value by single flat index
347  T getValFlat(const unsigned int flatIdx) const;
348 
349  // Get read/write reference to value
350  void setVal(const unsigned int i, const unsigned int j, T val);
351  T &operator()(const unsigned int i, const unsigned int j);
352  T &operator()(const unsigned int flatIdx);
353  void copyValuesOut(T* outVals) const;
354 
355  // Construct from Tensor4
356  explicit Tensor2(const Tensor4<T>& A);
357 
358  // Assignment operator
359  Tensor2<T>& operator=(const Tensor2<T>& input);
360 
361  // Printing
362  void printToStream(std::ostream& out) const;
363 
364  // HDF5 I/O, C++ interface
365  Tensor2(H5::DataSet& dataset, std::vector<hsize_t> gridOffset, std::vector<hsize_t> tensorDims);
366  void writeToExistingHDF5Dataset(H5::DataSet& dataset, std::vector<hsize_t> arrayOffset);
367 
368  Tensor2(H5::H5File& file, const H5std_string& datasetName, std::vector<hsize_t> tensorDims);
369  void writeToNewHDF5(const H5std_string& filename, const H5std_string& datasetName);
370  void addAsNewHDF5Dataset(H5::H5File& file, const H5std_string& datasetName);
371 
372  // HDF5 I/O, C interface
373  Tensor2(hid_t dset_id, hid_t plist_id, std::vector<hsize_t> gridOffset, std::vector<hsize_t> tensorDims);
374  void writeToExistingHDF5Dataset(hid_t dset_id, hid_t plist_id, std::vector<hsize_t> arrayOffset);
375 
376  // Inverse
377  void invInPlace();
378  Tensor2<T> inv() const;
379 
380  // Exponential
381  Tensor2<T> exp() const;
382 
383  // Assert squareness
384  void assertSquare() const;
385 
386  // Check if rotation matrix
387  bool isRotationMatrix() const;
388 
389  // Basic properties
390  T tr() const;
391  T det() const;
392  T min() const;
393  T max() const;
394  T absmax() const;
395  T spectralNorm() const;
396  T frobeniusNorm() const;
397 
398  // Product of all elements;
399  T prod() const;
400 
401  // Basic conversions
402  Tensor2<T> abs() const;
403  Tensor2<T> trans() const;
404 
405  // Constrain values
406  Tensor2<T> constrainedTo(const T minVal, const T maxVal) const;
407  void constrainInPlace(const T minVal, const T maxVal);
408  Tensor2<T> scaledToUnitDeterminant() const;
409 
410  // Deviatoric component
411  Tensor2<T> deviatoricComponent() const;
412 
413  // Decompositions
414  void evecDecomposition(std::valarray<T>& evals, Tensor2<T>& evecs);
415  Tensor2<T> sqrtMatrix();
416  PolarDecomposition<T> polarDecomposition() const;
417 
418  // FRIENDS
419 
420  // Dimension matching
421  template <typename U>
422  friend bool areSameShape(const Tensor2<U>& A, const Tensor2<U>& B);
423  template <typename U>
424  friend void assertSameShape(const Tensor2<U>& A, const Tensor2<U>& B);
425  template <typename U>
426  friend void assertCompatibleForMultiplication(const Tensor2<U>& A, const Tensor2<U>& B);
427  template <typename U>
428  friend void assertCompatibleForContraction(const Tensor4<U>& A, const Tensor2<U>& B);
429  template <typename U>
430  friend void assertCompatibleForContraction(const Tensor4<U>& A, const Tensor2<U>& B, const Tensor2<U>& C);
431 
432  // Equality
433  template <typename U>
434  friend bool areEqual(const Tensor2<U>& A, const Tensor2<U>& B, U tol);
435  template <typename U>
436  friend bool operator==(const Tensor2<U>& A, const Tensor2<U>& B);
437  template <typename U>
438  friend bool operator!=(const Tensor2<U>& A, const Tensor2<U>& B);
439 
440  // External operators
441  template <typename U>
442  friend Tensor2<U> operator*(const Tensor2<U>& A, const Tensor2<U>& B);
443  template <typename U>
444  friend void ABPlusBTransposeAInPlace(const hpp::Tensor2<U>& A, const hpp::Tensor2<U>& B, hpp::Tensor2<U>& C);
445  template <typename U>
446  friend Tensor2<U> contract(const Tensor4<U>& A, const Tensor2<U>& B);
447  template <typename U>
448  friend U contract(const Tensor2<U>& A, const Tensor2<U>& B);
449  template <typename U>
450  friend void contractInPlace(const Tensor4<U>& A, const Tensor2<U>& B, Tensor2<U>& C);
451  template <typename U>
452  friend void assertCompatibleForOuterProduct(const Tensor2<U>& A, const Tensor2<U>& B, const Tensor4<U>& C);
453  template <typename U>
454  friend Tensor4<U> outer(const Tensor2<U>& A, const Tensor2<U>& B);
455  template <typename U>
456  friend void outerInPlace(const Tensor2<U>& A, const Tensor2<U>& B, Tensor4<U>& C);
457 
458  // Addition
459  template <typename U>
460  friend Tensor2<U> operator+(const Tensor2<U>& A, const Tensor2<U>& B);
461  template <typename U>
462  friend Tensor2<U> operator+(const Tensor2<U>&A, const U&B);
463  template <typename U>
464  friend Tensor2<U> operator+(const U&A, const Tensor2<U>& B);
465  template <typename U>
466  friend void operator+=(Tensor2<U>& A, const U& B);
467  template <typename U>
468  friend void operator+=(Tensor2<U>& A, const Tensor2<U>& B);
469  template <typename U>
470  friend Tensor2<U> MPISum(Tensor2<U>& local, MPI_Comm comm);
471 
472  // Subtraction
473  template <typename U>
474  friend Tensor2<U> operator-(const Tensor2<U>& A, const Tensor2<U>& B);
475  template <typename U>
476  friend Tensor2<U> operator-(const Tensor2<U>& A, const U& B);
477  template <typename U>
478  friend Tensor2<U> operator-(const U&A, const Tensor2<U>& B);
479  template <typename U>
480  friend void operator-=(Tensor2<U>& A, const U& B);
481  template <typename U>
482  friend void operator-=(Tensor2<U>& A, const Tensor2<U>& B);
483 
484  // Multiplication
485  template <typename U>
486  friend Tensor2<U> operator*(const Tensor2<U>&A, const U &B);
487  template <typename U>
488  friend Tensor2<U> operator*(const U&A, const Tensor2<U>& B);
489  template <typename U>
490  friend void operator*=(Tensor2<U>& A, const U& B);
491 
492  // Division
493  template <typename U>
494  friend Tensor2<U> operator/(const Tensor2<U>&A, const U &B);
495  template <typename U>
496  friend void operator/=(Tensor2<U>& A, const U& B);
497 
498  // Special tensors
499  template <typename U>
500  friend void identityTensor2InPlace(unsigned int n, Tensor2<U>& A);
501 
502  protected:
504  unsigned int n1 = 0;
506  unsigned int n2 = 0;
508  unsigned int nVals = 0;
509 
511  std::valarray<T> vals;
512 
513  private:
514  // Initialization
515  void initialize(const unsigned int n1, const unsigned int n2);
516 
517  // Copy values from another tensor
518  void copyValues(const Tensor2<T>& input);
519 
520  // HDF I/O C++ API
521  void constructFromHDF5Dataset(H5::DataSet& dataset, std::vector<hsize_t> gridOffset, std::vector<hsize_t> tensorDims);
522 
523  // HDF I/O C API
524  void constructFromHDF5Dataset(hid_t dset_id, hid_t plist_id, std::vector<hsize_t> gridOffset, std::vector<hsize_t> tensorDims);
525 
526  // Construct from existing data
527  Tensor2(const unsigned int n1, const unsigned int n2, const T* inArray);
528  Tensor2(const unsigned int n1, const unsigned int n2, const std::valarray<T>& inVec);
529 
530  // Flattening and unflattening indices
531  unsigned int flat(const unsigned int i, const unsigned int j) const;
532  idx2d unflat(const unsigned int idx) const;
533 };
534 
535 // INLINE MEMBER FUNCTIONS //
536 
544 template <typename T>
545 inline unsigned int Tensor2<T>::flat(const unsigned int i, const unsigned int j) const
546 {
547  if (HPP_ARRAY_LAYOUT == LAPACK_ROW_MAJOR) {
548  return i*n2 + j;
549  } else if (HPP_ARRAY_LAYOUT == LAPACK_COL_MAJOR) {
550  return i + j*n1;
551  } else {
552  throw TensorError(std::string("No support for layout ")+std::to_string(HPP_ARRAY_LAYOUT));
553  }
554 }
555 
562 template <typename T>
563 inline idx2d Tensor2<T>::unflat(const unsigned int flat_idx) const
564 {
565  idx2d idx;
566  if (HPP_ARRAY_LAYOUT == LAPACK_ROW_MAJOR) {
567  idx.i = flat_idx/n2;
568  idx.j = flat_idx - idx.i*n2;
569  } else if (HPP_ARRAY_LAYOUT == LAPACK_COL_MAJOR) {
570  idx.j = flat_idx/n1;
571  idx.i = flat_idx - idx.j*n1;
572  } else {
573  throw TensorError(std::string("No support for layout ")+std::to_string(HPP_ARRAY_LAYOUT));
574  }
575 
576  return idx;
577 }
578 
587 template <typename T>
588 inline T Tensor2<T>::getVal(unsigned int i, unsigned int j) const
589 {
590  unsigned int flatIdx = this->flat(i,j);
591  return vals[flatIdx];
592 }
593 
603 template <typename T>
604 inline T Tensor2<T>::getValFlat(unsigned int flatIdx) const
605 {
606  return vals[flatIdx];
607 }
608 
617 template <typename T>
618 inline T& Tensor2<T>::operator()(const unsigned int i, const unsigned int j)
619 {
620  unsigned int flatIdx = this->flat(i,j);
621  return vals[flatIdx];
622 }
623 
624 template <typename T>
625 inline T& Tensor2<T>::operator()(const unsigned int flatIdx)
626 {
627  return vals[flatIdx];
628 }
629 
630 template <typename T>
631 void Tensor2<T>::setVal(const unsigned int i, const unsigned int j, T val) {
632  unsigned int flatIdx = this->flat(i,j);
633  vals[flatIdx] = val;
634 }
635 
637 
638 // Outer product with std::vector
639 template <typename T>
640 Tensor2<T> outer(const std::vector<T>& A, const std::vector<T>& B) {
641  int n1 = A.size();
642  int n2 = B.size();
643  Tensor2<T> C(n1,n2);
644  for (int i=0; i<n1; i++) {
645  for (int j=0; j<n2; j++) {
646  C(i,j) = A[i]*B[j];
647  }
648  }
649  return C;
650 }
651 
652 template <typename T>
653 void outerInPlace(const std::vector<T>& A, const std::vector<T>& B, Tensor2<T>& C) {
654  unsigned int n1 = A.size();
655  unsigned int n2 = B.size();
656  DEBUG_ONLY(if (n1 != C.getn1() || n2 != C.getn2()) throw TensorError("Size mismatch."););
657  for (unsigned int i=0; i<n1; i++) {
658  for (unsigned int j=0; j<n2; j++) {
659  C(i,j) = A[i]*B[j];
660  }
661  }
662 }
663 
664 // Outer product with std::valarray
665 template <typename T>
666 Tensor2<T> outer(const std::valarray<T>& A, const std::valarray<T>& B) {
667  int n1 = A.size();
668  int n2 = B.size();
669  Tensor2<T> C(n1,n2);
670  for (int i=0; i<n1; i++) {
671  for (int j=0; j<n2; j++) {
672  C(i,j) = A[i]*B[j];
673  }
674  }
675  return C;
676 }
677 
678 // Basic tensors
679 template<typename T>
681 {
682  Tensor2<T> A = Tensor2<T>(n,n);
683  for (unsigned int i=0; i<n; i++) {
684  for (unsigned int j=0; j<n; j++) {
685  A(i,j) = i==j;
686  }
687  }
688  return A;
689 }
690 
691 template<typename T>
692 void identityTensor2InPlace(unsigned int n, Tensor2<T>& A)
693 {
694  if (A.getn1() != n || A.getn2() != n) {
695  throw std::runtime_error("Wrong dimensions.");
696  }
697  A.vals = (T)0.0;
698  for (unsigned int i=0; i<n; i++) {
699  A(i,i) = (T)1.0;
700  }
701 }
702 
703 template<typename T>
704 Tensor2<T> ones2(unsigned int n)
705 {
706  Tensor2<T> A = Tensor2<T>(n,n);
707  for (unsigned int i=0; i<A.getNVals(); i++) {
708  A(i) = 1.0;
709  }
710  return A;
711 }
712 
713 // Basic tensors
714 template<typename T>
715 Tensor2<T> diag2(const std::vector<T>& vals)
716 {
717  unsigned int n = vals.size();
718  Tensor2<T> A(n,n);
719  for (unsigned int i=0; i<n; i++) {
720  A(i,i) = vals[i];
721  }
722  return A;
723 }
724 
725 template<typename T>
726 Tensor2<T> diag2(const std::valarray<T>& vals)
727 {
728  unsigned int n = vals.size();
729  Tensor2<T> A(n,n);
730  for (unsigned int i=0; i<n; i++) {
731  A(i,i) = vals[i];
732  }
733  return A;
734 }
735 
736 template<typename T>
737 bool areEqual(const Tensor2<T>& A, const Tensor2<T>& B, T tol) {
738  if (A.n1 != B.n1) return false;
739  if (A.n2 != B.n2) return false;
740  bool areEq = true;
741  for (unsigned int i=0; i<A.getNVals(); i++) {
742  T diff = A.getValFlat(i)-B.getValFlat(i);
743  if (std::abs(diff) > tol) {
744  areEq = false;
745  break;
746  }
747  }
748  return areEq;
749 }
750 
760 template <typename T>
761 bool operator==(const Tensor2<T>& A, const Tensor2<T>& B) {
762  return areEqual(A, B, (T)(10.0*std::numeric_limits<T>::epsilon()));
763 }
764 
765 // Inequality
766 template <typename T>
767 bool operator!=(const Tensor2<T>& A, const Tensor2<T>& B) {
768  return !(A==B);
769 }
770 
771 // Compatability
772 template <typename T>
773 inline bool areSameShape(const Tensor2<T>& A, const Tensor2<T>& B) {
774  return (A.n1 == B.n1 && A.n2 == B.n2);
775 }
776 
777 template <typename T>
778 void assertSameShape(const Tensor2<T>& A, const Tensor2<T>& B) {
779  if (!areSameShape(A,B)) {
780  throw TensorError(std::string("Dimension mismatch."));
781  }
782 }
783 
784 // ADDITION
785 
793 template <typename T>
795  assertSameShape(A,B);
796  Tensor2<T> C = A;
797  C.vals += B.vals;
798  return C;
799 }
800 
801 template <typename T>
802 void operator+=(Tensor2<T>& A, const Tensor2<T>& B) {
803  A.vals += B.vals;
804 }
805 
806 template <typename T>
807 Tensor2<T> operator+(const Tensor2<T>& A, const T& B) {
808  Tensor2<T> C = A;
809  C.vals += B;
810  return C;
811 }
812 
813 template <typename T>
814 Tensor2<T> operator+(const T&A, const Tensor2<T>& B) {
815  return B+A;
816 }
817 
818 template <typename T>
819 void operator+=(Tensor2<T>& A, const T& B) {
820  A.vals += B;
821 }
822 
823 template <typename T>
824 Tensor2<T> MPISum(Tensor2<T>& local, MPI_Comm comm) {
825  Tensor2<T> global(local.getn1(), local.getn2());
826  MPI_Allreduce(&(local.vals[0]), &(global.vals[0]), local.getNVals(), MPIType<T>(), MPI_SUM, comm);
827  return global;
828 }
829 
830 // Subtraction
831 template <typename T>
833  assertSameShape(A,B);
834  Tensor2<T> C = A;
835  C.vals -= B.vals;
836  return C;
837 }
838 
845 template <typename T>
846 void operator-=(Tensor2<T>& A, const Tensor2<T>& B) {
847  A.vals -= B.vals;
848 }
849 
850 template <typename T>
851 Tensor2<T> operator-(const Tensor2<T>& A, const T& B) {
852  Tensor2<T> C = A;
853  C.vals -= B;
854  return C;
855 }
856 
857 template <typename T>
858 Tensor2<T> operator-(const T&A, const Tensor2<T>& B) {
859  return B-A;
860 }
861 
862 template <typename T>
863 void operator-=(Tensor2<T>& A, const T& B) {
864  A.vals -= B;
865 }
866 // Multiplication
867 // Note: we haven't implemented element-wise multiplication with another tensor,
868 // because it would conflict with normal matrix multiplication
869 
870 template <typename T>
871 Tensor2<T> operator*(const Tensor2<T>& A, const T& B) {
872  Tensor2<T> C = A;
873  C.vals *= B;
874  return C;
875 }
876 
877 template <typename T>
878 Tensor2<T> operator*(const T&A, const Tensor2<T>& B) {
879  return B*A;
880 }
881 
882 template <typename T>
883 void operator*=(Tensor2<T>& A, const T& B) {
884  A.vals *= B;
885 }
886 
887 /* Division
888 Note: we haven't implemented element-wise division with another tensor,
889 or scalar divided by tensor, because the meaning of those operators has a lot
890 of room for ambiguity (thanks MATLAB)
891 */
892 template <typename T>
893 Tensor2<T> operator/(const Tensor2<T>& A, const T& B) {
894  Tensor2<T> C = A;
895  C.vals /= B;
896  return C;
897 }
898 
899 template <typename T>
900 void operator/=(Tensor2<T>& A, const T& B) {
901  A.vals /= B;
902 }
903 
904 template <typename T>
906  if (A.n2 != B.n1) {
907  throw TensorError("Shapes are incompatible for multiplication.");
908  }
909 }
910 
911 // Tensor multiplication
912 template<typename T>
914 {
916  unsigned int m = A.n1;
917  unsigned int n = A.n2;
918  unsigned int p = B.n2;
919  Tensor2<T> C = Tensor2<T>(m,p);
920  for (unsigned int i=0; i<m; i++) {
921  for (unsigned int j=0; j<p; j++) {
922  for (unsigned int k=0; k<n; k++) {
923  C(i,j) += A.getVal(i,k)*B.getVal(k,j);
924  }
925  }
926  }
927  return C;
928 }
929 
930 //
931 template <typename T>
933  A.assertSquare();
934  assertSameShape(A,B);
935  assertSameShape(B,C);
936  unsigned int m = A.n1;
937  C.vals = (T)0.0;
938  for (unsigned int i=0; i<m; i++) {
939  for (unsigned int j=0; j<m; j++) {
940  for (unsigned int k=0; k<m; k++) {
941  C(i,j) += A.getVal(i,k)*B.getVal(k,j);
942  C(i,j) += B.getVal(k,i)*A.getVal(k,j);
943  }
944  }
945  }
946 }
947 
948 // Tensor contraction
949 template<typename T>
950 T contract(const Tensor2<T>& A, const Tensor2<T>& B)
951 {
952  assertSameShape(A,B);
953  T C = 0;
954  for (unsigned int i=0; i<A.getNVals(); i++) {
955  C += A.getValFlat(i)*B.getValFlat(i);
956  }
957  return C;
958 }
959 
960 // Stream output
961 template <typename T>
962 std::ostream& operator<<(std::ostream& out, const Tensor2<T>& A)
963 {
964  A.printToStream(out);
965  return out;
966 }
967 
968 
969 // TENSOR 4D //
971 
980 template <typename T>
981 class Tensor4
982 {
983  friend Tensor2<T>;
984  template<typename U, unsigned int M, unsigned int N, unsigned int P, unsigned int Q> friend class Tensor4CUDA;
985 
986 public:
987  // Default constructor
988  Tensor4();
989 
990  // Constructor and desctructor
991  Tensor4(unsigned int n1, unsigned int n2, unsigned int n3, unsigned int n4);
992  ~Tensor4();
993 
994  // Construct from 2nd order
995  explicit Tensor4(const Tensor2<T>& A);
996 
997  // Assignment operator
998  Tensor4<T>& operator=(const Tensor4<T>& input);
999 
1000  // Copy constructor
1001  Tensor4(const Tensor4<T> &input);
1002 
1003  // Getters
1004  unsigned int getNVals() const {return nVals;}
1005 
1006  // Read/write access
1007  T& operator()(unsigned int i, unsigned int j, unsigned int k, unsigned int l);
1008  T& operator()(unsigned int flatIdx);
1009 
1010  // Read only access
1011  T getVal(unsigned int i, unsigned int j, unsigned int k, unsigned int l) const;
1012  T getValFlat(unsigned int flatIdx) const;
1013 
1014  // Basic properties
1015  T frobeniusNorm() const;
1016 
1017  // Inverse
1018  void invInPlace();
1019  Tensor4<T> inv() const;
1020 
1021  // Printing
1022  void printToStream(std::ostream& out);
1023 
1024  // Getters
1025  unsigned int getn1() const {return n1;}
1026  unsigned int getn2() const {return n2;}
1027  unsigned int getn3() const {return n3;}
1028  unsigned int getn4() const {return n4;}
1029 
1030  // EXTERNAL FRIEND FUNCTIONS
1031 
1032  // Equality
1033  template <typename U>
1034  friend bool areSameShape(const Tensor4<U>& A, const Tensor4<U>& B);
1035  template <typename U>
1036  friend void assertSameShape(const Tensor4<U>& A, const Tensor4<U>& B);
1037  template <typename U>
1038  friend bool operator==(const Tensor4<U>& A, const Tensor4<U>& B);
1039  template <typename U>
1040  friend bool operator!=(const Tensor4<U>& A, const Tensor4<U>& B);
1041 
1042  // Contraction
1043  template <typename U>
1044  friend void assertCompatibleForContraction(const Tensor4<U>& A, const Tensor2<U>& B);
1045  template <typename U>
1046  friend void assertCompatibleForContraction(const Tensor4<U>& A, const Tensor2<U>& B, const Tensor2<U>& C);
1047  template <typename U>
1048  friend Tensor2<U> contract(const Tensor4<U>& A, const Tensor2<U>& B);
1049  template <typename U>
1050  friend void contractInPlace(const Tensor4<U>& A, const Tensor2<U>& B, Tensor2<U>& C);
1051  template <typename U>
1052  friend void assertCompatibleForContraction(const Tensor4<U>& A, const Tensor4<U>& B);
1053  template <typename U>
1054  friend Tensor4<U> contract(const Tensor4<U>& A, const Tensor4<U>& B);
1055 
1056  // Products
1057  template <typename U>
1058  friend void assertCompatibleForOuterProduct(const Tensor2<U>& A, const Tensor2<U>& B, const Tensor4<U>& C);
1059  template <typename U>
1060  friend void outerInPlace(const Tensor2<U>& A, const Tensor2<U>& B, Tensor4<U>& C);
1061 
1062  // Addition
1063  template <typename U>
1064  friend Tensor4<U> operator+(const Tensor4<U>& A, const Tensor4<U>& B);
1065  template <typename U>
1066  friend Tensor4<U> operator+(const Tensor4<U>&A, const U&B);
1067  template <typename U>
1068  friend Tensor4<U> operator+(const U&A, const Tensor4<U>& B);
1069  template <typename U>
1070  friend void operator+=(Tensor4<U>& A, const U& B);
1071  template <typename U>
1072  friend void operator+=(Tensor4<U>& A, const Tensor4<U>& B);
1073 
1074  // Subtraction
1075  template <typename U>
1076  friend Tensor4<U> operator-(const Tensor4<U>& A);
1077  template <typename U>
1078  friend Tensor4<U> operator-(const Tensor4<U>& A, const Tensor4<U>& B);
1079  template <typename U>
1080  friend Tensor4<U> operator-(const Tensor4<U>& A, const U& B);
1081  template <typename U>
1082  friend Tensor4<U> operator-(const U&A, const Tensor4<U>& B);
1083  template <typename U>
1084  friend void operator-=(Tensor4<U>& A, const U& B);
1085  template <typename U>
1086  friend void operator-=(Tensor4<U>& A, const Tensor4<U>& B);
1087 
1088  // Multiplication
1089  template <typename U>
1090  friend Tensor4<U> operator*(const Tensor4<U>&A, const U &B);
1091  template <typename U>
1092  friend Tensor4<U> operator*(const U&A, const Tensor4<U>& B);
1093  template <typename U>
1094  friend void operator*=(Tensor4<U>& A, const U& B);
1095 
1096  // Division
1097  template <typename U>
1098  friend Tensor4<U> operator/(const Tensor4<U>&A, const U &B);
1099  template <typename U>
1100  friend void operator/=(Tensor4<U>& A, const U& B);
1101 
1102 protected:
1103  unsigned int n1 = 0;
1104  unsigned int n2 = 0;
1105  unsigned int n3 = 0;
1106  unsigned int n4 = 0;
1107  unsigned int nVals = 0;
1108 
1109  // Underlying contiguous array
1110  std::valarray<T> vals;
1111 
1112 private:
1113 
1114  // Initialization
1115  void initialize(unsigned int n1, unsigned int n2, unsigned int n3, unsigned int n4);
1116 
1117  // Flattening and unflattening indices
1118  unsigned int flat(unsigned int i, unsigned int j, unsigned int k, unsigned int l) const;
1119  idx4d unflat(unsigned int idx);
1120 
1121  void copyValues(const Tensor4<T>& input);
1122 };
1123 
1124 // INLINE MEMBER FUNCTIONS //
1125 
1136 template <typename T>
1137 inline unsigned int Tensor4<T>::flat(unsigned int i, unsigned int j, unsigned int k, unsigned int l) const
1138 {
1139  return i*n2*n3*n4 + j*n3*n4 + k*n4 + l;
1140 }
1141 
1148 template <typename T>
1149 inline idx4d Tensor4<T>::unflat(unsigned int flat_idx)
1150 {
1151  idx4d idx;
1152  idx.i = flat_idx/(n2*n3*n4);
1153  idx.j = (flat_idx - idx.i*n2*n3*n4)/(n3*n4);
1154  idx.k = (flat_idx - idx.i*n2*n3*n4 - idx.j*n3*n4)/(n4);
1155  idx.l = (flat_idx - idx.i*n2*n3*n4 - idx.j*n3*n4 - idx.k*n4);
1156  return idx;
1157 }
1158 
1159 // Get values
1160 template <typename T>
1161 inline T Tensor4<T>::getVal(unsigned int i, unsigned int j, unsigned int k, unsigned int l) const
1162 {
1163  unsigned int flatIdx = this->flat(i,j,k,l);
1164  return vals[flatIdx];
1165 }
1166 
1167 // Get values
1168 template <typename T>
1169 inline T Tensor4<T>::getValFlat(unsigned int flatIdx) const
1170 {
1171  return vals[flatIdx];
1172 }
1173 
1174 template <typename T>
1175 inline T& Tensor4<T>::operator()(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
1176 {
1177  unsigned int flatIdx = this->flat(i,j,k,l);
1178  return vals[flatIdx];
1179 }
1180 
1181 template <typename T>
1182 inline T& Tensor4<T>::operator()(unsigned int flatIdx)
1183 {
1184  return vals[flatIdx];
1185 }
1186 
1188 
1189 // Stream output
1190 template <typename T>
1191 std::ostream& operator<<(std::ostream& out, Tensor4<T>& A)
1192 {
1193  A.printToStream(out);
1194  return out;
1195 }
1196 
1197 // Basic tensors
1198 template<typename T>
1199 inline Tensor4<T> identityTensor4(unsigned int n)
1200 {
1201  Tensor4<T> A = Tensor4<T>(n,n,n,n);
1202  for (unsigned int i=0; i<n; i++) {
1203  for (unsigned int j=0; j<n; j++) {
1204  for (unsigned int k=0; k<n; k++) {
1205  for (unsigned int l=0; l<n; l++) {
1206  A(i,j,k,l) = (i==k)*(j==l);
1207  }
1208  }
1209  }
1210  }
1211  return A;
1212 }
1213 
1214 template<typename T>
1215 void identityTensor4InPlace(unsigned int n, Tensor4<T>& A)
1216 {
1217  if (A.getn1() != n || A.getn2() != n || A.getn3() != n || A.getn4() != n) {
1218  throw std::runtime_error("Wrong dimensions.");
1219  }
1220  for (unsigned int i=0; i<n; i++) {
1221  for (unsigned int j=0; j<n; j++) {
1222  for (unsigned int k=0; k<n; k++) {
1223  for (unsigned int l=0; l<n; l++) {
1224  A(i,j,k,l) = (i==k)*(j==l);
1225  }
1226  }
1227  }
1228  }
1229 }
1230 
1231 // Compatability
1232 template <typename T>
1233 inline bool areSameShape(const Tensor4<T>& A, const Tensor4<T>& B) {
1234  return (A.n1 == B.n1 && A.n2 == B.n2 && A.n3 == B.n3 && A.n4 == B.n4);
1235 }
1236 
1237 template <typename T>
1238 void assertSameShape(const Tensor4<T>& A, const Tensor4<T>& B) {
1239  if (!areSameShape(A,B)) {
1240  throw TensorError(std::string("Dimension mismatch."));
1241  }
1242 }
1243 
1253 template <typename T>
1254 bool operator==(const Tensor4<T>& A, const Tensor4<T>& B) {
1255  if (A.n1 != B.n1) return false;
1256  if (A.n2 != B.n2) return false;
1257  if (A.n3 != B.n3) return false;
1258  if (A.n4 != B.n4) return false;
1259  bool areEqual = true;
1260  for (unsigned int i=0; i<A.getNVals(); i++) {
1261  T diff = A.getValFlat(i)-B.getValFlat(i);
1262  if (std::abs(diff) > 10.0*std::numeric_limits<T>::epsilon()) {
1263  areEqual = false;
1264  break;
1265  }
1266  }
1267  return areEqual;
1268 }
1269 
1270 // Inequality
1271 template <typename T>
1272 bool operator!=(const Tensor4<T>& A, const Tensor4<T>& B) {
1273  return !(A==B);
1274 }
1275 
1283 template <typename T>
1285  assertSameShape(A,B);
1286  Tensor4<T> C = A;
1287  C.vals += B.vals;
1288  return C;
1289 }
1290 
1291 template <typename T>
1292 void operator+=(Tensor4<T>& A, const Tensor4<T>& B) {
1293  A.vals += B.vals;
1294 }
1295 
1296 template <typename T>
1297 Tensor4<T> operator+(const Tensor4<T>& A, const T& B) {
1298  Tensor4<T> C = A;
1299  C.vals += B;
1300  return C;
1301 }
1302 
1303 template <typename T>
1304 Tensor4<T> operator+(const T&A, const Tensor4<T>& B) {
1305  return B+A;
1306 }
1307 
1308 template <typename T>
1309 void operator+=(Tensor4<T>& A, const T& B) {
1310  A.vals += B;
1311 }
1312 
1313 // Unary negation
1314 template <typename T>
1316  Tensor4<T> C = A;
1317  C.vals *= -1.0;
1318  return C;
1319 }
1320 
1321 // Subtraction
1322 template <typename T>
1324  assertSameShape(A,B);
1325  Tensor4<T> C = A;
1326  C.vals -= B.vals;
1327  return C;
1328 }
1329 
1336 template <typename T>
1337 void operator-=(Tensor4<T>& A, const Tensor4<T>& B) {
1338  A.vals -= B.vals;
1339 }
1340 
1341 template <typename T>
1342 Tensor4<T> operator-(const Tensor4<T>& A, const T& B) {
1343  Tensor4<T> C = A;
1344  C.vals -= B;
1345  return C;
1346 }
1347 
1348 template <typename T>
1349 Tensor4<T> operator-(const T&A, const Tensor4<T>& B) {
1350  return B-A;
1351 }
1352 
1353 template <typename T>
1354 void operator-=(Tensor4<T>& A, const T& B) {
1355  A.vals -= B;
1356 }
1357 // Multiplication
1358 // Note: we haven't implemented element-wise multiplication with another tensor,
1359 // because it would conflict with normal matrix multiplication
1360 
1361 template <typename T>
1362 Tensor4<T> operator*(const Tensor4<T>& A, const T& B) {
1363  Tensor4<T> C = A;
1364  C.vals *= B;
1365  return C;
1366 }
1367 
1368 template <typename T>
1369 Tensor4<T> operator*(const T&A, const Tensor4<T>& B) {
1370  return B*A;
1371 }
1372 
1373 template <typename T>
1374 void operator*=(Tensor4<T>& A, const T& B) {
1375  A.vals *= B;
1376 }
1377 
1378 /* Division
1379 Note: we haven't implemented element-wise division with another tensor,
1380 or scalar divided by tensor, because the meaning of those operators has a lot
1381 of room for ambiguity (thanks MATLAB)
1382 */
1383 template <typename T>
1384 Tensor4<T> operator/(const Tensor4<T>& A, const T& B) {
1385  Tensor4<T> C = A;
1386  C.vals /= B;
1387  return C;
1388 }
1389 
1390 template <typename T>
1391 void operator/=(Tensor4<T>& A, const T& B) {
1392  A.vals /= B;
1393 }
1394 
1395 // TENSOR 2 AND TENSOR 4 INTERACTIONS
1396 
1397 // Outer product
1398 template<typename T>
1400 {
1401  unsigned int m = A.n1;
1402  unsigned int n = A.n2;
1403  unsigned int p = B.n1;
1404  unsigned int q = B.n2;
1405  Tensor4<T> C(m,n,p,q);
1406  for (unsigned int i=0; i<m; i++) {
1407  for (unsigned int j=0; j<n; j++) {
1408  for (unsigned int k=0; k<p; k++) {
1409  for (unsigned int l=0; l<q; l++) {
1410  C(i,j,k,l) = A.getVal(i,j)*B.getVal(k,l);
1411  }
1412  }
1413  }
1414  }
1415  return C;
1416 }
1417 
1418 template <typename T>
1420  if (A.n1 != C.n1 || A.n2 != C.n2 || B.n1 != C.n3 || B.n2 != C.n4) {
1421  throw TensorError("Shapes are incompatible for contraction.");
1422  }
1423 }
1424 
1425 template<typename T>
1426 void outerInPlace(const Tensor2<T>& A, const Tensor2<T>& B, Tensor4<T>& C)
1427 {
1428  unsigned int m = A.n1;
1429  unsigned int n = A.n2;
1430  unsigned int p = B.n1;
1431  unsigned int q = B.n2;
1433  for (unsigned int i=0; i<m; i++) {
1434  for (unsigned int j=0; j<n; j++) {
1435  for (unsigned int k=0; k<p; k++) {
1436  for (unsigned int l=0; l<q; l++) {
1437  C(i,j,k,l) = A.getVal(i,j)*B.getVal(k,l);
1438  }
1439  }
1440  }
1441  }
1442 }
1443 
1444 //
1445 template <typename T>
1446 inline void assertCompatibleForContraction(const Tensor4<T>& A, const Tensor2<T>& B) {
1447  if (A.n3 != B.n1 || A.n4 != B.n2) {
1448  throw TensorError("Shapes are incompatible for contraction.");
1449  }
1450 }
1451 
1452 template <typename T>
1453 inline void assertCompatibleForContraction(const Tensor4<T>& A, const Tensor2<T>& B, const Tensor2<T>& C) {
1454  if (A.n1 != C.n1 || A.n2 != C.n2 || A.n3 != B.n1 || A.n4 != B.n2) {
1455  throw TensorError("Shapes are incompatible for contraction.");
1456  }
1457 }
1458 
1459 // Tensor contraction
1460 template <typename T>
1461 inline Tensor2<T> contract(const Tensor4<T>& A, const Tensor2<T>& B)
1462 {
1464  unsigned int m = A.n1;
1465  unsigned int n = A.n2;
1466  unsigned int p = A.n3;
1467  unsigned int q = A.n4;
1468  Tensor2<T> C = Tensor2<T>(m,n);
1469  for (unsigned int i=0; i<m; i++) {
1470  for (unsigned int j=0; j<n; j++) {
1471  for (unsigned int k=0; k<p; k++) {
1472  for (unsigned int l=0; l<q; l++) {
1473  C(i,j) += A.getVal(i,j,k,l)*B.getVal(k,l);
1474  }
1475  }
1476  }
1477  }
1478  return C;
1479 }
1480 
1481 template <typename T>
1482 inline void contractInPlace(const Tensor4<T>& A, const Tensor2<T>& B, Tensor2<T>& C)
1483 {
1485  unsigned int m = A.n1;
1486  unsigned int n = A.n2;
1487  unsigned int p = A.n3;
1488  unsigned int q = A.n4;
1489  C.vals = (T)0.0;
1490  for (unsigned int i=0; i<m; i++) {
1491  for (unsigned int j=0; j<n; j++) {
1492  for (unsigned int k=0; k<p; k++) {
1493  for (unsigned int l=0; l<q; l++) {
1494  C(i,j) += A.getVal(i,j,k,l)*B.getVal(k,l);
1495  }
1496  }
1497  }
1498  }
1499 }
1500 
1501 template <typename U>
1502 inline void assertCompatibleForContraction(const Tensor4<U>& A, const Tensor4<U>& B) {
1503  if (A.n3 != B.n1 || A.n4 != B.n2) {
1504  throw TensorError("Shapes are incompatible for contraction.");
1505  }
1506 }
1507 
1508 template <typename U>
1509 inline Tensor4<U> contract(const Tensor4<U>& A, const Tensor4<U>& B) {
1511  unsigned int m = A.n1;
1512  unsigned int n = A.n2;
1513  unsigned int p = A.n3;
1514  unsigned int q = A.n4;
1515  unsigned int r = B.n3;
1516  unsigned int s = B.n4;
1517  Tensor4<U> C(m,n,r,s);
1518  for (unsigned int im=0; im<m; im++) {
1519  for (unsigned int in=0; in<n; in++) {
1520  for (unsigned int ir=0; ir<r; ir++) {
1521  for (unsigned int is=0; is<s; is++) {
1522  C(im,in,ir,is) = 0.0;
1523  for (unsigned int ip=0; ip<p; ip++) {
1524  for (unsigned int iq=0; iq<q; iq++) {
1525  C(im,in,ir,is) += A.getVal(im,in,ip,iq)*B.getVal(ip,iq,ir,is);
1526  }
1527  }
1528  }
1529  }
1530  }
1531  }
1532  return C;
1533 }
1534 
1535 // Interactions with std::vector
1536 template <typename T>
1537 std::vector<T> operator*(const hpp::Tensor2<T>& A, const std::vector<T>& x)
1538 {
1539  unsigned int m = A.getn1();
1540  unsigned int n = A.getn2();
1541  if (n != x.size()) {
1542  throw hpp::TensorError("Incompatible tensor vector multiplication sizes.");
1543  }
1544  std::vector<T> b(m);
1545  for (unsigned int i=0; i<m; i++) {
1546  for (unsigned int j=0; j<n; j++) {
1547  b[i] += A.getVal(i,j)*x[j];
1548  }
1549  }
1550  return b;
1551 }
1552 
1553 // Interactions with std::valarray
1554 template <typename T>
1555 std::valarray<T> operator*(const hpp::Tensor2<T>& A, const std::valarray<T>& x)
1556 {
1557  unsigned int m = A.getn1();
1558  unsigned int n = A.getn2();
1559  if (n != x.size()) {
1560  throw hpp::TensorError("Incompatible tensor vector multiplication sizes.");
1561  }
1562  std::valarray<T> b(m);
1563  for (unsigned int i=0; i<m; i++) {
1564  for (unsigned int j=0; j<n; j++) {
1565  b[i] += A.getVal(i,j)*x[j];
1566  }
1567  }
1568  return b;
1569 }
1570 
1571 // Interactions with std::vector
1578 template <typename T>
1579 hpp::Tensor2<T> operator*(const std::vector<T>& x, const hpp::Tensor2<T>& A)
1580 {
1581  unsigned int m = A.getn1();
1582  unsigned int n = A.getn2();
1583  if (n != x.size()) {
1584  throw hpp::TensorError("Incompatible vector tensor multiplication sizes.");
1585  }
1586  hpp::Tensor2<T> B = A;
1587  for (unsigned int i=0; i<m; i++) {
1588  for (unsigned int j=0; j<n; j++) {
1589  B(i,j) *= x[j];
1590  }
1591  }
1592  return B;
1593 }
1594 
1601 template <typename T>
1603  return Q.trans()*A*Q;
1604 }
1605 
1612 template <typename T>
1614  return Q*A_star*Q.trans();
1615 }
1616 
1624 template <typename T>
1626  Tensor4<T> E(3,3,3,3);
1627  for (int m=0; m<3; m++) {
1628  for (int n=0; n<3; n++) {
1629  for (int p=0; p<3; p++) {
1630  for (int q=0; q<3; q++) {
1631  T val = 0.0;
1632  for (int i=0; i<3; i++) {
1633  for (int j=0; j<3; j++) {
1634  for (int k=0; k<3; k++) {
1635  for (int l=0; l<3; l++) {
1636  val += Q.getVal(m,i)*Q.getVal(n,j)*Q.getVal(p,k)*Q.getVal(q,l)*E_star.getVal(i,j,k,l);
1637  }
1638  }
1639  }
1640  }
1641  E(m,n,p,q) = val;
1642  }
1643  }
1644  }
1645  }
1646  return E;
1647 }
1648 
1649 } //END NAMESPACE HPP
1650 
1651 #endif /* HPP_TENSOR_H */
unsigned int getNVals() const
Definition: tensor.h:341
Tensor2< T > transformOutOfFrame(const Tensor2< T > &A_star, const Tensor2< T > &Q)
Transform tensor out of the frame given by the columns of .
Definition: tensor.h:1613
void operator+=(GSHCoeffs< T > &A, const GSHCoeffs< T > &B)
Definition: gsh.h:231
Tensor2< T > diag2(const std::vector< T > &vals)
Definition: tensor.h:715
void ABPlusBTransposeAInPlace(const hpp::Tensor2< T > &A, const hpp::Tensor2< T > &B, hpp::Tensor2< T > &C)
Definition: tensor.h:932
Tensor2< T > outer(const std::vector< T > &A, const std::vector< T > &B)
Definition: tensor.h:640
unsigned int j
Definition: tensor.h:214
Tensor2< T > transformIntoFrame(const Tensor2< T > &A, const Tensor2< T > &Q)
Transform tensor into the frame given by the columns of .
Definition: tensor.h:1602
Definition: tensorCUDA.h:28
Definition: tensor.h:305
unsigned int getn2() const
Definition: tensor.h:1026
Tensor2< T > ones2(unsigned int n)
Definition: tensor.h:704
unsigned int flat(unsigned int i, unsigned int j, unsigned int k, unsigned int l) const
Flatten the four indices of the tensor.
Definition: tensor.h:1137
void operator/=(GSHCoeffs< T > &A, const T B)
Definition: gsh.h:257
Definition: casesUtils.cpp:4
void operator-=(Tensor2< T > &A, const Tensor2< T > &B)
Definition: tensor.h:846
void assertSameShape(const Tensor2< T > &A, const Tensor2< T > &B)
Definition: tensor.h:778
Header file for helper functions with HDF, C++ API.
std::vector< T > operator*(const std::vector< T > &vec, const T scalar)
Definition: tensor.h:72
unsigned int j
Definition: tensor.h:182
Tensor2< T > R
Definition: tensor.h:310
unsigned int getn1() const
Definition: tensor.h:1025
unsigned int flat(const unsigned int i, const unsigned int j) const
Flatten the two indices of the tensor.
Definition: tensor.h:545
T MPISum(const T &localVal, MPI_Comm comm)
Definition: mpiUtils.h:66
unsigned int getn3() const
Definition: tensor.h:1027
Tensor2< T > U
Definition: tensor.h:311
Tensor2< T > identityTensor2(unsigned int n)
Definition: tensor.h:680
A 2D index.
Definition: tensor.h:180
T getValFlat(unsigned int flatIdx) const
Definition: tensor.h:1169
std::vector< T > ones(unsigned int n)
Definition: tensor.h:293
GSHCoeffs< T > operator/(const GSHCoeffs< T > &coeffs, const T val)
Definition: gsh.h:236
void operator*=(std::vector< T > &vec, const T scalar)
Definition: tensor.h:58
unsigned int getn4() const
Definition: tensor.h:1028
T getVal(const unsigned int i, const unsigned int j) const
Get the value of .
Definition: tensor.h:588
void assertCompatibleForContraction(const Tensor4< T > &A, const Tensor2< T > &B)
Definition: tensor.h:1446
std::vector< T > unflatC(T flatIdx, std::vector< U > dims)
Definition: tensor.h:241
Header file for helper functions with HDF.
idx4d unflat(unsigned int idx)
Unflatten the four indices of the tensor.
Definition: tensor.h:1149
Definition: tensor.h:309
bool operator==(const SpectralDatasetID &l, const SpectralDatasetID &r)
Definition: spectralUtils.cpp:132
A 4D index.
Definition: tensor.h:212
unsigned int i
Definition: tensor.h:213
#define DEBUG_ONLY(x)
Definition: config.h:34
void assertCompatibleForOuterProduct(const Tensor2< T > &A, const Tensor2< T > &B, const Tensor4< T > &C)
Definition: tensor.h:1419
A class for second order tensors.
Definition: tensor.h:303
unsigned int getNVals() const
Definition: tensor.h:1004
unsigned int l
Definition: tensor.h:216
unsigned int n2
Definition: tensor.h:1104
Definition: tensor.h:166
void assertSquare() const
Definition: tensor.cpp:334
void identityTensor2InPlace(unsigned int n, Tensor2< T > &A)
Definition: tensor.h:692
bool areEqual(const Tensor2< T > &A, const Tensor2< T > &B, T tol)
Definition: tensor.h:737
unsigned int n3
Definition: tensor.h:1105
unsigned int n1
Definition: tensor.h:1103
std::vector< T > abs(const std::vector< T > &vec)
Definition: tensor.h:89
idx2d unflat(unsigned int flatIdx, unsigned int n1, unsigned int n2)
Definition: tensor.h:185
std::vector< T > operator-(const std::vector< T > &vec1, const std::vector< T > &vec2)
Definition: tensor.h:110
void outerInPlace(const std::vector< T > &A, const std::vector< T > &B, Tensor2< T > &C)
Definition: tensor.h:653
T contract(const Tensor2< T > &A, const Tensor2< T > &B)
Definition: tensor.h:950
bool operator!=(const EulerAngles< T > &l, const EulerAngles< T > &r)
Definition: rotation.h:113
Tensor2< T > trans() const
The transpose of the tensor.
Definition: tensor.cpp:536
#define HPP_ARRAY_LAYOUT
Definition: config.h:25
std::valarray< T > vals
Definition: tensor.h:1110
T & operator()(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
Definition: tensor.h:1175
T & operator()(const unsigned int i, const unsigned int j)
Get the a reference to the value of .
Definition: tensor.h:618
unsigned int n1
the first dimension of the tensor
Definition: tensor.h:504
T getValFlat(const unsigned int flatIdx) const
Get the value of .
Definition: tensor.h:604
void assertCompatibleForMultiplication(const Tensor2< T > &A, const Tensor2< T > &B)
Definition: tensor.h:905
T flatC(std::vector< T > idx, std::vector< U > dims)
Definition: tensor.h:279
T min(const std::vector< T > &vec)
Definition: tensor.h:98
T max(const std::vector< T > &vec)
Definition: tensor.h:104
unsigned int k
Definition: tensor.h:215
unsigned int i
Definition: tensor.h:181
Tensor4< T > identityTensor4(unsigned int n)
Definition: tensor.h:1199
GSHCoeffs< T > operator+(const GSHCoeffs< T > &coeffs1, const GSHCoeffs< T > &coeffs2)
Definition: gsh.h:210
void identityTensor4InPlace(unsigned int n, Tensor4< T > &A)
Definition: tensor.h:1215
unsigned int n4
Definition: tensor.h:1106
void setVal(const unsigned int i, const unsigned int j, T val)
Definition: tensor.h:631
unsigned int getn1() const
Definition: tensor.h:339
void contractInPlace(const Tensor4< T > &A, const Tensor2< T > &B, Tensor2< T > &C)
Definition: tensor.h:1482
T getVal(unsigned int i, unsigned int j, unsigned int k, unsigned int l) const
Definition: tensor.h:1161
T prod(const std::vector< T > &vec)
Product of all the entries of a vector.
Definition: tensor.h:128
std::valarray< T > vals
the underlying 1D array
Definition: tensor.h:511
idx2d unflat(const unsigned int idx) const
Unflatten the two indices of the tensor.
Definition: tensor.h:563
TensorError(const std::string &val)
Definition: tensor.h:169
unsigned int getn2() const
Definition: tensor.h:340
bool areSameShape(const Tensor2< T > &A, const Tensor2< T > &B)
Definition: tensor.h:773
unsigned int n2
the second dimension of the tensor
Definition: tensor.h:506
Definition: tensorCUDA.h:902