/* LA: linear algebra C++ interface library Copyright (C) 2008 Jiri Pittner or This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _LA_NONCLASS_H_ #define _LA_NONCLASS_H_ #include "vec.h" #include "smat.h" #include "mat.h" #include "la_traits.h" namespace LA { //MISC //positive power for a general class template T positive_power(const T &x, const int n) { if(n<=0) laerror("zero or negative n in positive_power"); int i=n; if(i==1) return x; T y,z; z=x; while(!(i&1)) { z = z*z; i >>= 1; } y=z; while((i >>= 1)/*!=0*/) { z = z*z; if(i&1) y = y*z; } return y; } //general integer power for a class providing identity() and inverse() template T power(const T &x, const int n) { int i=n; if(i==0) {T r(x); r.identity(); return r;} T z; if(i>0) z=x; else {z=x.inverse(); i= -i;} if(i==1) return z; return positive_power(z,i); } template const NRSMat twoside_transform(const NRSMat &S, const NRMat &C, bool transp=0) //calculate C^dagger S C { if(transp) { NRMat tmp = C * S; NRMat result(C.nrows(),C.nrows()); result.gemm((T)0,tmp,'n',C,'t',(T)1); return NRSMat(result); } NRMat tmp = S * C; NRMat result(C.ncols(),C.ncols()); result.gemm((T)0,C,'t',tmp,'n',(T)1); return NRSMat(result); } template const NRMat diagonalmatrix(const NRVec &x) { int n=x.size(); NRMat result((T)0,n,n); T *p = result[0]; for(int j=0; j inline const NRMat commutator ( const NRMat &x, const NRMat &y, const bool trx=0, const bool tryy=0) { NRMat r(trx?x.ncols():x.nrows(), tryy?y.nrows():y.ncols()); r.gemm((T)0,x,trx?'t':'n',y,tryy?'t':'n',(T)1); r.gemm((T)1,y,tryy?'t':'n',x,trx?'t':'n',(T)-1); return r; } //more efficient commutator for a special case of full matrices template inline const NRMat anticommutator ( const NRMat &x, const NRMat &y, const bool trx=0, const bool tryy=0) { NRMat r(trx?x.ncols():x.nrows(), tryy?y.nrows():y.ncols()); r.gemm((T)0,x,trx?'t':'n',y,tryy?'t':'n',(T)1); r.gemm((T)1,y,tryy?'t':'n',x,trx?'t':'n',(T)1); return r; } ////////////////////// // LAPACK interface // ////////////////////// #define declare_la(T) \ extern const NRVec diagofproduct(const NRMat &a, const NRMat &b,\ bool trb=0, bool conjb=0); \ extern T trace2(const NRMat &a, const NRMat &b, bool trb=0); \ extern T trace2(const NRSMat &a, const NRSMat &b, const bool diagscaled=0);\ extern T trace2(const NRSMat &a, const NRMat &b, const bool diagscaled=0);\ extern T trace2(const NRMat &a, const NRSMat &b, const bool diagscaled=0);\ extern void linear_solve(NRMat &a, NRMat *b, T *det=0,int n=0); /*solve Ax^T=b^T (b is nrhs x n) */ \ extern void linear_solve(NRSMat &a, NRMat *b, T *det=0, int n=0); /*solve Ax^T=b^T (b is nrhs x n) */\ extern void linear_solve(NRMat &a, NRVec &b, double *det=0, int n=0); \ extern void linear_solve(NRSMat &a, NRVec &b, double *det=0, int n=0); \ extern void diagonalize(NRMat &a, NRVec::normtype> &w, const bool eivec=1, const bool corder=1, int n=0, NRMat *b=NULL, const int itype=1); \ extern void diagonalize(NRSMat &a, NRVec::normtype> &w, NRMat *v, const bool corder=1, int n=0, NRSMat *b=NULL, const int itype=1);\ extern void singular_decomposition(NRMat &a, NRMat *u, NRVec::normtype> &s, NRMat *v, const bool vnotdagger=0, int m=0, int n=0); /*NOTE!!! all versions of diagonalize DESTROY A and generalized diagonalize also B matrix */ declare_la(double) declare_la(std::complex) // Separate declarations //general nonsymmetric matrix and generalized diagonalization //corder =0 ... C rows are eigenvectors, =1 ... C columns are eigenvectors extern void gdiagonalize(NRMat &a, NRVec &wr, NRVec &wi, NRMat *vl=NULL, NRMat *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat *b=NULL, NRVec *beta=NULL); //this used real storage of eigenvectors like dgeev template extern void gdiagonalize(NRMat &a, NRVec< std::complex > &w, NRMat< std::complex >*vl=NULL, NRMat< std::complex > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat *b=NULL, NRVec *beta=NULL); //eigenvectors are stored in complex matrices for T both double and complex //for compatibility in davidson extern void gdiagonalize(NRMat > &a, NRVec &wr, NRVec &wi, NRMat > *vl=NULL, NRMat > *vr=NULL, const bool corder=1, int n=0, const int sorttype=0, const int biorthonormalize=0, NRMat > *b=NULL, NRVec > *beta=NULL); //complex,real,imaginary parts of various entities template extern const typename LA_traits::realtype realpart(const T&); template extern const typename LA_traits::realtype imagpart(const T&); template extern const typename LA_traits::complextype realmatrix (const T&); template extern const typename LA_traits::complextype imagmatrix (const T&); template extern const typename LA_traits::complextype complexmatrix (const T&, const T&); //Cholesky decomposition extern void cholesky(NRMat &a, bool upper=1); extern void cholesky(NRMat > &a, bool upper=1); //inverse by means of linear solve, preserving rhs intact template const NRMat calcinverse(NRMat a, T *det=NULL) { #ifdef DEBUG if(a.nrows()!=a.ncols()) laerror("inverse() for non-square matrix"); #endif NRMat result(a.nrows(),a.nrows()); result = (T)1.; a.copyonwrite(); linear_solve(a, &result, det); result.transposeme(); //tested with noncblas return result; } //several matrix norms template typename LA_traits::normtype MatrixNorm(const MAT &A, const char norm); //condition number template typename LA_traits::normtype CondNumber(const MAT &A, const char norm); //general determinant template const typename LA_traits::elementtype determinant(MAT a)//passed by value { typename LA_traits::elementtype det; if(a.nrows()!=a.ncols()) laerror("determinant of non-square matrix"); linear_solve(a,NULL,&det); return det; } //general determinant destructive on input template const typename LA_traits::elementtype determinant_destroy(MAT &a) //passed by reference { typename LA_traits::elementtype det; if(a.nrows()!=a.ncols()) laerror("determinant of non-square matrix"); linear_solve(a,NULL,&det); return det; } //------------------------------------------------------------------------------ // solves set of linear equations using gesvx // input: // A double precision matrix of dimension nn x mm, where min(nn, mm) >= n // B double prec. array dimensioned as nrhs x n // rhsCount nrhs - count of right hand sides // eqCount n - count of equations // eq use equilibration of matrix A before solving // saveA if set, do no overwrite A if equilibration in effect // rcond if not NULL, store the returned rcond value from dgesvx // output: // solution is stored in B // the info parameter of gesvx is returned (see man dgesvx) //------------------------------------------------------------------------------ template int linear_solve_x(NRMat &A, T *B, const int rhsCount, const int eqCount, const bool eq, const bool saveA, double *rcond); //------------------------------------------------------------------------------ // for given square matrices A, B computes X = AB^{-1} as follows // XB = A => B^TX^T = A^T // input: // _A double precision matrix of dimension nn x nn // _B double prec. matrix of dimension nn x nn // _useEq use equilibration suitable for badly conditioned matrices // _rcond if not NULL, store the returned value of rcond fromd dgesvx // output: // solution is stored in _B // the info parameter of dgesvx is returned (see man dgesvx) //------------------------------------------------------------------------------ template int multiply_by_inverse(NRMat &A, NRMat &B, bool useEq=false, double *rcond=NULL); //general submatrix, INDEX will typically be NRVec or even int* //NOTE: in order to check consistency between nrows and rows in rows is a NRVec //some advanced metaprogramming would be necessary //application: e.g. ignoresign=true, equalsigns=true, indexshift= -1 ... elements of Slater overlaps for RHF template const NRMat::elementtype> submatrix(const MAT a, const int nrows, const INDEX rows, const int ncols, const INDEX cols, int indexshift=0, bool ignoresign=false, bool equalsigns=false) { NRMat::elementtype> r(nrows,ncols); if(equalsigns) //make the element zero if signs of both indices are opposite { if(ignoresign) { for(int i=0; i &v); //declaration of template interface to cblas routines with full options available //just to facilitate easy change between float, double, complex in a user code //very incomplete, add new ones as needed template inline void xcopy(int n, const T *x, int incx, T *y, int incy); template inline void xaxpy(int n, const T &a, const T *x, int incx, T *y, int incy); template inline T xdot(int n, const T *x, int incx, const T *y, int incy); //specialized definitions have to be in the header file to be inlineable, eliminating any runtime overhead template<> inline void xcopy (int n, const double *x, int incx, double *y, int incy) { cblas_dcopy(n, x, incx, y, incy); } template<> inline void xaxpy(int n, const double &a, const double *x, int incx, double *y, int incy) { cblas_daxpy(n, a, x, incx, y, incy); } template<> inline double xdot(int n, const double *x, int incx, const double *y, int incy) { return cblas_ddot(n,x,incx,y,incy); } //debugging aid: reconstruct an explicit matrix from the implicit version //which provides gemv only template NRMat reconstructmatrix(const M &implicitmat) { NRMat r(implicitmat.nrows(),implicitmat.ncols()); NRVec rhs(0.,implicitmat.ncols()); NRVec tmp(implicitmat.nrows()); for(int i=0; i realmatrixfunction(NRMat a, double (*f) (double)); //a has to by in fact symmetric extern NRMat > complexmatrixfunction(NRMat a, double (*fre) (double), double (*fim) (double)); //a has to by in fact symmetric template NRMat matrixfunction(NRSMat a, double (*f) (double)) //of symmetric/hermitian matrix { int n = a.nrows(); NRVec w(n); NRMat v(n, n); diagonalize(a, w, &v, 0); for (int i=0; i u = v; NRVec ww=w; //diagmultl needs same type v.diagmultl(ww); NRMat r(n, n); r.gemm(0.0, u, 't', v, 'n', 1.0); //gemm will use 'c' for complex ones return r; } template extern NRMat matrixfunction(NRMat a, std::complex (*f)(const std::complex &)) //of a general real/complex matrix { int n = a.nrows(); NRVec > w(n); NRMat > u(n,n),v(n,n); #ifdef debugmf NRMat > a0=a; #endif gdiagonalize(a, w, &u, &v, false,n,0,false,NULL,NULL);//a gets destroyed, eigenvectors are rows NRVec< std::complex > z = diagofproduct(u, v, 1, 1); #ifdef debugmf std::cout <<"TEST matrixfunction\n"< > wz(n); for (int i=0; i > r(n, n); r.gemm(0.0, v, 'c', u, 'n', 1.0); return (NRMat) r; //convert back to real if applicable by the explicit decomplexifying constructor; it is NOT checked to which accuracy the imaginary part is actually zero } extern std::complex sqrtinv(const std::complex &); extern double sqrtinv(const double); //functions on matrices inline NRMat sqrt(const NRSMat &a) { return matrixfunction(a,&std::sqrt); } inline NRMat sqrtinv(const NRSMat &a) { return matrixfunction(a,&sqrtinv); } inline NRMat realsqrt(const NRMat &a) { return realmatrixfunction(a,&std::sqrt); } inline NRMat realsqrtinv(const NRMat &a) { return realmatrixfunction(a,&sqrtinv); } inline NRMat log(const NRSMat &a) { return matrixfunction(a,&std::log); } extern NRMat log(const NRMat &a); extern NRMat > log(const NRMat > &a); extern NRMat > exp0(const NRMat > &a); extern NRMat > copytest(const NRMat > &a); extern NRMat copytest(const NRMat &a); extern NRMat exp0(const NRMat &a); }//namespace #endif