/* 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 _SPARSESMAT_H_ #define _SPARSESMAT_H_ #include #include #include #include #include #include #include #include "la_traits.h" #include "sparsemat.h" #include "vec.h" #include "mat.h" #include "smat.h" #include "qsort.h" #include #include #define CHOLESKYEPSILON 1e-16 namespace LA { //sparse matrix stored by rows, each row a std::map from column index to value, for efficient exponentiation and products //originally written for symmetric matrices, in particular the unitary complex symmetric exp(iH) with H real symmetric, //but it need not be symmetric nor square: elements (i,j) and (j,i) are stored independently and operator*, gemv, //transpose and the conversions work for general matrices //the "S" remains in some conventions: add() with both=true (default) inserts (i,j) and conj at (j,i) (hermitian for complex), //as do the constructor from NRSMat and otimes_sparse; gemm assumes A=A^T (A^dagger); issymmetric() returns always true (for davidson) //indices are counted from zero //TODO: handle it as a general matrix class with a member symmetry_type (0 none, 1 symmetric, 2 hermitian); //the conversion from NRSMat, otimes_sparse, gemm (and issymmetric) would then be implemented according to it, in a backward compatible way template class SparseSMat { protected: SPMatindex nn; SPMatindex mm; std::map **v; int *count; public: SparseSMat() : nn(0), mm(0), v(NULL), count(NULL) {}; explicit SparseSMat(const SPMatindex n, const SPMatindex m); //prevent double -> int -> SparseSMat explicit SparseSMat(const SPMatindex n); SparseSMat(const SparseSMat &rhs); explicit SparseSMat(const SparseMat &rhs); explicit SparseSMat(const NRSMat &rhs); explicit SparseSMat(const NRMat &rhs); explicit SparseSMat(const CSRMat &rhs); SparseSMat & operator=(const SparseSMat &rhs); void copyonwrite(); void resize(const SPMatindex nn, const SPMatindex mm); void dealloc(void) {resize(0,0);} inline void setcoldim(int i) {mm=(SPMatindex)i;}; inline const std::map *line(const SPMatindex n) const {return v[n];}; //read-only row access typedef std::map *ROWTYPE; inline typename SparseSMat::ROWTYPE & operator[](const SPMatindex i) {return v[i];}; void clear() {resize(nn,mm);} unsigned long long simplify(); ~SparseSMat(); inline int getcount() const {return count?*count:0;} SparseSMat & operator*=(const T &a); //multiply by a scalar inline const SparseSMat operator*(const T &rhs) const {return SparseSMat(*this) *= rhs;} inline const SparseSMat operator+(const T &rhs) const {return SparseSMat(*this) += rhs;} inline const SparseSMat operator-(const T &rhs) const {return SparseSMat(*this) -= rhs;} inline const SparseSMat operator+(const SparseSMat &rhs) const {return SparseSMat(*this) += rhs;} inline const SparseSMat operator-(const SparseSMat &rhs) const {return SparseSMat(*this) -= rhs;} SparseSMat & operator=(const T &a); //assign a to diagonal SparseSMat & operator+=(const T &a); //assign a to diagonal SparseSMat & operator-=(const T &a); //assign a to diagonal void axpy(const T alpha, const SparseSMat &x, const bool transp=0); // this+= a*x inline SparseSMat & operator+=(const SparseSMat &rhs) {axpy((T)1,rhs); return *this;}; inline SparseSMat & operator-=(const SparseSMat &rhs) {axpy((T)-1,rhs); return *this;}; const T* diagonalof(NRVec &, const bool divide=0, bool cache=false) const; //get diagonal void gemv(const T beta, NRVec &r, const char trans, const T alpha, const NRVec &x) const; inline const NRVec operator*(const NRVec &rhs) const {NRVec result(nn); this->gemv((T)0,result,'n',(T)1,rhs); return result;}; typename LA_traits::normtype norm(const T scalar=(T)0) const; const SparseSMat operator*(const SparseSMat &rhs) const; //general product A B of the stored matrices (hermitian, complex symmetric or general) void gemm(const T beta, const SparseSMat &a, const char transa, const SparseSMat &b, const char transb, const T alpha); //this := alpha*A^T*op( B ) + beta*this (A^dagger if transa=='c'), i.e. A*op(B) only for A=A^T (A=A^dagger) !!!NOT A GENERAL ROUTINE inline void add(const SPMatindex n, const SPMatindex m, const T elem, const bool both=true); inline unsigned long long length() {return simplify();}; void transposeme() const {laerror("in-place transposition not necessary/implemented for SparseSMat");}; SparseSMat transpose(bool conj=false) const; //if we store a non-symmetric matrix there inline bool issymmetric() const {return true;} // LV: for davidson void get(int fd, bool dimen=1, bool transp=0, bool orcaformat=false); //orcaformat not supported (dense only), for LA_traits void put(int fd, bool dimen=1, bool transp=0, bool orcaformat=false) const; int nrows() const {return nn;} int ncols() const {return mm;} SparseSMat cholesky(void) const; SparseSMat submatrix(const int fromrow, const int torow, const int fromcol, const int tocol) const; void storesubmatrix(const int fromrow, const int fromcol, const SparseSMat &rhs); class iterator {//not efficient, just for output to ostreams private: matel *p; matel my; SPMatindex row; typename std::map::iterator *col; typename std::map::iterator mycol; SPMatindex mynn; SPMatindex mymm; std::map **myv; public: //compiler-generated iterator & operator=(const iterator &rhs); //compiler-generated iterator(const iterator &rhs); iterator(): p(NULL),row(0),col(NULL),mynn(0),mymm(0),myv(NULL) {}; iterator(const SparseSMat &rhs) : mynn(rhs.nn), mymm(rhs.mm), myv(rhs.v), col(NULL) {row=0; p= &my; operator++();} iterator operator++() { if(col) //finish column list { ++mycol; if(mycol != myv[row]->end()) { p->row = row; p->col = mycol->first; p->elem = mycol->second; return *this; } else { col=NULL; ++row; if(row==mynn) {p=NULL; return *this;} //end() } } nextrow: while(myv[row]==NULL) {++row; if(row==mynn) {p=NULL; return *this;}} //end() //we are at next nonempty row col = &mycol; mycol = myv[row]->begin(); if(mycol == myv[row]->end()) {col=NULL; ++row; if(row==mynn) {p=NULL; return *this;} else goto nextrow; } //first column of new row p->row = row; p->col = mycol->first; p->elem = mycol->second; return *this; }; iterator(matel *q) {p=q; col=NULL;}//just for end() //compiler-generated ~iterator() {}; bool operator!=(const iterator &rhs) const {return p!=rhs.p;} //only for comparison with end() bool operator==(const iterator &rhs) const {return p==rhs.p;} //only for comparison with end() matel & operator*() const {return *p;} matel * operator->() const {return p;} bool notend() const {return (bool)p;}; }; iterator begin() const {return iterator(*this);} iterator end() const {return iterator(NULL);} }; template SparseSMat::SparseSMat(const SPMatindex n) :nn(n), mm(n), count(new int(1)) { v= new std::map * [n]; memset(v,0,nn*sizeof(std::map *)); } template SparseSMat::SparseSMat(const SPMatindex n, const SPMatindex m) :nn(n), mm(m), count(new int(1)) { v= new std::map * [n]; memset(v,0,nn*sizeof(std::map *)); } template SparseSMat::SparseSMat(const NRSMat &rhs) :nn(rhs.nrows()), mm(rhs.ncols()), count(new int(1)) { v= new std::map * [nn]; memset(v,0,nn*sizeof(std::map *)); int i,j; for(i=0; iSPARSEEPSILON) (*this).add(i,j,rhs(i,j),true); } //from SparseMat: duplicates summed; a symmetric SparseMat (one triangle stored, complex symmetric) is unfolded without conjugation template SparseSMat::SparseSMat(const SparseMat &rhs) :nn(rhs.nrows()), mm(rhs.ncols()), count(new int(1)) { v= new std::map * [nn]; memset(v,0,nn*sizeof(std::map *)); const bool sym=rhs.issymmetric(); for(const matel *l=rhs.getlist(); l; l=l->next) { add(l->row,l->col,l->elem,false); if(sym && l->row!=l->col) add(l->col,l->row,l->elem,false); } } template SparseSMat::SparseSMat(const NRMat &rhs) :nn(rhs.nrows()), mm(rhs.ncols()), count(new int(1)) { v= new std::map * [nn]; memset(v,0,nn*sizeof(std::map *)); int i,j; for(i=0; iSPARSEEPSILON) (*this).add(i,j,rhs(i,j),false); } template SparseSMat::SparseSMat(const SparseSMat &rhs) { v = rhs.v; nn = rhs.nn; mm = rhs.mm; count = rhs.count; if(count) (*count)++; } //NRSMat from SparseSMat #define nn2 (nn*(nn+1)/2) template NRSMat::NRSMat(const SparseSMat &rhs) : nn(rhs.nrows()) { if(rhs.nrows()!=rhs.ncols()) laerror("cannot transform rectangular matrix to NRSMat"); #ifdef CUDALA location = cpu; #endif count = new int(1); v=new T[nn2]; memset(v,0,nn2*sizeof(T)); typename SparseSMat::iterator p(rhs); for(; p.notend(); ++p) if(p->row >= p->col) (*this)(p->row,p->col)=p->elem; //lower triangle as stored in NRSMat } #undef nn2 //construct dense from sparse template NRMat::NRMat(const SparseSMat &rhs) : nn(rhs.nrows()), mm(rhs.ncols()), count(new int(1)) { #ifdef CUDALA location = cpu; #endif #ifdef MATPTR v = new T*[nn]; v[0] = new T[mm*nn]; for (int i=1; i::iterator p(rhs); for(; p.notend(); ++p) (*this)(p->row,p->col)= p->elem; } template SparseSMat::~SparseSMat() { if(!count) return; if(--(*count) <= 0) { if(v) { for(SPMatindex i=0; i void SparseSMat::resize(const SPMatindex n, const SPMatindex m) { if(!count) { if(n==0) return; count = new int(1); nn=n; mm=m; v= new std::map * [nn]; for(SPMatindex i=0; i 1) //it was shared { (*count)--; if(n) { count = new int(1); nn=n; mm=m; v= new std::map * [nn]; for(SPMatindex i=0; inn) {delete[] v; v= new std::map * [n];} //shrinking keeps the longer array nn=n; mm=m; for(SPMatindex i=0; i SparseSMat & SparseSMat::operator=(const SparseSMat &rhs) { if (this != &rhs) { if(count) if(--(*count) == 0) { if(v) { for(SPMatindex i=0; i void SparseSMat::copyonwrite() { if(!count) laerror("SparseSmat::copyonwrite() of an undefined object"); if(*count > 1) { (*count)--; count = new int; *count = 1; typename std::map **newv= new std::map * [nn]; for(SPMatindex i=0; i(*(v[i])); //deep copy of each map else newv[i]= NULL; v = newv; } } //general product A B row by row: row i of the result is sum_k A_ik (row k of B) template const SparseSMat SparseSMat::operator*(const SparseSMat &rhs) const { if(mm!=rhs.nn) laerror("incompatible sizes in SparseSMat::operator*"); SparseSMat r(nn,rhs.mm); for(SPMatindex i=0; i *ri=NULL; typename std::map::const_iterator p,q; for(p=v[i]->begin(); p!=v[i]->end(); ++p) if(rhs.v[p->first]) { if(!ri) ri = r.v[i] = new std::map; for(q=rhs.v[p->first]->begin(); q!=rhs.v[p->first]->end(); ++q) (*ri)[q->first] += p->second * q->second; //new map element is value-initialized to 0 } } r.simplify(); return r; } template void SparseSMat::add(const SPMatindex n, const SPMatindex m, const T elem, const bool both) { #ifdef DEBUG if(n>=nn || m>=mm) laerror("illegal index in SparseSMat::add()"); #endif if(!v[n]) v[n] = new std::map; typename std::map::iterator p; p= v[n]->find(m); if(p!=v[n]->end()) p->second+=elem; else (*v[n])[m] = elem; if(n!=m && both) //add also transposed, conjugated (hermitian for complex) { if(!v[m]) v[m] = new std::map; p= v[m]->find(n); if(p!=v[m]->end()) p->second+=LA_traits::conjugate(elem); else (*v[m])[n] = LA_traits::conjugate(elem); } //addition can lead to zero, but do not treat it now, make a simplify } template unsigned long long SparseSMat::simplify() { unsigned long long count=0; for(SPMatindex i=0; i l; typename std::map::iterator p; for(p=v[i]->begin(); p!=v[i]->end(); ++p) if(std::abs(p->second) < SPARSEEPSILON) l.push_front(p->first); else ++count; typename std::list::iterator q; for(q=l.begin(); q!=l.end(); ++q) v[i]->erase(*q); if(v[i]->size() == 0) {delete v[i]; v[i]=NULL;} } return count; } template std::ostream & operator<<(std::ostream &s, const SparseSMat &x) { SPMatindex n; s << x.nrows() << " "<< x.ncols()<< std::endl; typename SparseSMat::iterator p(x); for(; p.notend(); ++p) s << (int)p->row << ' ' << (int)p->col << ' ' << (typename LA_traits_io::IOtype) p->elem << '\n'; s << "-1 -1\n"; return s; } template std::istream& operator>>(std::istream &s, SparseSMat &x) { SPMatindex n,m; long i,j; s >> n >> m; if(n!=m) laerror("SparseSMat must be square"); x.resize(n,m); s >> i >> j; typename LA_traits_io::IOtype tmp; while(i>=0 && j>=0) { s>>tmp; if(i>=n||j>=m) laerror("bad index in SparseSMat::operator>>"); x.add(i,j,tmp,false); s >> i >> j; } return s; } template SparseSMat SparseSMat::transpose(bool conj) const { SparseSMat r(mm,nn); typename SparseSMat::iterator p(*this); for(; p.notend(); ++p) r.add(p->col, p->row, (conj?LA_traits::conjugate(p->elem):p->elem), false); return r; } //Cholesky decomposition, pivoted, positive semidefinite, not in place //it is NOT checked that the input matrix is symmetric/hermitean //result.transpose(true)*result reproduces the original matrix //Due to pivoting the result is upper triangular only before applying final permutation // template SparseSMat SparseSMat::cholesky(void) const { if(nn!=mm) laerror("Cholesky defined only for square matrices"); //we need real values for sorting, if T is already real it makes just an unnecessary copy of one vector NRVec::normtype> diagreal(nn); { NRVec diag(nn); diagonalof(diag); for(SPMatindex i=0; i::realpart(diag[i]); } NRVec pivot(nn); for(int i=0; i invpivot(nn); for(int i=0; i r; r.nn=nn; r.mm=nn; r.count = new int(1); r.v = new std::map * [nn]; for(SPMatindex i=0; i; typename std::map::iterator p; for(p=v[pivot[i]]->begin(); p!=v[pivot[i]]->end(); ++p) { if(invpivot[p->first] >= i) { (*r.v[i])[invpivot[p->first]] = p->second; } } } else r.v[i]= NULL; //std::cout <<"Permuted upper triangle matrix\n"< r0(r);r.copyonwrite(); //perform complex, positive semidefinite Cholesky with thresholding by SPARSEEPSILON SPMatindex i,j,k; int rank=0; for(k=0; k::iterator p; p= r.v[k]->find(k); if(p==r.v[k]->end()) continue; //must not break due to the a priori pivoting if(LA_traits::realpart(p->second) < CHOLESKYEPSILON) continue; //must not break due to the a priori pivoting ++rank; typename LA_traits::normtype tmp = std::sqrt(LA_traits::realpart(p->second)); p->second = tmp; NRVec linek(0.,nn); for(p=r.v[k]->begin(); p!=r.v[k]->end(); ++p) { if(p->first > k) p->second /= tmp; linek[p->first] = p->second; } for(j=k+1; j::conjugate(linek[j]); NRVec linek_used(0,nn); if(std::abs(akj)>SPARSEEPSILON) { for(p=r.v[j]->begin(); p!=r.v[j]->end(); ++p) { p->second -= akj * linek[p->first]; linek_used[p->first]=1; } //subtract also elements nonzero in line k but non-existent in line j for(i=j; i SPARSEEPSILON) { (*r.v[j])[i] = -akj * linek[i]; } } } } /* SparseSMat br(nn); br.gemm(0,r,'c',r,'n',1); //cancel low triangle from br for(k=0; k::iterator p; for(p=br.v[k]->begin(); p!=br.v[k]->end(); ++p) if(p->first second=0.; } std::cout << "Error before permute = " <<(br-r0).norm()<::iterator p; typename std::map *vnew = new typename std::map; for(p=r.v[k]->begin(); p!=r.v[k]->end(); ++p) { if(std::abs(p->second) > SPARSEEPSILON) (*vnew)[pivot[p->first]] = p->second; } delete r.v[k]; r.v[k]=vnew; } return r; } //outer product expected to be sparse template SparseSMat otimes_sparse(const NRVec &lhs, const NRVec &rhs, const bool conjugate=false, const T &scale=1) { if(lhs.size()!=rhs.size()) laerror("otimes_sparse requires vectors of equal length"); SparseSMat r(lhs.size(),rhs.size()); for(SPMatindex i=0; i::conjugate(rhs[j]):rhs[j])*scale; if(i==j) x+=LA_traits::conjugate(x); //diagonal of M + M^\dagger, add() mirrors only off-diagonal elements if(std::abs(x)>SPARSEEPSILON) r.add(i,j,x); } } return r; } }//namespace #endif //_SPARSESMAT_H_