/* 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 _SPARSEMAT_H_ #define _SPARSEMAT_H_ #include "la_traits.h" namespace LA { //threshold for neglecting elements, if not defined, no tests are done except exact zero test in simplify - might be even faster //seems to perform better with a threshold, in spite of abs() tests const double SPARSEEPSILON=1e-35; typedef unsigned int SPMatindex; typedef int SPMatindexdiff; //more clear would be to use traits //element of a linked list template struct matel { T elem; SPMatindex row; SPMatindex col; matel *next; }; template class SparseMat { protected: SPMatindex nn; SPMatindex mm; bool symmetric; unsigned int nonzero; int *count; matel *list; private: matel **rowsorted; //NULL terminated matel **colsorted; //NULL terminated void unsort(); void deletelist(); void copylist(const matel *l); public: //iterator class iterator { private: matel *p; public: iterator() {}; ~iterator() {}; iterator(matel *list): p(list) {}; bool operator==(const iterator rhs) const {return p==rhs.p;} bool operator!=(const iterator rhs) const {return p!=rhs.p;} iterator operator++() {return p=p->next;} iterator operator++(int) {matel *q=p; p=p->next; return q;} matel & operator*() const {return *p;} matel * operator->() const {return p;} }; iterator begin() const {return list;} iterator end() const {return NULL;} //constructors etc. inline SparseMat() :nn(0),mm(0),symmetric(0),nonzero(0),count(NULL),list(NULL),rowsorted(NULL),colsorted(NULL) {}; inline SparseMat(const SPMatindex n, const SPMatindex m) :nn(n),mm(m),symmetric(0),nonzero(0),count(new int(1)),list(NULL),rowsorted(NULL),colsorted(NULL) {}; SparseMat(const SparseMat &rhs); //copy constructor inline int getcount() const {return count?*count:0;} explicit SparseMat(const NRMat &rhs); //construct from a dense one explicit SparseMat(const NRSMat &rhs); //construct from a dense symmetric one SparseMat & operator=(const SparseMat &rhs); SparseMat & operator=(const T &a); //assign a to diagonal SparseMat & operator+=(const T &a); //assign a to diagonal SparseMat & operator-=(const T &a); //assign a to diagonal SparseMat & operator*=(const T &a); //multiply by a scalar SparseMat & operator+=(const SparseMat &rhs); SparseMat & addtriangle(const SparseMat &rhs, const bool lower, const char sign); SparseMat & join(SparseMat &rhs); //more efficient +=, rhs will be emptied SparseMat & operator-=(const SparseMat &rhs); inline const SparseMat operator+(const T &rhs) const {return SparseMat(*this) += rhs;} inline const SparseMat operator-(const T &rhs) const {return SparseMat(*this) -= rhs;} inline const SparseMat operator*(const T &rhs) const {return SparseMat(*this) *= rhs;} inline const SparseMat operator+(const SparseMat &rhs) const {return SparseMat(*this) += rhs;} //must not be symmetric+general inline const SparseMat operator-(const SparseMat &rhs) const {return SparseMat(*this) -= rhs;} //must not be symmetric+general inline const NRVec operator*(const NRVec &rhs) const; // SparseMat * Vec inline const NRMat operator*(const NRMat &rhs) const; // SparseMat * Mat 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, bool treat_as_symmetric=false) const {r.gemv(beta,*this,trans,alpha,x,treat_as_symmetric);}; const SparseMat operator*(const SparseMat &rhs) const; SparseMat & oplusequal(const SparseMat &rhs); //direct sum SparseMat & oplusequal(const NRMat &rhs); SparseMat & oplusequal(const NRSMat &rhs); const SparseMat oplus(const SparseMat &rhs) const {return SparseMat(*this).oplusequal(rhs);}; //direct sum const SparseMat oplus(const NRMat &rhs) const {return SparseMat(*this).oplusequal(rhs);}; const SparseMat oplus(const NRSMat &rhs) const {return SparseMat(*this).oplusequal(rhs);}; const SparseMat otimes(const SparseMat &rhs) const; //direct product const SparseMat otimes(const NRMat &rhs) const; const SparseMat otimes(const NRSMat &rhs) const; void gemm(const T beta, const SparseMat &a, const char transa, const SparseMat &b, const char transb, const T alpha);//this := alpha*op( A )*op( B ) + beta*this, if this is symemtric, only half will be added onto it const T dot(const SparseMat &rhs) const; //supervector dot product const T dot(const NRMat &rhs) const; //supervector dot product inline ~SparseMat(); void axpy(const T alpha, const SparseMat &x, const bool transp=0); // this+= a*x(transposed) inline matel *getlist() const {return list;} void setlist(matel *l) {list=l;} inline SPMatindex nrows() const {return nn;} inline SPMatindex ncols() const {return mm;} void get(int fd, bool dimensions=1, bool transposed=false); void put(int fd, bool dimensions=1, bool transposed=false) const; void resize(const SPMatindex n, const SPMatindex m); //destructive void dealloc(void) {resize(0,0);} void incsize(const SPMatindex n, const SPMatindex m); //increase size without destroying the data void transposeme(); const SparseMat transpose() const; void permuteindices(const NRVec &p); void permuterows(const NRVec &p); void permutecolumns(const NRVec &p); inline void setsymmetric() {if(nn!=mm) laerror("non-square cannot be symmetric"); symmetric=1;} inline void defineunsymmetric() {symmetric=0;} //just define and do nothing with it void setunsymmetric();//unwind the matrix assuming it was indeed symmetric inline bool issymmetric() const {return symmetric;} unsigned int length() const; void copyonwrite(bool detachonly=false); void clear() {copyonwrite(true); if(count) {delete count; count=NULL;}} void simplify(); const T trace() const; const typename LA_traits::normtype norm(const T scalar=(T)0) const; //is const only mathematically, not in internal implementation - we have to simplify first unsigned int sort(int type) const; inline void add(const SPMatindex n, const SPMatindex m, const T elem) {matel *ltmp= new matel; ltmp->next=list; list=ltmp; list->row=n; list->col=m; list->elem=elem;} void addsafe(const SPMatindex n, const SPMatindex m, const T elem); }; }//namespace //due to mutual includes this has to be after full class declaration #include "vec.h" #include "smat.h" #include "mat.h" namespace LA { template inline const NRVec SparseMat::operator*(const NRVec &rhs) const {NRVec result(nn); result.gemv((T)0,*this,'n',(T)1,rhs); return result;}; template inline const NRMat SparseMat::operator*(const NRMat &rhs) const {NRMat result(nn,rhs.ncols()); result.gemm((T)0,*this,'n',rhs,'n',(T)1); return result;}; template std::ostream& operator<<(std::ostream &s, const SparseMat &x) { SPMatindex n,m; n=x.nrows(); m=x.ncols(); s << (int)n << ' ' << (int)m << '\n'; matel *list=x.getlist(); while(list) { s << (int)list->row << ' ' << (int)list->col << ' ' << (typename LA_traits_io::IOtype)list->elem << '\n'; list=list->next; } s << "-1 -1\n"; return s; } template std::istream& operator>>(std::istream &s, SparseMat &x) { int i,j; int n,m; matel *l=NULL; s >> n >> m; x.resize(n,m); s >> i >> j; while(i>=0 && j>=0) { matel *ll = l; l= new matel; l->next= ll; l->row=i; l->col=j; typename LA_traits_io::IOtype tmp; s >> tmp; l->elem=tmp; s >> i >> j; } x.setlist(l); return s; } //destructor template SparseMat::~SparseMat() { unsort(); if(!count) return; if(--(*count)<=0) { deletelist(); delete count; } } //copy constructor (sort arrays are not going to be copied) template SparseMat::SparseMat(const SparseMat &rhs) { #ifdef debug if(! &rhs) laerror("SparseMat copy constructor with NULL argument"); #endif nn=rhs.nn; mm=rhs.mm; symmetric=rhs.symmetric; if(rhs.list&&!rhs.count) laerror("some inconsistency in SparseMat contructors or assignments"); list=rhs.list; if(list) {count=rhs.count; (*count)++;} else count=new int(1); //make the matrix defined, but empty and not shared colsorted=rowsorted=NULL; nonzero=0; } template const SparseMat SparseMat::transpose() const { if(list&&!count) laerror("some inconsistency in SparseMat transpose"); SparseMat result; result.nn=mm; result.mm=nn; result.symmetric=symmetric; if(result.symmetric) { result.list=list; if(list) {result.count=count; (*result.count)++;} else result.count=new int(1); //make the matrix defined, but empty and not shared } else //really transpose it { result.count=new int(1); result.list=NULL; matel *l =list; while(l) { result.add(l->col,l->row,l->elem); l=l->next; } } result.colsorted=result.rowsorted=NULL; result.nonzero=0; return result; } template inline const SparseMat commutator ( const SparseMat &x, const SparseMat &y, const bool trx=0, const bool tryy=0) { SparseMat r; 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); //saves a temporary and simplifies the whole sum return r; } template inline const SparseMat anticommutator ( const SparseMat &x, const SparseMat &y, const bool trx=0, const bool tryy=0) { SparseMat r; 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); //saves a temporary and simplifies the whole sum return r; } //add sparse to dense template NRMat & NRMat::operator+=(const SparseMat &rhs) { if((unsigned int)nn!=rhs.nrows()||(unsigned int)mm!=rhs.ncols()) laerror("incompatible matrices in +="); matel *l=rhs.getlist(); bool sym=rhs.issymmetric(); while(l) { #ifdef MATPTR v[l->row][l->col] +=l->elem; if(sym && l->row!=l->col) v[l->col][l->row] +=l->elem; #else v[l->row*mm+l->col] +=l->elem; if(sym && l->row!=l->col) v[l->col*mm+l->row] +=l->elem; #endif l=l->next; } return *this; } }//namespace #endif