/* LA: linear algebra C++ interface library Copyright (C) 2008 Jiri Pittner or complex versions written by Roman Curik 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 . */ #include "mat.h" #include #include #include #include #include #include extern "C" { extern ssize_t read(int, void *, size_t); extern ssize_t write(int, const void *, size_t); } // TODO : // namespace LA { /* * Templates first, specializations for BLAS next */ //direct sum template const NRMat NRMat::oplus(const NRMat &rhs) const { if(nn==0 && mm == 0) return rhs; if(rhs.nn==0 && rhs.mm== 0) return *this; NRMat r((T)0,nn+rhs.nn,mm+rhs.mm); #ifdef oldversion int i,j; for(i=0;i const NRMat NRMat::otimes(const NRMat &rhs, bool reversecolumns) const { if(nn==0 && mm == 0) return *this; if(rhs.nn==0 && rhs.mm== 0) return rhs; NRMat r((T)0,nn*rhs.nn,mm*rhs.mm); int i,j,k,l; if(reversecolumns) { for(i=0;i const NRVec NRMat::row(const int i, int l) const { #ifdef DEBUG if(i<0||i>=nn) laerror("illegal index in row()"); #endif if(l < 0) l=mm; NRVec r(l); LA_traits::copy(&r[0], #ifdef MATPTR v[i] #else v+i*l #endif ,l); return r; } //raw I/O template void NRMat::put(int fd, bool dim, bool transp) const { errno=0; if(dim) { if(sizeof(int) != write(fd,&(transp?mm:nn),sizeof(int))) laerror("cannot write"); if(sizeof(int) != write(fd,&(transp?nn:mm),sizeof(int))) laerror("cannot write"); } if(transp) //not particularly efficient { for(int j=0; j::put(fd, #ifdef MATPTR v[i][j] #else v[i*mm+j] #endif ,dim,transp); } else LA_traits::multiput(nn*mm,fd, #ifdef MATPTR v[0] #else v #endif ,dim); } template void NRMat::get(int fd, bool dim, bool transp) { int nn0,mm0; errno=0; if(dim) { if(sizeof(int) != read(fd,&nn0,sizeof(int))) laerror("cannot read"); if(sizeof(int) != read(fd,&mm0,sizeof(int))) laerror("cannot read"); if(transp) resize(mm0,nn0); else resize(nn0,mm0); } else copyonwrite(); if(transp) //not particularly efficient { for(int j=0; j::get(fd, #ifdef MATPTR v[i][j] #else v[i*mm+j] #endif ,dim,transp); } else LA_traits::multiget(nn*mm,fd, #ifdef MATPTR v[0] #else v #endif ,dim); } // Assign diagonal template NRMat & NRMat::operator=(const T &a) { copyonwrite(); #ifdef DEBUG if (nn != mm) laerror("RMat.operator=scalar on non-square matrix"); #endif #ifdef MATPTR memset(v[0],0,nn*nn*sizeof(T)); for (int i=0; i< nn; i++) v[i][i] = a; #else memset(v,0,nn*nn*sizeof(T)); for (int i=0; i< nn*nn; i+=nn+1) v[i] = a; #endif return *this; } // M += a template NRMat & NRMat::operator+=(const T &a) { copyonwrite(); #ifdef DEBUG if (nn != mm) laerror("Mat.operator+=scalar on non-square matrix"); #endif #ifdef MATPTR for (int i=0; i< nn; i++) v[i][i] += a; #else for (int i=0; i< nn*nn; i+=nn+1) v[i] += a; #endif return *this; } // M -= a template NRMat & NRMat::operator-=(const T &a) { copyonwrite(); #ifdef DEBUG if (nn != mm) laerror("Mat.operator-=scalar on non-square matrix"); #endif #ifdef MATPTR for (int i=0; i< nn; i++) v[i][i] -= a; #else for (int i=0; i< nn*nn; i+=nn+1) v[i] -= a; #endif return *this; } // unary minus template const NRMat NRMat::operator-() const { NRMat result(nn, mm); #ifdef MATPTR for (int i=0; i const NRMat NRMat::operator&(const NRMat & b) const { NRMat result((T)0, nn+b.nn, mm+b.mm); for (int i=0; i const NRMat NRMat::operator|(const NRMat &b) const { NRMat result(nn*b.nn, mm*b.mm); for (int i=0; i const NRVec NRMat::csum() const { NRVec result(nn); T sum; for (int i=0; i const NRVec NRMat::rsum() const { NRVec result(nn); T sum; for (int i=0; i const NRMat NRMat::submatrix(const int fromrow, const int torow, const int fromcol, const int tocol) const { #ifdef DEBUG if(fromrow <0 ||fromrow >=nn||torow <0 ||torow >=nn ||fromcol<0||fromcol>=mm||tocol<0||tocol>=mm||fromrow>torow||fromcol>tocol) laerror("bad indices in submatrix"); #endif int n=torow-fromrow+1; int m=tocol-fromcol+1; NRMat r(n,m); for(int i=fromrow; i<=torow; ++i) #ifdef MATPTR memcpy(r.v[i-fromrow],v[i]+fromcol,m*sizeof(T)); #else memcpy(r.v+(i-fromrow)*m,v+i*mm+fromcol,m*sizeof(T)); #endif return r; } template void NRMat::storesubmatrix(const int fromrow, const int fromcol, const NRMat &rhs) { int tocol=fromcol+rhs.ncols()-1; int torow=fromrow+rhs.nrows()-1; #ifdef DEBUG if(fromrow <0 ||fromrow >=nn||torow >=nn ||fromcol<0||fromcol>=mm||tocol>=mm) laerror("bad indices in storesubmatrix"); #endif int m=tocol-fromcol+1; for(int i=fromrow; i<=torow; ++i) #ifdef MATPTR memcpy(v[i]+fromcol,rhs.v[i-fromrow],m*sizeof(T)); #else memcpy(v+i*mm+fromcol,rhs.v+(i-fromrow)*m,m*sizeof(T)); #endif } // transpose Mat template NRMat & NRMat::transposeme(int n) { if(n==0) n=nn; #ifdef DEBUG if (n==nn && nn != mm || n>mm || n>nn) laerror("transpose of non-square Mat"); #endif copyonwrite(); for(int i=1; i NRMat >::NRMat(const NRMat &rhs, bool imagpart) : nn(rhs.nrows()), mm(rhs.ncols()), count(new int(1)) { #ifdef MATPTR v = new complex*[n]; v[0] = new complex[mm*nn]; for (int i=1; i)); cblas_dcopy(nn*mm,&rhs[0][0],1,((double *)v[0]) + (imagpart?1:0),2); #else v = new complex[mm*nn]; memset(v, 0, nn*mm*sizeof(complex)); cblas_dcopy(nn*mm,&rhs[0][0],1,((double *)v) + (imagpart?1:0),2); #endif } // Output of Mat template void NRMat::fprintf(FILE *file, const char *format, const int modulo) const { lawritemat(file, (const T*)(*this), nn, mm, format, 2, modulo, 0); } // Input of Mat template void NRMat::fscanf(FILE *f, const char *format) { int n, m; if (::fscanf(f, "%d %d", &n, &m) != 2) laerror("cannot read matrix dimensions in Mat::fscanf()"); resize(n,m); T *p = *this; for(int i=0; i */ template<> const NRSMat NRMat::transposedtimes() const { NRSMat r(mm,mm); int i,j; for(i=0; i const NRSMat > NRMat >::transposedtimes() const { NRSMat > r(mm,mm); int i,j; for(i=0; i const NRSMat NRMat::transposedtimes() const { NRSMat r(mm,mm); int i,j; for(i=0; i const NRSMat NRMat::timestransposed() const { NRSMat r(nn,nn); int i,j; for(i=0; i const NRSMat > NRMat >::timestransposed() const { NRSMat > r(nn,nn); int i,j; for(i=0; i const NRSMat NRMat::timestransposed() const { NRSMat r(nn,nn); int i,j; for(i=0; i void NRMat::randomize(const double &x) { for(int i=0; i void NRMat >::randomize(const double &x) { for(int i=0; i NRMat & NRMat::operator*=(const double &a) { copyonwrite(); cblas_dscal(nn*mm, a, *this, 1); return *this; } template<> NRMat< complex > & NRMat< complex >::operator*=(const complex &a) { copyonwrite(); cblas_zscal(nn*mm, &a, (*this)[0], 1); return *this; } //and for general type template NRMat & NRMat::operator*=(const T &a) { copyonwrite(); #ifdef MATPTR for (int i=0; i< nn*mm; i++) v[0][i] *= a; #else for (int i=0; i< nn*mm; i++) v[i] *= a; #endif return *this; } // Mat += Mat template<> NRMat & NRMat::operator+=(const NRMat &rhs) { #ifdef DEBUG if (nn != rhs.nn || mm!= rhs.mm) laerror("Mat += Mat of incompatible matrices"); #endif copyonwrite(); cblas_daxpy(nn*mm, 1.0, rhs, 1, *this, 1); return *this; } template<> NRMat< complex > & NRMat< complex >::operator+=(const NRMat< complex > &rhs) { #ifdef DEBUG if (nn != rhs.nn || mm!= rhs.mm) laerror("Mat += Mat of incompatible matrices"); #endif copyonwrite(); cblas_zaxpy(nn*mm, &CONE, rhs[0], 1, (*this)[0], 1); return *this; } //and for general type template NRMat & NRMat::operator+=(const NRMat &rhs) { #ifdef DEBUG if (nn != rhs.nn || mm!= rhs.mm) laerror("Mat -= Mat of incompatible matrices"); #endif copyonwrite(); #ifdef MATPTR for (int i=0; i< nn*mm; i++) v[0][i] += rhs.v[0][i] ; #else for (int i=0; i< nn*mm; i++) v[i] += rhs.v[i] ; #endif return *this; } // Mat -= Mat template<> NRMat & NRMat::operator-=(const NRMat &rhs) { #ifdef DEBUG if (nn != rhs.nn || mm!= rhs.mm) laerror("Mat -= Mat of incompatible matrices"); #endif copyonwrite(); cblas_daxpy(nn*mm, -1.0, rhs, 1, *this, 1); return *this; } template<> NRMat< complex > & NRMat< complex >::operator-=(const NRMat< complex > &rhs) { #ifdef DEBUG if (nn != rhs.nn || mm!= rhs.mm) laerror("Mat -= Mat of incompatible matrices"); #endif copyonwrite(); cblas_zaxpy(nn*mm, &CMONE, rhs[0], 1, (*this)[0], 1); return *this; } //and for general type template NRMat & NRMat::operator-=(const NRMat &rhs) { #ifdef DEBUG if (nn != rhs.nn || mm!= rhs.mm) laerror("Mat -= Mat of incompatible matrices"); #endif copyonwrite(); #ifdef MATPTR for (int i=0; i< nn*mm; i++) v[0][i] -= rhs.v[0][i] ; #else for (int i=0; i< nn*mm; i++) v[i] -= rhs.v[i] ; #endif return *this; } // Mat += SMat template<> NRMat & NRMat::operator+=(const NRSMat &rhs) { #ifdef DEBUG if (nn!=mm || nn!=rhs.nrows()) laerror("incompatible matrix size in Mat+=SMat"); #endif const double *p = rhs; copyonwrite(); for (int i=0; i NRMat< complex > & NRMat< complex >::operator+=(const NRSMat< complex > &rhs) { #ifdef DEBUG if (nn!=mm || nn!=rhs.nrows()) laerror("incompatible matrix size in Mat+=SMat"); #endif const complex *p = rhs; copyonwrite(); for (int i=0; i NRMat & NRMat::operator+=(const NRSMat &rhs) { #ifdef DEBUG if (nn!=mm || nn!=rhs.nrows()) laerror("incompatible matrix size in Mat+=SMat"); #endif const T *p = rhs; copyonwrite(); for (int i=0; i NRMat & NRMat::operator-=(const NRSMat &rhs) { #ifdef DEBUG if (nn!=mm || nn!=rhs.nrows()) laerror("incompatible matrix size in Mat-=SMat"); #endif const double *p = rhs; copyonwrite(); for (int i=0; i NRMat< complex > & NRMat< complex >::operator-=(const NRSMat< complex > &rhs) { #ifdef DEBUG if (nn!=mm || nn!=rhs.nrows()) laerror("incompatible matrix size in Mat-=SMat"); #endif const complex *p = rhs; copyonwrite(); for (int i=0; i NRMat & NRMat::operator-=(const NRSMat &rhs) { #ifdef DEBUG if (nn!=mm || nn!=rhs.nrows()) laerror("incompatible matrix size in Mat+=SMat"); #endif const T *p = rhs; copyonwrite(); for (int i=0; i const double NRMat::dot(const NRMat &rhs) const { #ifdef DEBUG if(nn!=rhs.nn || mm!= rhs.mm) laerror("Mat.Mat incompatible matrices"); #endif return cblas_ddot(nn*mm, (*this)[0], 1, rhs[0], 1); } template<> const complex NRMat< complex >::dot(const NRMat< complex > &rhs) const { #ifdef DEBUG if(nn!=rhs.nn || mm!= rhs.mm) laerror("Mat.Mat incompatible matrices"); #endif complex dot; cblas_zdotc_sub(nn*mm, (*this)[0], 1, rhs[0], 1, &dot); return dot; } // Mat * Mat template<> const NRMat NRMat::operator*(const NRMat &rhs) const { #ifdef DEBUG if (mm != rhs.nn) laerror("product of incompatible matrices"); if (rhs.mm <=0) laerror("illegal matrix dimension in gemm"); #endif NRMat result(nn, rhs.mm); cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nn, rhs.mm, mm, 1.0, *this, mm, rhs, rhs.mm, 0.0, result, rhs.mm); return result; } template<> const NRMat< complex > NRMat< complex >::operator*(const NRMat< complex > &rhs) const { #ifdef DEBUG if (mm != rhs.nn) laerror("product of incompatible matrices"); #endif NRMat< complex > result(nn, rhs.mm); cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nn, rhs.mm, mm, &CONE,(*this)[0], mm, rhs[0], rhs.mm, &CZERO, result[0], rhs.mm); return result; } // Multiply by diagonal from L template<> void NRMat::diagmultl(const NRVec &rhs) { #ifdef DEBUG if (nn != rhs.size()) laerror("incompatible matrix dimension in diagmultl"); #endif copyonwrite(); for(int i=0; i void NRMat< complex >::diagmultl(const NRVec< complex > &rhs) { #ifdef DEBUG if (nn != rhs.size()) laerror("incompatible matrix dimension in diagmultl"); #endif copyonwrite(); for (int i=0; i void NRMat::diagmultr(const NRVec &rhs) { #ifdef DEBUG if (mm != rhs.size()) laerror("incompatible matrix dimension in diagmultr"); #endif copyonwrite(); for (int i=0; i void NRMat< complex >::diagmultr(const NRVec< complex > &rhs) { #ifdef DEBUG if (mm != rhs.size()) laerror("incompatible matrix dimension in diagmultl"); #endif copyonwrite(); for (int i=0; i const NRMat NRMat::operator*(const NRSMat &rhs) const { #ifdef DEBUG if (mm != rhs.nrows()) laerror("incompatible dimension in Mat*SMat"); #endif NRMat result(nn, rhs.ncols()); for (int i=0; i const NRMat< complex > NRMat< complex >::operator*(const NRSMat< complex > &rhs) const { #ifdef DEBUG if (mm != rhs.nrows()) laerror("incompatible dimension in Mat*SMat"); #endif NRMat< complex > result(nn, rhs.ncols()); for (int i=0; i NRMat &NRMat::conjugateme() {return *this;} template<> NRMat< complex > & NRMat< complex >::conjugateme() { copyonwrite(); cblas_dscal(mm*nn, -1.0, (double *)((*this)[0])+1, 2); return *this; } // transpose and optionally conjugate template<> const NRMat NRMat::transpose(bool conj) const { NRMat result(mm,nn); for(int i=0; i const NRMat< complex > NRMat< complex >::transpose(bool conj) const { NRMat< complex > result(mm,nn); for (int i=0; i void NRMat::gemm(const double &beta, const NRMat &a, const char transa, const NRMat &b, const char transb, const double &alpha) { int k(transa=='n'?a.mm:a.nn); #ifdef DEBUG int l(transa=='n'?a.nn:a.mm); int kk(transb=='n'?b.nn:b.mm); int ll(transb=='n'?b.mm:b.nn); if (l!=nn || ll!=mm || k!=kk) laerror("incompatible matrices in Mat:gemm()"); if(b.mm <=0 || mm<=0) laerror("illegal matrix dimension in gemm"); #endif if (alpha==0.0 && beta==1.0) return; copyonwrite(); cblas_dgemm(CblasRowMajor, (transa=='n' ? CblasNoTrans : CblasTrans), (transb=='n' ? CblasNoTrans : CblasTrans), nn, mm, k, alpha, a, a.mm, b , b.mm, beta, *this , mm); } template<> void NRMat< complex >::gemm(const complex & beta, const NRMat< complex > & a, const char transa, const NRMat< complex > & b, const char transb, const complex & alpha) { int k(transa=='n'?a.mm:a.nn); #ifdef DEBUG int l(transa=='n'?a.nn:a.mm); int kk(transb=='n'?b.nn:b.mm); int ll(transb=='n'?b.mm:b.nn); if (l!=nn || ll!=mm || k!=kk) laerror("incompatible matrices in Mat:gemm()"); #endif if (alpha==CZERO && beta==CONE) return; copyonwrite(); cblas_zgemm(CblasRowMajor, (transa=='n' ? CblasNoTrans : (transa=='c'?CblasConjTrans:CblasTrans)), (transb=='n' ? CblasNoTrans : (transa=='c'?CblasConjTrans:CblasTrans)), nn, mm, k, &alpha, a , a.mm, b , b.mm, &beta, *this , mm); } // norm of Mat template<> const double NRMat::norm(const double scalar) const { if (!scalar) return cblas_dnrm2(nn*mm, (*this)[0], 1); double sum = 0; for (int i=0; i const double NRMat< complex >::norm(const complex scalar) const { if (scalar == CZERO) return cblas_dznrm2(nn*mm, (*this)[0], 1); double sum = 0; for (int i=0; i tmp; #ifdef MATPTR tmp = v[i][j]; #else tmp = v[i*mm+j]; #endif if (i==j) tmp -= scalar; sum += tmp.real()*tmp.real()+tmp.imag()*tmp.imag(); } return std::sqrt(sum); } // axpy: this = a * Mat template<> void NRMat::axpy(const double alpha, const NRMat &mat) { #ifdef DEBUG if (nn!=mat.nn || mm!=mat.mm) laerror("daxpy of incompatible matrices"); #endif copyonwrite(); cblas_daxpy(nn*mm, alpha, mat, 1, *this, 1); } template<> void NRMat< complex >::axpy(const complex alpha, const NRMat< complex > & mat) { #ifdef DEBUG if (nn!=mat.nn || mm!=mat.mm) laerror("zaxpy of incompatible matrices"); #endif copyonwrite(); cblas_zaxpy(nn*mm, &alpha, mat, 1, (*this)[0], 1); } // trace of Mat template const T NRMat::trace() const { #ifdef DEBUG if (nn != mm) laerror("no-square matrix in Mat::trace()"); #endif T sum=0; #ifdef MATPTR for (int i=0; i const double * NRMat::diagonalof(NRVec &r, const bool divide, bool cache) const { if (r.size() != nn) laerror("diagonalof() incompatible vector"); double a; r.copyonwrite(); if(nn==mm) { #ifdef MATPTR if(divide) for (int i=0; i< nn; i++) if((a=v[i][i])) r[i]/=a; else for (int i=0; i< nn; i++) r[i] = v[i][i]; #else if(divide) {int i,j; for (i=j=0; j< nn; ++j, i+=nn+1) if((a=v[i])) r[j] /=a;} else {int i,j; for (i=j=0; j< nn; ++j, i+=nn+1) r[j] = v[i];} #endif } else //non-square { for (int i=0; i< mm; i++) { #ifdef MATPTR a= cblas_ddot(nn,v[0]+i,mm,v[0]+i,mm); #else a=cblas_ddot(nn,v+i,mm,v+i,mm); #endif if(divide) {if(a) r[i]/=a;} else r[i] = a; } } return divide?NULL:&r[0]; } //set diagonal template<> void NRMat::diagonalset(const NRVec &r) { if (r.size() != nn) laerror("diagonalset() incompatible vector"); if(nn!=mm) laerror("diagonalset only for square matrix"); copyonwrite(); #ifdef MATPTR for (int i=0; i< nn; i++) v[i][i] = r[i]; #else {int i,j; for (i=j=0; j< nn; ++j, i+=nn+1) v[i] = r[j];} #endif } template<> void NRMat::orthonormalize(const bool rowcol, const NRSMat *metric) //modified Gram-Schmidt { if(metric) //general metric { if(rowcol) //vectors are rows { if((*metric).nrows() != mm) laerror("incompatible metric in orthonormalize"); for(int j=0; j tmp = *metric * (*this).row(i); double fact = cblas_ddot(mm,(*this)[j],1,tmp,1); cblas_daxpy(mm,-fact,(*this)[i],1,(*this)[j],1); } NRVec tmp = *metric * (*this).row(j); double norm = cblas_ddot(mm,(*this)[j],1,tmp,1); if(norm<=0.) laerror("zero vector in orthonormalize or nonpositive metric"); cblas_dscal(mm,1./std::sqrt(norm),(*this)[j],1); } } else //vectors are columns { if((*metric).nrows() != nn) laerror("incompatible metric in orthonormalize"); for(int j=0; j tmp = *metric * (*this).column(i); double fact = cblas_ddot(nn,&(*this)[0][j],mm,tmp,1); cblas_daxpy(nn,-fact,&(*this)[0][i],mm,&(*this)[0][j],mm); } NRVec tmp = *metric * (*this).column(j); double norm = cblas_ddot(nn,&(*this)[0][j],mm,tmp,1); if(norm<=0.) laerror("zero vector in orthonormalize or nonpositive metric"); cblas_dscal(nn,1./std::sqrt(norm),&(*this)[0][j],mm); } } } else //unit metric { if(rowcol) //vectors are rows { for(int j=0; j; template class NRMat >; template class NRMat; template class NRMat; template class NRMat; template class NRMat; template class NRMat; template class NRMat; template class NRMat; template class NRMat; template class NRMat; template class NRMat; }//namespace