LCOV - code coverage report
Current view: top level - tools - Tensor.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 138 147 93.9 %
Date: 2026-11-05 10:19:04 Functions: 21 24 87.5 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #ifndef __PLUMED_tools_Tensor_h
      23             : #define __PLUMED_tools_Tensor_h
      24             : 
      25             : #include "MatrixSquareBracketsAccess.h"
      26             : #include "Vector.h"
      27             : #include "LoopUnroller.h"
      28             : #include "Exception.h"
      29             : 
      30             : #include <array>
      31             : #include <type_traits>
      32             : 
      33             : namespace PLMD {
      34             : 
      35             : //should I add something for calling ssyevr?
      36             : /// Small class to contain local utilities.
      37             : /// Should not be used outside of the TensorGeneric class.
      38             : struct TensorGenericAux {
      39             : // local redefinition, just to avoid including lapack.h here
      40             :   static void local_xsyevr(const char *jobz, const char *range, const char *uplo, int *n,
      41             :                            double *a, int *lda, double *vl, double *vu, int *
      42             :                            il, int *iu, double *abstol, int *m, double *w,
      43             :                            double *z__, int *ldz, int *isuppz, double *work,
      44             :                            int *lwork, int *iwork, int *liwork, int *info);
      45             :   static void local_xsyevr(const char *jobz, const char *range, const char *uplo, int *n,
      46             :                            float *a, int *lda, float *vl, float *vu, int *
      47             :                            il, int *iu, float *abstol, int *m, float *w,
      48             :                            float *z__, int *ldz, int *isuppz, float *work,
      49             :                            int *lwork, int *iwork, int *liwork, int *info);
      50             : };
      51             : 
      52             : /**
      53             : \ingroup TOOLBOX
      54             : Class implementing fixed size matrices of doubles
      55             : 
      56             : \tparam n The number rows
      57             : \tparam m The number columns
      58             : 
      59             : This class implements a matrix of doubles with size fixed at
      60             : compile time. It is useful for small fixed size objects (e.g.
      61             : 3x3 tensors) as it does not waste space to store the vector size.
      62             : Moreover, as the compiler knows the size, it can be completely
      63             : optimized inline.
      64             : Most of the loops are explicitly unrolled using PLMD::LoopUnroller class
      65             : Matrix elements are initialized to zero by default. Notice that
      66             : this means that constructor is a bit slow. This point might change
      67             : in future if we find performance issues.
      68             : It takes advantage of MatrixSquareBracketsAccess to provide both
      69             : () and [] syntax for access.
      70             : Several functions are declared as friends even if not necessary so as to
      71             : properly appear in Doxygen documentation.
      72             : 
      73             : Aliases are defined to simplify common declarations (Tensor, Tensor2d, Tensor3d, Tensor4d).
      74             : Also notice that some operations are only available for 3x3 tensors.
      75             : 
      76             : Example of usage
      77             : \verbatim
      78             : 
      79             : #include "Tensor.h"
      80             : 
      81             : using namespace PLMD;
      82             : 
      83             : int main(){
      84             :   Tensor a;
      85             :   TensorGeneric<3,2> b;
      86             :   TensorGeneric<3,2> c=matmul(a,b);
      87             :   return 0;
      88             : }
      89             : 
      90             : \endverbatim
      91             : */
      92             : template<typename T, unsigned n, unsigned m> class TensorTyped;
      93             : 
      94             : template<typename T, unsigned n, unsigned m>
      95             : std::ostream & operator<<(std::ostream &os, const TensorTyped<T,n,m>& t);
      96             : 
      97             : /// Compute the Frobenius norm 2.
      98             : template<typename T, unsigned n, unsigned m>
      99             : T frobeniusNorm(const TensorTyped<T,m,n>&t);
     100             : 
     101             : /// Diagonalize tensor.
     102             : /// Syntax is the same as Matrix::diagMat.
     103             : /// In addition, it is possible to call if with m smaller than n. In this case,
     104             : /// only the first (smaller) m eigenvalues and eigenvectors are retrieved.
     105             : /// If case lapack fails (info!=0) it throws an exception.
     106             : /// Notice that tensor is assumed to be symmetric!!!
     107             : 
     108             : template<typename T, unsigned n,unsigned m>
     109             : void diagMatSym(const TensorTyped<T,n,n>&,VectorTyped<T,m>&evals,TensorTyped<T,m,n>&evec);
     110             : /// Compute lowest eigenvalue and eigenvector, using a branchless iterative implementation.
     111             : /// This function is amenable for running on openACC.
     112             : /// The accuracy could be controlled increasing the number of iterations. The default value (24)
     113             : /// seems sufficient for most applications.
     114             : /// In principle, it could be extended to compute m eigenvalues by:
     115             : /// - computing the lowest
     116             : /// - compute the proper projector and reduce the matrix to <n-1,n-1>
     117             : /// - proceed iteratively
     118             : /// The interface should then be likely the same as diagMatSym
     119             : template<typename T, unsigned n>
     120             : T lowestEigenpairSym(const TensorTyped<T,n, n>& K, VectorTyped<T,n>& eigenvector, unsigned niter=24);
     121             : 
     122             : template<typename T, unsigned n, unsigned m>
     123             : class TensorTyped:
     124             :   public MatrixSquareBracketsAccess<TensorTyped<T,n,m>,T> {
     125             :   std::array<T,n*m> d;
     126             : /// Auxiliary private function for constructor
     127             :   constexpr void auxiliaryConstructor();
     128             : /// Auxiliary private function for constructor
     129             :   template<typename... Args>
     130             :   constexpr void auxiliaryConstructor(T first,Args... arg);
     131             :   template <int n_, int m_>
     132             :   using is3x3 = std::enable_if_t<(n_==3 && m_==3)>;
     133             : public:
     134             : //upload the tensor to the current acc device
     135             :   void toACCDevice()const  {
     136             : #pragma acc enter data copyin(this[0:1], d, d[0:n*m-1])
     137             :   }
     138             :   void removeFromACCDevice() const {
     139             :     // just delete
     140             : #pragma acc exit data delete( d[0:n*m-1], d, this[0:1])
     141             :   }
     142             : /// Constructor accepting n*m T parameters.
     143             : /// Can be used as Tensor<2,2>(1.0,2.0,3.0,4.0)
     144             : /// In case a wrong number of parameters is given, a static assertion will fail.
     145             :   template<typename... Args>
     146             :   constexpr TensorTyped(T first,Args... arg);
     147             : /// initialize the tensor to zero
     148             :   constexpr TensorTyped();
     149             : /// initialize a tensor as an external product of two Vector
     150             :   constexpr TensorTyped(const VectorTyped<T,n>&v1,const VectorTyped<T,m>&v2);
     151             : /// set it to zero
     152             :   constexpr void zero();
     153             : /// get the underline pointer to data
     154             :   constexpr T* data();
     155             : /// get the underline pointer to data
     156             :   constexpr const T* data() const;
     157             : /// access element
     158             :   constexpr T & operator() (unsigned i,unsigned j);
     159             : /// access element
     160             :   constexpr const T & operator() (unsigned i,unsigned j)const;
     161             : /// increment
     162             :   constexpr TensorTyped& operator +=(const TensorTyped& b);
     163             : /// decrement
     164             :   constexpr TensorTyped& operator -=(const TensorTyped& b);
     165             : /// multiply
     166             :   constexpr TensorTyped& operator *=(T);
     167             : /// divide
     168             :   constexpr TensorTyped& operator /=(T);
     169             : /// return +t
     170             :   constexpr TensorTyped operator +()const;
     171             : /// return -t
     172             :   constexpr TensorTyped operator -()const;
     173             : /// set j-th column
     174             :   constexpr TensorTyped& setCol(unsigned j,const VectorTyped<T,n> & c);
     175             : /// set i-th row
     176             :   constexpr TensorTyped& setRow(unsigned i,const VectorTyped<T,m> & r);
     177             : /// get j-th column
     178             :   constexpr VectorTyped<T,n> getCol(unsigned j)const;
     179             : /// get i-th row
     180             :   constexpr VectorTyped<T,m> getRow(unsigned i)const;
     181             : /// returns the determinant
     182             :   template<int m_=m, int n_=n,typename = is3x3<m_,n_>>
     183             :   constexpr T determinant()const;
     184             : /// return an identity tensor
     185             :   static constexpr TensorTyped<T,n,n> identity();
     186             : /// return the matrix inverse
     187             :   template<int m_=m, int n_=n,typename = is3x3<m_,n_>>
     188             :   constexpr TensorTyped inverse()const;
     189             : /// return the transpose matrix
     190             :   constexpr TensorTyped<T,m,n> transpose()const;
     191             : /// << operator.
     192             : /// Allows printing tensor `t` with `std::cout<<t;`
     193             :   friend std::ostream & operator<< <>(std::ostream &os, const TensorTyped&);
     194             : };
     195             : 
     196             : template<typename T, unsigned n, unsigned m>
     197             : constexpr void TensorTyped<T,n,m>::auxiliaryConstructor()
     198             : {}
     199             : 
     200             : template<typename T, unsigned n, unsigned m>
     201             : template<typename... Args>
     202             : constexpr void TensorTyped<T,n,m>::auxiliaryConstructor(T first,Args... arg) {
     203    22369772 :   d[n*m-(sizeof...(Args))-1]=first;
     204           0 :   auxiliaryConstructor(arg...);
     205             : }
     206             : 
     207             : template<typename T, unsigned n, unsigned m>
     208             : template<typename... Args>
     209     3156452 : constexpr TensorTyped<T,n,m>::TensorTyped(T first,Args... arg) {
     210             :   static_assert((sizeof...(Args))+1==n*m,"you are trying to initialize a Tensor with the wrong number of arguments");
     211             :   auxiliaryConstructor(first,arg...);
     212             : }
     213             : 
     214             : template<typename T, unsigned n, unsigned m>
     215             : constexpr T* TensorTyped<T,n,m>::data() {
     216             :   return d.data();
     217             : }
     218             : template<typename T, unsigned n, unsigned m>
     219             : constexpr const T* TensorTyped<T,n,m>::data() const {
     220             :   return d.data();
     221             : }
     222             : 
     223             : template<typename T, unsigned n, unsigned m>
     224    11324708 : constexpr TensorTyped<T,n,m>::TensorTyped() {
     225             :   LoopUnroller<n*m>::_zero(d.data());
     226             : }
     227             : 
     228             : /* between RVO and compile time this should be faster, but slows down openACC, a lot
     229             : template <typename T, unsigned i, unsigned j, unsigned m>
     230             : void external_rec(T*const out,const T*const v1, const T*const v2){
     231             :       if constexpr (j>0) {
     232             :         external_rec<i,j-1,m>(out,v1,v2);
     233             :       } else if constexpr (i>0) {
     234             :           external_rec<i-1,m-1,m>(out,v1,v2);
     235             :       }
     236             :       out[i*m+j]=v1[i]*v2[j];
     237             : }
     238             : template<typename T, unsigned n, unsigned m>
     239             : std::array<T,n*m> externaProd(const VectorGeneric<T,n>&v1,const VectorGeneric<T,m>&v2){
     240             : std::array<T,n*m> toRet;
     241             : external_rec<n-1,m-1,m>(toRet.data(),v1.data(),v2.data());
     242             : return toRet;
     243             : }
     244             : template<typename T, unsigned n, unsigned m>
     245             : TensorGeneric<T,n,m>::TensorGeneric(const VectorGeneric<T,n>&v1,const VectorGeneric<T,m>&v2)
     246             : :d(externaProd(v1,v2)) {}
     247             : */
     248             : 
     249             : template<typename T, unsigned n, unsigned m>
     250   111639276 : constexpr TensorTyped<T,n,m>::TensorTyped(const VectorTyped<T,n>&v1,const VectorTyped<T,m>&v2) {
     251   446557104 :   for(unsigned i=0; i<n; i++) {
     252  1339671312 :     for(unsigned j=0; j<m; j++) {
     253  1004753484 :       d[i*m+j]=v1[i]*v2[j];
     254             :     }
     255             :   }
     256   111639276 : }
     257             : 
     258             : template<typename T, unsigned n, unsigned m>
     259             : constexpr void TensorTyped<T,n,m>::zero() {
     260             :   LoopUnroller<n*m>::_zero(d.data());
     261         100 : }
     262             : 
     263             : template<typename T, unsigned n, unsigned m>
     264             : constexpr T & TensorTyped<T,n,m>::operator() (unsigned i,unsigned j) {
     265             : #ifdef _GLIBCXX_DEBUG
     266             : // index i is implicitly checked by the std::array class
     267             :   plumed_assert(j<m);
     268             : #endif
     269    47479734 :   return d[m*i+j];
     270             : }
     271             : 
     272             : template<typename T, unsigned n, unsigned m>
     273             : constexpr const T & TensorTyped<T,n,m>::operator() (unsigned i,unsigned j)const {
     274             : #ifdef _GLIBCXX_DEBUG
     275             : // index i is implicitly checked by the std::array class
     276             :   plumed_assert(j<m);
     277             : #endif
     278  3772500020 :   return d[m*i+j];
     279             : }
     280             : 
     281             : template<typename T, unsigned n, unsigned m>
     282             : constexpr TensorTyped<T,n,m>& TensorTyped<T,n,m>::operator +=(const TensorTyped<T,n,m>& b) {
     283             :   LoopUnroller<n*m>::_add(d.data(),b.d.data());
     284             :   return *this;
     285             : }
     286             : 
     287             : template<typename T, unsigned n, unsigned m>
     288             : constexpr TensorTyped<T,n,m>& TensorTyped<T,n,m>::operator -=(const TensorTyped<T,n,m>& b) {
     289             :   LoopUnroller<n*m>::_sub(d.data(),b.d.data());
     290             :   return *this;
     291             : }
     292             : 
     293             : template<typename T, unsigned n, unsigned m>
     294             : constexpr TensorTyped<T,n,m>& TensorTyped<T,n,m>::operator *=(T s) {
     295    77829008 :   LoopUnroller<n*m>::_mul(d.data(),s);
     296             :   return *this;
     297             : }
     298             : 
     299             : template<typename T, unsigned n, unsigned m>
     300             : constexpr TensorTyped<T,n,m>& TensorTyped<T,n,m>::operator /=(T s) {
     301    18636720 :   LoopUnroller<n*m>::_mul(d.data(),1.0/s);
     302             :   return *this;
     303             : }
     304             : 
     305             : template<typename T, unsigned n, unsigned m>
     306             : constexpr TensorTyped<T,n,m> TensorTyped<T,n,m>::operator+()const {
     307      449931 :   return *this;
     308             : }
     309             : 
     310             : template<typename T, unsigned n, unsigned m>
     311             : constexpr TensorTyped<T,n,m> TensorTyped<T,n,m>::operator-()const {
     312     1945850 :   TensorTyped<T,n,m> r;
     313     1619251 :   LoopUnroller<n*m>::_neg(r.d.data(),d.data());
     314             :   return r;
     315             : }
     316             : 
     317             : template<typename T, unsigned n, unsigned m>
     318             : constexpr TensorTyped<T,n,m>& TensorTyped<T,n,m>::setCol(unsigned j,const VectorTyped<T,n> & c) {
     319      485892 :   for(unsigned i=0; i<n; ++i) {
     320      364419 :     (*this)(i,j)=c(i);
     321             :   }
     322             :   return *this;
     323             : }
     324             : 
     325             : template<typename T, unsigned n, unsigned m>
     326             : constexpr TensorTyped<T,n,m>& TensorTyped<T,n,m>::setRow(unsigned i,const VectorTyped<T,m> & r) {
     327    12541938 :   for(unsigned j=0; j<m; ++j) {
     328     9440397 :     (*this)(i,j)=r(j);
     329             :   }
     330             :   return *this;
     331             : }
     332             : 
     333             : template<typename T, unsigned n, unsigned m>
     334             : constexpr VectorTyped<T,n> TensorTyped<T,n,m>::getCol(unsigned j)const {
     335    10458675 :   VectorTyped<T,n> v;
     336    51303258 :   for(unsigned i=0; i<n; ++i) {
     337    43200045 :     v(i)=(*this)(i,j);
     338             :   }
     339             :   return v;
     340             : }
     341             : 
     342             : template<typename T, unsigned n, unsigned m>
     343             : constexpr VectorTyped<T,m> TensorTyped<T,n,m>::getRow(unsigned i)const {
     344     9840503 :   VectorTyped<T,m> v;
     345    44363848 :   for(unsigned j=0; j<m; ++j) {
     346    35733749 :     v(j)=(*this)(i,j);
     347             :   }
     348             :   return v;
     349             : }
     350             : 
     351             : template<typename T, unsigned n, unsigned m>
     352             : constexpr TensorTyped<T,n,m> operator+(const TensorTyped<T,n,m>&t1,const TensorTyped<T,n,m>&t2) {
     353     4162891 :   TensorTyped<T,n,m> t(t1);
     354             :   t+=t2;
     355             :   return t;
     356             : }
     357             : 
     358             : template<typename T, unsigned n, unsigned m>
     359             : constexpr TensorTyped<T,n,m> operator-(const TensorTyped<T,n,m>&t1,const TensorTyped<T,n,m>&t2) {
     360    21074803 :   TensorTyped<T,n,m> t(t1);
     361             :   t-=t2;
     362             :   return t;
     363             : }
     364             : 
     365             : template<typename T, typename J, unsigned n, unsigned m>
     366             : constexpr TensorTyped<T,n,m> operator*(const TensorTyped<T,n,m>&t1,J s) {
     367    73951680 :   TensorTyped<T,n,m> t(t1);
     368             :   t*=s;
     369             :   return t;
     370             : }
     371             : 
     372             : template<typename T, typename J, unsigned n, unsigned m>
     373             : constexpr TensorTyped<T,n,m> operator*(J s,const TensorTyped<T,n,m>&t1) {
     374             :   return t1*s;
     375             : }
     376             : 
     377             : template<typename T, typename J, unsigned n, unsigned m>
     378             : constexpr TensorTyped<T,n,m> operator/(const TensorTyped<T,n,m>&t1,J s) {
     379       21771 :   return t1*(T(1.0)/s);
     380             : }
     381             : 
     382             : template<typename T, unsigned n, unsigned m>
     383             : template<int m_, int n_,typename>
     384      568457 : constexpr inline T TensorTyped<T,n,m>::determinant()const {
     385             :   return
     386      568457 :     d[0]*d[4]*d[8]
     387      568457 :     + d[1]*d[5]*d[6]
     388      568457 :     + d[2]*d[3]*d[7]
     389      568457 :     - d[0]*d[5]*d[7]
     390      568457 :     - d[1]*d[3]*d[8]
     391      568457 :     - d[2]*d[4]*d[6];
     392             : }
     393             : 
     394             : //consider to make this a constexpr function
     395             : template<typename T, unsigned n, unsigned m>
     396             : constexpr inline TensorTyped<T,n,n> TensorTyped<T,n,m>::identity() {
     397             :   TensorTyped<T,n,n> t;
     398             :   for(unsigned i=0; i<n; i++) {
     399             :     t(i,i)=1.0;
     400             :   }
     401             :   return t;
     402             : }
     403             : 
     404             : template<typename T, unsigned n, unsigned m>
     405     6975960 : constexpr TensorTyped<T,m,n> TensorTyped<T,n,m>::transpose()const {
     406     6975960 :   TensorTyped<T,m,n> t;
     407    27903840 :   for(unsigned i=0; i<m; i++)
     408    83711520 :     for(unsigned j=0; j<n; j++) {
     409    62783640 :       t(i,j)=(*this)(j,i);
     410             :     }
     411     6975960 :   return t;
     412             : }
     413             : 
     414             : template<typename T, unsigned n, unsigned m>
     415             : template<int m_, int n_,typename>
     416      364767 : constexpr inline TensorTyped<T,n,m> TensorTyped<T,n,m>::inverse()const {
     417      364767 :   TensorTyped t;
     418      364767 :   T invdet=1.0/determinant();
     419     1459068 :   for(unsigned i=0; i<3; i++)
     420     4377204 :     for(unsigned j=0; j<3; j++)
     421     3282903 :       t(j,i)=invdet*(   (*this)((i+1)%3,(j+1)%3)*(*this)((i+2)%3,(j+2)%3)
     422     3282903 :                         -(*this)((i+1)%3,(j+2)%3)*(*this)((i+2)%3,(j+1)%3));
     423      364767 :   return t;
     424             : }
     425             : 
     426             : /// matrix-matrix multiplication
     427             : template<typename T, unsigned n, unsigned m, unsigned l>
     428    23572032 : constexpr TensorTyped<T,n,l> matmul(const TensorTyped<T,n,m>&a,const TensorTyped<T,m,l>&b) {
     429    23572032 :   TensorTyped<T,n,l> t;
     430   112924848 :   for(unsigned i=0; i<n; i++)
     431   431958144 :     for(unsigned j=0; j<l; j++)
     432  1668608832 :       for(unsigned k=0; k<m; k++) {
     433  1326003504 :         t(i,j)+=a(i,k)*b(k,j);
     434             :       }
     435    23572032 :   return t;
     436             : }
     437             : 
     438             : /// matrix-vector multiplicationi, the first argument determines the type of the result
     439             : template<typename T,typename TT, unsigned n, unsigned m>
     440    80117374 : constexpr VectorTyped<T,n> matmul(const TensorTyped<T,n,m>&a,const VectorTyped<TT,m>&b) {
     441    80117374 :   VectorTyped<T,n> t;
     442   320469496 :   for(unsigned i=0; i<n; i++)
     443   961408488 :     for(unsigned j=0; j<m; j++) {
     444   721056366 :       t(i)+=a(i,j)*b(j);
     445             :     }
     446    80117374 :   return t;
     447             : }
     448             : 
     449             : /// vector-matrix multiplication, the first argument determines the type of the result
     450             : template<typename T,typename TT, unsigned n, unsigned m>
     451   126614546 : constexpr VectorTyped<T,n> matmul(const VectorTyped<T,m>&a,const TensorTyped<TT,m,n>&b) {
     452   126614546 :   VectorTyped<T,n> t;
     453   507234714 :   for(unsigned i=0; i<n; i++)
     454  1526741690 :     for(unsigned j=0; j<m; j++) {
     455  1146121522 :       t(i)+=a(j)*b(j,i);
     456             :     }
     457   126614546 :   return t;
     458             : }
     459             : 
     460             : /// vector-vector multiplication (maps to dotProduct)
     461             : template<typename T, unsigned n_>
     462             : constexpr T matmul(const VectorTyped<T,n_>&a,const VectorTyped<T,n_>&b) {
     463             :   return dotProduct(a,b);
     464             : }
     465             : 
     466             : /// matrix-matrix-matrix multiplication
     467             : template<typename T, unsigned n, unsigned m, unsigned l, unsigned i>
     468             : constexpr TensorTyped<T,n,i> matmul(const TensorTyped<T,n,m>&a,const TensorTyped<T,m,l>&b,const TensorTyped<T,l,i>&c) {
     469             :   return matmul(matmul(a,b),c);
     470             : }
     471             : 
     472             : /// matrix-matrix-vector multiplication
     473             : template<typename T, unsigned n, unsigned m, unsigned l>
     474      407268 : constexpr VectorTyped<T,n> matmul(const TensorTyped<T,n,m>&a,const TensorTyped<T,m,l>&b,const VectorTyped<T,l>&c) {
     475      407268 :   return matmul(matmul(a,b),c);
     476             : }
     477             : 
     478             : /// vector-matrix-matrix multiplication
     479             : template<typename T, unsigned n, unsigned m, unsigned l>
     480             : constexpr VectorTyped<T,l> matmul(const VectorTyped<T,n>&a,const TensorTyped<T,n,m>&b,const TensorTyped<T,m,l>&c) {
     481             :   return matmul(matmul(a,b),c);
     482             : }
     483             : 
     484             : /// vector-matrix-vector multiplication
     485             : template<typename T, unsigned n, unsigned m>
     486      776530 : constexpr T matmul(const VectorTyped<T,n>&a,const TensorTyped<T,n,m>&b,const VectorTyped<T,m>&c) {
     487      776530 :   return matmul(matmul(a,b),c);
     488             : }
     489             : 
     490             : template <typename T>
     491             : inline
     492             : constexpr T determinant(const TensorTyped<T,3,3>&t) {
     493         817 :   return t.determinant();
     494             : }
     495             : 
     496             : template <typename T>
     497             : inline
     498             : constexpr TensorTyped<T,3,3> inverse(const TensorTyped<T,3,3>&t) {
     499      182596 :   return t.inverse();
     500             : }
     501             : 
     502             : /// returns the transpose of a tensor (same as TensorGeneric::transpose())
     503             : template<typename T, unsigned n, unsigned m>
     504             : constexpr TensorTyped<T,n,m> transpose(const TensorTyped<T,m,n>&t) {
     505      944580 :   return t.transpose();
     506             : }
     507             : 
     508             : /// returns the transpose of a tensor (same as TensorGeneric(const VectorGeneric&,const VectorGeneric&))
     509             : template<typename T, unsigned n, unsigned m>
     510             : constexpr TensorTyped<T,n,m> extProduct(const VectorTyped<T,n>&v1,const VectorTyped<T,m>&v2) {
     511     3566376 :   return TensorTyped<T,n,m>(v1,v2);
     512             : }
     513             : 
     514             : template <typename T>
     515             : inline
     516             : constexpr TensorTyped<T,3,3> dcrossDv1(const VectorTyped<T,3>&v1,const VectorTyped<T,3>&v2) {
     517             :   (void) v1; // this is to avoid warnings. still the syntax of this function is a bit dummy...
     518             :   return TensorTyped<T,3,3>(
     519             :            T(0.0),  v2[2], -v2[1],
     520             :            -v2[2], T(0.0), v2[0],
     521     1112903 :            v2[1],  -v2[0], T(0.0));
     522             : }
     523             : 
     524             : template <typename T>
     525             : inline
     526             : constexpr TensorTyped<T,3,3> dcrossDv2(const VectorTyped<T,3>&v1,const VectorTyped<T,3>&v2) {
     527             :   (void) v2; // this is to avoid warnings. still the syntax of this function is a bit dummy...
     528             :   return TensorTyped<T,3,3>(
     529             :            T(0.0),-v1[2], v1[1],
     530             :            v1[2], T(0.0),-v1[0],
     531     1691135 :            -v1[1],v1[0], T(0.0));
     532             : }
     533             : 
     534             : template<typename T, unsigned n,unsigned m>
     535    18636720 : T frobeniusNorm(const TensorTyped<T,m,n>&t) {
     536    18636720 :   return std::sqrt(LoopUnroller<m*n>::_dot(t.data(),t.data()));
     537             : }
     538             : 
     539             : template<typename T, unsigned n, unsigned m>
     540           0 : std::ostream & operator<<(std::ostream &os, const TensorTyped<T,n,m>& t) {
     541           0 :   for(unsigned i=0; i<n; i++)
     542           0 :     for(unsigned j=0; j<m; j++) {
     543           0 :       if(i!=(n-1) || j!=(m-1)) {
     544           0 :         os<<t(i,j)<<" ";
     545             :       } else {
     546           0 :         os<<t(i,j);
     547             :       }
     548             :     }
     549           0 :   return os;
     550             : }
     551             : 
     552             : template <unsigned n,unsigned m>
     553             : using TensorGeneric=TensorTyped<double,n,m>;
     554             : 
     555             : /// \ingroup TOOLBOX
     556             : typedef TensorGeneric<1,1> Tensor1d;
     557             : /// \ingroup TOOLBOX
     558             : typedef TensorGeneric<2,2> Tensor2d;
     559             : /// \ingroup TOOLBOX
     560             : typedef TensorGeneric<3,3> Tensor3d;
     561             : /// \ingroup TOOLBOX
     562             : typedef TensorGeneric<4,4> Tensor4d;
     563             : /// \ingroup TOOLBOX
     564             : typedef TensorGeneric<5,5> Tensor5d;
     565             : /// \ingroup TOOLBOX
     566             : typedef Tensor3d Tensor;
     567             : 
     568             : /// \ingroup TOOLBOX
     569             : typedef TensorTyped<float,1,1> Tensor1f;
     570             : /// \ingroup TOOLBOX
     571             : typedef TensorTyped<float,2,2> Tensor2f;
     572             : /// \ingroup TOOLBOX
     573             : typedef TensorTyped<float,3,3> Tensor3f;
     574             : /// \ingroup TOOLBOX
     575             : typedef TensorTyped<float,4,4> Tensor4f;
     576             : /// \ingroup TOOLBOX
     577             : typedef TensorTyped<float,5,5> Tensor5f;
     578             : 
     579             : /// \ingroup TOOLBOX
     580             : /// Alias for three by three Tensor of any type:
     581             : template <typename T>
     582             : using TensorT=TensorTyped<T,3,3>;
     583             : 
     584             : template <typename T>
     585             : inline
     586      144558 : TensorTyped<T,3,3> VcrossTensor(const VectorTyped<T,3>&v1,const TensorTyped<T,3,3>&v2) {
     587      144558 :   TensorTyped<T,3,3> t;
     588      578232 :   for(unsigned i=0; i<3; i++) {
     589      867348 :     t.setRow(i,matmul(dcrossDv2(v1,v1),v2.getRow(i)));
     590             :   }
     591      144558 :   return t;
     592             : }
     593             : 
     594             : template <typename T>
     595             : inline
     596       48186 : TensorTyped<T,3,3> VcrossTensor(const TensorTyped<T,3,3>&v2,const VectorTyped<T,3>&v1) {
     597       48186 :   TensorTyped<T,3,3> t;
     598      192744 :   for(unsigned i=0; i<3; i++) {
     599      289116 :     t.setRow(i,-matmul(dcrossDv2(v1,v1),v2.getRow(i)));
     600             :   }
     601       48186 :   return t;
     602             : }
     603             : 
     604             : template <typename T>
     605             : inline
     606       48186 : TensorTyped<T,3,3> deriNorm(const VectorTyped<T,3>&v1,const TensorTyped<T,3,3>&v2) {
     607             :   // delta(v) = delta(v1/v1.norm) = 1/v1.norm*(delta(v1) - (v.delta(v1))cross v;
     608       48186 :   T over_norm = 1./v1.modulo();
     609       48186 :   return over_norm*(v2 - over_norm*over_norm*(extProduct(matmul(v2,v1),v1)));
     610             : }
     611             : 
     612             : /// Diagonalize tensor.
     613             : /// Syntax is the same as Matrix::diagMat.
     614             : /// In addition, it is possible to call if with m_ smaller than n_. In this case,
     615             : /// only the first (smaller) m_ eigenvalues and eigenvectors are retrieved.
     616             : /// If case lapack fails (info!=0) it throws an exception.
     617             : /// Notice that tensor is assumed to be symmetric!!!
     618             : template<typename precision, unsigned n, unsigned m>
     619      150843 : void diagMatSym(const TensorTyped<precision,n,n>&mat,VectorTyped<precision,m>&evals,TensorTyped<precision,m,n>&evec) {
     620             :   // some guess number to make sure work is large enough.
     621             :   // for correctness it should be >=20. However, it is recommended to be the block size.
     622             :   // I put some likely exaggerated number
     623             :   constexpr int bs=100;
     624             :   // temporary data, on stack so as to avoid allocations
     625             :   std::array<int,10*n> iwork;
     626             :   std::array<precision,(6+bs)*n> work;
     627             :   std::array<int,2*m> isup;
     628             :   // documentation says that "matrix is destroyed" !!!
     629      150843 :   auto mat_copy=mat;
     630             :   // documentation says this is size n (even if m<n)
     631             :   std::array<precision,n> evals_tmp;
     632      150843 :   int nn=n;              // dimension of matrix
     633      150843 :   precision vl=0.0, vu=1.0; // ranges - not used
     634      150843 :   int one=1,mm=m;        // minimum and maximum index
     635      150843 :   precision abstol=0.0;     // tolerance
     636      150843 :   int mout=0;            // number of eigenvalues found (same as mm)
     637      150843 :   int info=0;            // result
     638      150843 :   int liwork=iwork.size();
     639      150843 :   int lwork=work.size();
     640      150843 :   TensorGenericAux::local_xsyevr("V", (n==m?"A":"I"), "U", &nn, mat_copy.data(), &nn, &vl, &vu, &one, &mm,
     641             :                                  &abstol, &mout, evals_tmp.data(), evec.data(), &nn,
     642             :                                  isup.data(), work.data(), &lwork, iwork.data(), &liwork, &info);
     643      150843 :   if(info!=0)
     644           0 :     plumed_error()<<"Error diagonalizing matrix\n"
     645             :                   <<"Matrix:\n"<<mat<<"\n"
     646             :                   <<"Info: "<<info<<"\n";
     647      150843 :   plumed_assert(mout==m);
     648      753349 :   for(unsigned i=0; i<m; i++) {
     649      602506 :     evals[i]=evals_tmp[i];
     650             :   }
     651             :   // This changes eigenvectors so that the first non-null element
     652             :   // of each of them is positive
     653             :   // We can do it because the phase is arbitrary, and helps making
     654             :   // the result reproducible
     655      753349 :   for(unsigned i=0; i<m; ++i) {
     656             :     unsigned j=0;
     657      605841 :     for(j=0; j<n; j++)
     658      605841 :       if(evec(i,j)*evec(i,j)>1e-14) {
     659             :         break;
     660             :       }
     661      602506 :     if(j<n)
     662      602506 :       if(evec(i,j)<0.0)
     663      738762 :         for(j=0; j<n; j++) {
     664      590816 :           evec(i,j)*=-1;
     665             :         }
     666             :   }
     667      150843 : }
     668             : 
     669             : template<typename T, unsigned n>
     670      776530 : T lowestEigenpairSym(const TensorTyped<T,n,n>& K, VectorTyped<T,n>& eigenvector, const unsigned niter) {
     671             :   // Estimate upper bound for largest eigenvalue using Gershgorin disks
     672             :   T upper_bound = std::numeric_limits<T>::lowest();
     673     3882650 :   for (unsigned i = 0; i < n; ++i) {
     674             :     auto row = K.getRow(i);
     675     3106120 :     T center = row[i];
     676     3106120 :     row[i] = 0.0;  // zero out the diagonal entry
     677             : 
     678             :     // Compute sum of absolute values of the off-diagonal elements
     679             :     T row_sum = 0.0;
     680    15530600 :     for (unsigned j = 0; j < n; ++j) {
     681    12424480 :       row_sum += std::fabs(row[j]);
     682             :     }
     683             : 
     684     3106120 :     upper_bound = std::fmax(upper_bound, center + row_sum);
     685             :   }
     686             : 
     687             :   // Build shifted matrix A = -K + lambda_shift * I
     688             :   TensorTyped<T,n,n> A = -K;
     689     3882650 :   for (unsigned i = 0; i < n; ++i) {
     690     3106120 :     A[i][i] += upper_bound;
     691             :   }
     692             : 
     693             :   // Repeated squaring + normalization to project onto dominant eigenspace
     694    19413250 :   for (unsigned k = 0; k < niter; ++k) {
     695    18636720 :     A = matmul(A, A);
     696    18636720 :     auto frob = frobeniusNorm(A);
     697    18636720 :     A /= (frob > 0.0 ? frob : 1.0);
     698             :   }
     699             : 
     700             :   // Extract dominant eigenvector from projector A
     701      776530 :   VectorTyped<T,n> v;
     702             :   T best_norm2 = 0.0;
     703     3882650 :   for (unsigned j = 0; j < n; ++j) {
     704             :     auto row = A.getRow(j);
     705             :     T norm2 = modulo2(row);
     706     3106120 :     T use = static_cast<T>(norm2 > best_norm2);
     707     3106120 :     best_norm2 = use * norm2 + (1.0 - use) * best_norm2;
     708     3106120 :     v = use * row + (1.0 - use) * v;
     709             :   }
     710             : 
     711             :   v /= modulo(v);
     712             : 
     713      776530 :   eigenvector = v;
     714             : 
     715      776530 :   return  matmul(eigenvector, K, eigenvector);
     716             : }
     717             : 
     718             : 
     719             : static_assert(sizeof(Tensor)==9*sizeof(double), "code cannot work if this is not satisfied");
     720             : static_assert(sizeof(TensorT<float>)==9*sizeof(float), "code cannot work if this is not satisfied");
     721             : 
     722             : } //PLMD
     723             : 
     724             : #endif

Generated by: LCOV version 1.16