/* LA: linear algebra C++ interface library Copyright (C) 2008-2019 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 _fourindex_included #define _fourindex_included #include #include #include #include #include #include #include #include #include #include #include "laerror.h" #include "vec.h" #include "smat.h" #include "mat.h" #include "nonclass.h" namespace LA { static unsigned int hcd0(unsigned int big,unsigned int small) { register unsigned int help; if(big==0) { if(small==0) laerror("bad arguments in hcd"); return small; } if(small==0) return big; if(small==1||big==1) return 1; if(small>big) {help=big; big=small; small=help;} do { help=small; small= big%small; big=help; } while(small != 0); return big; } static inline unsigned int lcm0(unsigned int i,unsigned int j) { return (i/hcd0(i,j)*j); } //element of a linked list, indices in a portable way, no bit shifts and endianity problems any more! //note: nn is never compared with individual indices, so indexing from 1 as well as from 0 is possible //it is actually not needed for the algorithms here, but may be useful for the //user of this class to keep this piece of information along with the data //when patient enough, make const_casts for piterators to have pbegin() const template struct indiv_index { I i; I j; I k; I l; } #ifdef __GNUC__ __attribute__((packed)) #endif ; template union packed_index { I packed[4]; struct indiv_index indiv; } #ifdef __GNUC__ __attribute__((packed)) #endif ; template struct matel4 { T elem; matel4 *next; union packed_index index; }; //should not be packed template struct matel4stored { T elem; union packed_index index; } #ifdef __GNUC__ __attribute__((packed)) #endif ; //later add symmetry of complex integrals typedef enum { undefined_symmetry=-1, nosymmetry=0, twoelectronrealmullikan=1, twoelectronrealmullikanAA=1, twoelectronrealdirac=2, T2ijab_aces=3, trdm2AA=3, antisymtwoelectronrealdirac=4, rdm2AA=4, T2IjAb_aces=5, trdm2AB=5, twoelectronrealmullikanAB=6, T2ijab_unitary=7, T2IjAb_unitary=8, antisymtwoelectronrealdiracAB=9,rdm2AB=9,twoelectroncomplexmullikan_without_conjugation=9, twoelectronrealmullikanreducedsymAA=10, twoelectronrealmullikanreducedsymAB=11, twoelectroncomplexmullikan=12, } fourindexsymtype; //only permutation-nonequivalent elements are stored // these should actually be static private members of the fourindex class, but leads to an ICE on gcc3.2 static const int fourindex_n_symmetrytypes=13; static const int fourindex_permnumbers[fourindex_n_symmetrytypes]={1,8,8,4,8,1,4,8,2,2,4,2,4}; static const int fourindex_permutations[fourindex_n_symmetrytypes][9][5]= { {{0,1,2,3,1}}, //nosymmetry {{0,1,2,3,1}, {1,0,2,3,1}, {0,1,3,2,1}, {1,0,3,2,1}, {2,3,0,1,1}, {3,2,0,1,1}, {2,3,1,0,1}, {3,2,1,0,1}}, //twoelectronrealmullikan {{0,1,2,3,1},{2,1,0,3,1},{0,3,2,1,1},{2,3,0,1,1},{1,0,3,2,1},{3,0,1,2,1},{1,2,3,0,1},{3,2,1,0,1}}, //twoelectronrealdirac {{0,1,2,3,1},{1,0,2,3,-1},{0,1,3,2,-1},{1,0,3,2,1}}, //T2ijab_aces {{0,1,2,3,1},{1,0,2,3,-1},{0,1,3,2,-1},{1,0,3,2,1},{2,3,0,1,1},{3,2,0,1,-1},{2,3,1,0,-1},{3,2,1,0,1}}, //antisymtwoelectronrealdirac {{0,1,2,3,1}}, //T2IjAb_aces is like nosymmetry but different index ranges {{0,1,2,3,1},{1,0,2,3,1},{0,1,3,2,1},{1,0,3,2,1}}, //twoelectronrealmullikanAB {{0,1,2,3,1},{1,0,2,3,-1},{0,1,3,2,-1},{1,0,3,2,1},{2,3,0,1,-1},{3,2,0,1,1},{2,3,1,0,1},{3,2,1,0,-1}}, //T2ijab_unitary (antiherm exponent) {{0,1,2,3,1},{2,3,0,1,-1}}, //T2IjAb_unitary (antihermitian exponent) {{0,1,2,3,1},{2,3,0,1,1}}, //antisymtwoelectronrealdiracAB {{0,1,2,3,1},{2,3,0,1,1},{1,0,3,2,1},{3,2,1,0,1}}, //twoelectronrealmullikanreducedsymAA (e.g. unitary downfolded CC eff. integrals) {{0,1,2,3,1},{1,0,3,2,1}}, //twoelectronrealmullikanreducedsymAB (e.g. unitary downfolded CC eff. integrals) {{0,1,2,3,1},{2,3,0,1,1},{1,0,3,2,2},{3,2,1,0,2}}, //twoelectroncomplexmullikan ... 2 means complex conjugation }; template void symmetry_faktor(const fourindexsymtype symmetry,const union packed_index &index, T &elem) { switch(symmetry) { case twoelectronrealmullikanreducedsymAA: if(index.indiv.i==index.indiv.j && index.indiv.k==index.indiv.l) elem*=.5; if(index.indiv.i==index.indiv.k && index.indiv.j==index.indiv.l) elem*=.5; break; case twoelectronrealmullikanreducedsymAB: if(index.indiv.i==index.indiv.j && index.indiv.k==index.indiv.l) elem*=.5; break; case antisymtwoelectronrealdirac: case antisymtwoelectronrealdiracAB: laerror("not implemented"); case twoelectronrealmullikan: if(index.indiv.i==index.indiv.j) elem*=.5; if(index.indiv.k==index.indiv.l) elem*=.5; if(index.indiv.i==index.indiv.k && index.indiv.j==index.indiv.l || index.indiv.i==index.indiv.l && index.indiv.j==index.indiv.k ) elem*=.5; break; case twoelectronrealdirac: if(index.indiv.i==index.indiv.k) elem*=.5; if(index.indiv.j==index.indiv.l) elem*=.5; if(index.indiv.i==index.indiv.j && index.indiv.k==index.indiv.l || index.indiv.k==index.indiv.j && index.indiv.i==index.indiv.l) elem*=.5; break; case T2ijab_aces: break; //result will automatically vanish due to generated antisymmetry; i!=a from principle case T2IjAb_aces: break; //no actual symmetry case nosymmetry: break; default: laerror("illegal symmetry or symmetry-redundant scaling factor not implemented"); } } template class fourindex { protected: fourindexsymtype symmetry; I nn; int *count; matel4 *list; I terminator; bool doscaling; private: void deletelist(); void copylist(const matel4 *l); public: //iterator class iterator { private: matel4 *p; public: iterator() {}; ~iterator() {}; iterator(matel4 *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++() {p=p->next; return *this;} iterator operator++(int) {iterator q(p); p=p->next; return q;} matel4 & operator*() {return *p;} matel4 * operator->() {return p;} const matel4 * operator->() const {return p;} const matel4 & operator*() const {return *p;} }; void setterminator(const I terminator0) {terminator=terminator0;} I getterminator() const {return terminator;} void setscaling(const bool doscaling0) {doscaling=doscaling0;} iterator begin() const {return list;} iterator end() const {return NULL;} //permiterator ... iterates also over all permutations, with a possibly scaled matrix element for redundant results of the permutations //it is assumed the original fourindex is the symmetry-reduced petite list, then the results of piterator contributions can be accumulated //has to take into account the symmetry type of the fourindex class piterator { private: fourindexsymtype symmetry; matel4 *p; matel4 my; int permindex; bool doscaling; void setup(void) //make a copy of *p to my with scaled element and anti/permuted indices { if(symmetry==undefined_symmetry) laerror("fourindex symmetry has not been set"); if(!p) {permindex=0; memset(&my,0,sizeof(my)); return;} for(int i=0; i<4; ++i) my.index.packed[i] = p->index.packed[fourindex_permutations[symmetry][permindex][i]]; my.elem = p->elem * fourindex_permutations[symmetry][permindex][4]; //now treat the redundancy due to possibly equal indices by a scaling factor //if the processing of individual term becomes very costly, an alternative would be to screen permutations yielding identical result if(doscaling) symmetry_faktor(symmetry, p->index, my.elem); }; public: piterator() {}; piterator(matel4 *pp): symmetry(nosymmetry),p(pp),permindex(0),doscaling(true){}; ~piterator() {}; piterator(const fourindex &x): symmetry(x.symmetry),p(x.list),permindex(0),doscaling(x.doscaling) {setup();}; piterator& operator++() {if(++permindex>=fourindex_permnumbers[symmetry]) {permindex=0; p=p->next;} setup(); return *this;} const matel4 & operator*() const {return my;} const matel4 * operator->() const {return &my;} piterator operator++(int) {laerror("postincrement not possible on permute-iterator"); return *this;} bool operator!=(const piterator &rhs) const {return p!=rhs.p;} //should only be used for comparison with pend() bool end(void) {return !p;} bool notend(void) {return p;} }; piterator pbegin() const {return piterator(*this);} piterator pend() const {return piterator(NULL);}//inefficient, use end() or notend() instead //constructors etc. inline fourindex() :symmetry(undefined_symmetry),nn(0),count(NULL),list(NULL),terminator(-1),doscaling(true) {}; inline fourindex(const I n, const fourindexsymtype symmetry0=undefined_symmetry, const I terminator0= -1, const bool doscaling0=true) :nn(n),count(new int(1)),list(NULL),symmetry(symmetry0),terminator(terminator0),doscaling(doscaling0) {}; fourindex(const fourindex &rhs); //copy constructor inline int getcount() const {return count?*count:0;} fourindex & operator=(const fourindex &rhs); fourindex & operator+=(const fourindex &rhs); void setsymmetry(fourindexsymtype s) {symmetry=s;} fourindexsymtype getsymmetry() const {return symmetry;} fourindex & join(fourindex &rhs); //more efficient +=, rhs will be emptied inline ~fourindex(); inline matel4 *getlist() const {return list;} inline I size() const {return nn;} void resize(const I n); void dealloc(void) {resize(0);} void copyonwrite(); unsigned long length() const; inline void add(const I i, const I j, const I k, const I l, const T elem) {matel4 *ltmp= new matel4; ltmp->next=list; list=ltmp; list->index.indiv.i=i;list->index.indiv.j=j;list->index.indiv.k=k;list->index.indiv.l=l; list->elem=elem;} inline void add(const union packed_index &index , const T elem) {matel4 *ltmp= new matel4; ltmp->next=list; list=ltmp; list->index=index; list->elem=elem;} inline void add(const I (&index)[4], const T elem) {matel4 *ltmp= new matel4; ltmp->next=list; list=ltmp; memcpy(&list->index.packed, &index, sizeof(union packed_index)); list->elem=elem;} inline void add(const matel4 &rhs) {matel4 *ltmp= new matel4; ltmp->next=list; list=ltmp; memcpy(&list->index.packed, &rhs.index, sizeof(union packed_index)); list->elem=rhs.elem;} inline void add(const matel4stored &rhs) {matel4 *ltmp= new matel4; ltmp->next=list; list=ltmp; memcpy(&list->index.packed, &rhs.index, sizeof(union packed_index)); list->elem=rhs.elem;} unsigned long put(int fd,bool withattr=true) const; unsigned long get(int fd,bool withattr=true); void fscanf(FILE *f); //C-style formatted IO void fprintf(FILE *f, char *format) const; void permuteme(const NRPerm &p, const bool inverse=false)// index permutation { if(p.size()!=nn) laerror("inconsistent dimension in fourindex::permuteme"); copyonwrite(); matel4 *l=list; NRPerm pp; if(inverse) pp=p.inverse(); else pp=p; while(l) { l->index.indiv.i = (I) pp[(int)l->index.indiv.i]; l->index.indiv.j = (I) pp[(int)l->index.indiv.j]; l->index.indiv.k = (I) pp[(int)l->index.indiv.k]; l->index.indiv.l = (I) pp[(int)l->index.indiv.l]; l=l->next; } } void swap(fourindex &rhs) //more efficient swap than via tmp and constructors and operator= { I tmpnn=nn; nn=rhs.nn; rhs.nn=tmpnn; I tmpterminator=terminator; terminator=rhs.terminator; rhs.terminator=tmpterminator; int *tmpcount=count; count=rhs.count; rhs.count=tmpcount; matel4 *tmplist=list; list=rhs.list; rhs.list=tmplist; bool tmpdoscaling=doscaling; doscaling=rhs.doscaling; rhs.doscaling=tmpdoscaling; fourindexsymtype tmpsymmetry=symmetry; symmetry=rhs.symmetry; rhs.symmetry=tmpsymmetry; } }; //and a class for accessing a disc-stored fourindex, taking care of permutational index symmetry //O_DIRECT approach to avoid filling of the buffer cache when reading //large file sequentially is implemented: //the user of the class must open the file with O_DIRECT //NOTE!!! it will not work on linux 2.4, where O_DIRECT requires filesize to be a multiple of the block; 2.6 kernel is necessary!!! //it used to work on older 2.6 kernels, but now does not work again since there is restriction to 512-byte alignment template class fourindex_ext { private: //at the moment for simplicity forbid some operations, otherwise reference counting on the buffer has to be done fourindex_ext(); fourindex_ext(const fourindex_ext &rhs); fourindex_ext & operator=(const fourindex_ext &rhs); protected: matel4stored *buffer0; matel4stored *buffer; matel4stored *current; int fd; unsigned int bufsize; unsigned int nread; fourindexsymtype symmetry; I nn; bool doscaling; I terminator; //methods void tryread() const { const_cast *>(this)->current=NULL; errno=0; //printf("read %d %llx %d\n",fd,buffer,bufsize*sizeof(matel4stored)); ssize_t r=read(fd,buffer,bufsize*sizeof(matel4stored)); if(r<0) {perror("read error"); laerror("read error in fourindex_ext (might be bug of O_DIRECT)");} if(r%sizeof(matel4stored)) laerror("read inconsistency in fourindex_ext"); const_cast *>(this)->nread = r/sizeof(matel4stored); if(nread) const_cast *>(this)->current=buffer; } void next() const { if(current) { if( (unsigned int) (++ const_cast *>(this)->current - buffer) >=nread) tryread(); } } bool eof() const {return !current;}; public: void setterminator(const I terminator0) {terminator=terminator0;} I getterminator() const {return terminator;} void setscaling(const bool doscaling0) {doscaling=doscaling0;} void resize(I n) {nn=n;} fourindex_ext(const int file, const fourindexsymtype s=undefined_symmetry, const I n=0, const unsigned int b=1024, const I terminator0= -1, const bool doscaling0= true) :current(NULL),fd(file),nread(0),symmetry(s),nn(n) { struct statfs sfs; struct stat64 sf; if(fstat64(fd,&sf)) {perror("cannot fstat");laerror("I/O error");} if(fstatfs(fd,&sfs)) {perror("cannot fstatfs");laerror("I/O error");} const unsigned int pagesize=getpagesize(); //make bufsize*sizeof(matel4stored) a multiple of fs block size and page size bufsize=b*sizeof(matel4stored); bufsize=lcm0(bufsize,pagesize); bufsize=lcm0(bufsize,sfs.f_bsize); bufsize=lcm0(bufsize,sf.st_blksize); buffer0 = new matel4stored[(bufsize+pagesize)/sizeof(matel4stored)+1]; //ensure alignment at page boundary unsigned char *buf= (unsigned char *) buffer0; buf= buf + pagesize - ((uint64_t)buf % pagesize); buffer = (matel4stored *) buf; mlock(buf,bufsize); //ignore error when not root, hope we will not be paged out anyway bufsize /= sizeof(matel4stored); terminator=terminator0; doscaling=doscaling0; } ~fourindex_ext() {if(buffer0) delete[] buffer0;} void setsymmetry(fourindexsymtype s) {symmetry=s;}; fourindexsymtype getsymmetry() const {return symmetry;} void rewind() const {if(0!=lseek64(fd,0L,SEEK_SET)) {perror("seek error"); laerror("cannot seek in fourindex_ext");} }; //file output void put(const matel4stored x) { if(!current) current=buffer; *current++ = x; if(current-buffer >= bufsize ) flush(); } void put(I i, I j, I k, I l, const T &elem) { if(!current) current=buffer; current->index.indiv.i=i; current->index.indiv.j=j; current->index.indiv.k=k; current->index.indiv.l=l; current->elem = elem; ++current; if(current-buffer >= bufsize ) flush(); } void flush() { if(current) { ssize_t r=write(fd,buffer,(current-buffer)*sizeof(matel4stored)); if(r!=(current-buffer)*sizeof(matel4stored)) laerror("write error in fourindex_ext"); } current=NULL; } inline I size() const {return nn;} //iterator and permute-iterator are both implemented as poiters to the original class, using private functions of this class //this is possible, since one instance of this class can have only one active iterator at a time //iterator class iterator { private: const fourindex_ext *base; public: iterator() {}; iterator(const fourindex_ext *p): base(p) {}; ~iterator() {}; bool operator!=(const iterator &rhs) const {return base!=rhs.base;} //should only be used for comparison with end() iterator &operator++() {if(base) base->next(); if(base->eof()) base=NULL; return *this;} iterator operator++(int) {laerror("postincrement not possible"); return *this;} const matel4stored * operator->() const {return base->current;} const matel4stored & operator*() const {return *base->current;} bool notNULL() const {return base;} }; iterator begin() const {rewind(); tryread(); if(!eof()) return this; else return NULL;} iterator end() const {return iterator(NULL);} //piterator ... iterate over all allowed permutations; conveniently expressed via the basic iterator which does the block-buffering class piterator { private: fourindex_ext *base; matel4 my; int permindex; typename fourindex_ext::iterator it; //private methods void setup(void) //make a copy of *it to my with scaled element and anti/permuted indices { if(base->symmetry==undefined_symmetry) laerror("fourindex symmetry has not been set"); if(!it.notNULL()) {permindex=0; memset(&my,0,sizeof(my)); return;} //we rely that end() is NULL for(int i=0; i<4; ++i) my.index.packed[i] = it->index.packed[fourindex_permutations[base->symmetry][permindex][i]]; my.elem = it->elem * fourindex_permutations[base->symmetry][permindex][4]; //redundancy due to possibly equal indices //if the processing of individual term becomes very costly, an alternative would be to screen permutations yielding identical result if(base->doscaling) symmetry_faktor(base->symmetry, it->index, my.elem); }; public: piterator() {}; piterator(fourindex_ext *p): base(p),permindex(0) {if(p) {it=p->begin(); setup();}}; piterator(fourindex_ext &x): base(&x),permindex(0) {it= x.begin(); setup();}; ~piterator() {}; bool operator!=(const piterator &rhs) const {return base!=rhs.base;} //should only be used for comparison with end() piterator &operator++() {if(++permindex>=fourindex_permnumbers[base->symmetry]) {permindex=0; ++it;} if(it.notNULL()) setup(); else base=NULL; return *this;} piterator operator++(int) {laerror("postincrement not possible"); return *this;} const matel4 * operator->() const {return &my;} const matel4 & operator*() const {return my;} bool end(void) {return !base;} bool notend(void) {return base;} }; piterator pbegin() {return piterator(*this);} piterator pend() const {return piterator(NULL);} //inefficient, use end() or notend() instead }; /////////////////////////////implementations/////////////////////////////////// template unsigned long fourindex::put(int fd, bool withattr) const { unsigned long n=0; matel4 *l=list; matel4stored buf; if(withattr) { union {fourindexsymtype sym; I n; T padding;} u; u.sym=symmetry; if(sizeof(u)!=write(fd,&u,sizeof(u))) laerror("write error in fourindex::put"); u.n=nn; if(sizeof(u)!=write(fd,&u,sizeof(u))) laerror("write error in fourindex::put"); } while(l) { ++n; buf.elem= l->elem; buf.index= l->index; if(sizeof(buf)!=write(fd,&buf,sizeof(buf))) laerror("write error in fourindex::put"); l=l->next; } return n; } template unsigned long fourindex::get(int fd,bool withattr) { unsigned long n=0; matel4stored buf; if(withattr) { union {fourindexsymtype sym; I n; T padding;} u; if(sizeof(u)!=read(fd,&u,sizeof(u))) laerror("read inconsistency in fourindex::put"); symmetry=u.sym; if(sizeof(u)!=read(fd,&u,sizeof(u))) laerror("read inconsistency in fourindex::put"); nn=u.n; } while(sizeof(buf)==read(fd,&buf,sizeof(buf))) {++n; add(buf.index,buf.elem);} return n; } //destructor template fourindex::~fourindex() { if(!count) return; if(--(*count)<=0) { deletelist(); delete count; } } //copy constructor (sort arrays are not going to be copied) template fourindex::fourindex(const fourindex &rhs) { #ifdef debug if(! &rhs) laerror("fourindex copy constructor with NULL argument"); #endif nn=rhs.nn; if(rhs.list&&!rhs.count) laerror("some inconsistency in fourindex 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 terminator=rhs.terminator; doscaling=rhs.doscaling; } //assignment operator template fourindex & fourindex::operator=(const fourindex &rhs) { if (this != &rhs) { if(count) if(--(*count) ==0) {deletelist(); delete count;} // old stuff obsolete list=rhs.list; nn=rhs.nn; terminator=rhs.terminator; doscaling=rhs.doscaling; if(list) count=rhs.count; else count= new int(0); //make the matrix defined, but empty and not shared, count will be incremented below if(count) (*count)++; } return *this; } template fourindex & fourindex::operator+=(const fourindex &rhs) { if(nn!=rhs.nn) laerror("incompatible dimensions for +="); if(!count) {count=new int; *count=1; list=NULL;} else copyonwrite(); register matel4 *l=rhs.list; while(l) { add( l->index,l->elem); l=l->next; } return *this; } template fourindex & fourindex::join(fourindex &rhs) { if(nn!=rhs.nn) laerror("incompatible dimensions for join"); if(*rhs.count!=1) laerror("shared rhs in join()"); if(!count) {count=new int; *count=1; list=NULL;} else copyonwrite(); matel4 **last=&list; while(*last) last= &((*last)->next); *last=rhs.list; rhs.list=NULL; return *this; } template void fourindex::resize(const I n) { if(n<=0 ) laerror("illegal fourindex dimension"); if(count) { if(*count > 1) {(*count)--; count=NULL; list=NULL;} //detach from previous else if(*count==1) deletelist(); } nn=n; count=new int(1); //empty but defined matrix list=NULL; } template void fourindex::deletelist() { if(*count >1) laerror("trying to delete shared list"); matel4 *l=list; while(l) { matel4 *ltmp=l; l=l->next; delete ltmp; } list=NULL; delete count; count=NULL; } template void fourindex::copylist(const matel4 *l) { list=NULL; while(l) { add(l->index,l->elem); l=l->next; } } template void fourindex::copyonwrite() { if(!count) laerror("probably an assignment to undefined fourindex"); if(*count > 1) { (*count)--; count = new int; *count=1; if(!list) laerror("empty list with count>1"); copylist(list); } } template unsigned long fourindex::length() const { unsigned long n=0; matel4 *l=list; while(l) { ++n; l=l->next; } return n; } template std::ostream& operator<<(std::ostream &s, const fourindex_ext &x) { int n; n=x.size(); s << n << '\n'; typename fourindex_ext::iterator it=x.begin(); while(it!=x.end()) { s << (typename LA_traits_io::IOtype)it->index.indiv.i << ' ' << (typename LA_traits_io::IOtype)it->index.indiv.j<< ' ' <<(typename LA_traits_io::IOtype)it->index.indiv.k << ' ' << (typename LA_traits_io::IOtype)it->index.indiv.l << ' ' << (typename LA_traits_io::IOtype) it->elem << '\n'; ++it; } s << (typename LA_traits_io::IOtype) x.getterminator() << ' ' << (typename LA_traits_io::IOtype)x.getterminator() << ' ' <<(typename LA_traits_io::IOtype)x.getterminator() << ' ' << (typename LA_traits_io::IOtype)x.getterminator() << ' ' << (typename LA_traits_io::IOtype) 0 << '\n'; return s; } template std::ostream& operator<<(std::ostream &s, const fourindex &x) { int n; n=x.size(); s << n << '\n'; typename fourindex::iterator it=x.begin(),end=x.end(); while(it!=end) { s << (typename LA_traits_io::IOtype)it->index.indiv.i << ' ' << (typename LA_traits_io::IOtype)it->index.indiv.j<< ' ' <<(typename LA_traits_io::IOtype)it->index.indiv.k << ' ' << (typename LA_traits_io::IOtype)it->index.indiv.l << ' ' << (typename LA_traits_io::IOtype) it->elem << '\n'; ++it; } s << (typename LA_traits_io::IOtype)x.getterminator() << ' ' << (typename LA_traits_io::IOtype)x.getterminator() << ' ' <<(typename LA_traits_io::IOtype)x.getterminator() << ' ' << (typename LA_traits_io::IOtype)x.getterminator() << ' ' << (typename LA_traits_io::IOtype) 0 << '\n'; return s; } template std::istream& operator>>(std::istream &s, fourindex &x) { typename LA_traits_io::IOtype i,j,k,l; typename LA_traits_io::IOtype elem; int n; s >> n ; x.resize(n); s >> i >> j >>k >>l; while(i!= (typename LA_traits_io::IOtype)x.getterminator() && j!= (typename LA_traits_io::IOtype)x.getterminator() && k != (typename LA_traits_io::IOtype)x.getterminator() && l!= (typename LA_traits_io::IOtype)x.getterminator()) { s>>elem; x.add((I)i,(I)j,(I)k,(I)l,(T)elem); s >> i >> j >>k >>l; } return s; } template std::istream& operator>>(std::istream &s, fourindex_ext &x) { int n; s >> n; x.resize(n); typename LA_traits_io::IOtype i,j,k,l; typename LA_traits_io::IOtype elem; s >> i >> j >>k >>l; while(i!= (typename LA_traits_io::IOtype)x.getterminator() && j!= (typename LA_traits_io::IOtype)x.getterminator() && k != (typename LA_traits_io::IOtype)x.getterminator() && l!= (typename LA_traits_io::IOtype)x.getterminator()) { s>>elem; x.put((I)i,(I)j,(I)k,(I)l,(T)elem); s >> i >> j >>k >>l; } x.flush(); return s; } /////////////////////densely stored fourindex/////////////////////////////////// //not all symmetry cases implemented yet, but a general template declaration used //we use a general template forward declaration, but then it has to be done differently for (almost) each case //by means of partial template specialization //note - loops for the twoelectronrealmullikan integral to be unique and in canonical order // i=1..n, j=1..i, k=1..i, l=1..(i==k?j:k) //general template declaration template class fourindex_dense; //traits class template struct LA_traits > { typedef T elementtype; typedef typename LA_traits::normtype normtype; }; //make it as a derived class in order to be able to use it in a base class context - "supermatrix" operations //note - loops for the twoelectronrealmullikanAB integral to be unique and in canonical order // i=1..n, j=1..i, k=1..n, l=1..k template class fourindex_dense : public NRMat { public: fourindex_dense(): NRMat() {}; explicit fourindex_dense(const int n): NRMat(n*(n+1)/2,n*(n+1)/2) {}; fourindex_dense(const NRMat &rhs): NRMat(rhs) {}; //be able to convert the parent class transparently to this fourindex_dense(const T &a, const int n): NRMat(a,n*(n+1)/2,n*(n+1)/2) {}; fourindex_dense(const T *a, const int n): NRMat(a,n*(n+1)/2,n*(n+1)/2) {}; //and also construct it from sparse and externally stored fourindex classes //it seems not possible to nest template just for the two constructors fourindex_dense(const fourindex &rhs); fourindex_dense(const fourindex_ext &rhs); T& operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l); const T& operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) const; void resize(const int n) {(*this).NRMat::resize(n*(n+1)/2,n*(n+1)/2);}; void putext(int f, T thr=1e-15); int nbas() const {return (int)std::sqrt(2*(*this).nrows());}; }; template void fourindex_dense::putext(int f, T thr) { T y; for(int i=1; i<=nbas(); ++i) for(int j=1; j<=i; ++j) for(int k=1; k<=nbas(); ++k) for(int l=1; l<=k; ++l) { y=(*this)(i,j,k,l); if(abs(y)>thr) { matel4stored x; x.elem= y; x.index.indiv.i=i; x.index.indiv.j=j; x.index.indiv.k=k; x.index.indiv.l=l; if(sizeof(matel4stored) != write(f,&x,sizeof(matel4stored)) ) laerror("write error in putext"); } } } template fourindex_dense::fourindex_dense(const fourindex &rhs) : NRMat((T)0,rhs.size()*(rhs.size()+1)/2,rhs.size()*(rhs.size()+1)/2) { if(rhs.getsymmetry() != twoelectronrealmullikanAB ) laerror("fourindex_dense symmetry mismatch"); typename fourindex::iterator p; #ifdef DEBUG unsigned int IJ = SMat_index_1(p->index.indiv.i,p->index.indiv.j); unsigned int KL = SMat_index_1(p->index.indiv.k,p->index.indiv.l); if (IJ<0 || IJ>=(unsigned int)NRMat::nn || KL<0 || KL>=(unsigned int)NRMat::mm) laerror("fourindex_dense index out of range in constructor"); #endif for(p=rhs.begin(); p!= rhs.end(); ++p) (*this)(p->index.indiv.i,p->index.indiv.j,p->index.indiv.k,p->index.indiv.l) = p->elem; } template fourindex_dense::fourindex_dense(const fourindex_ext &rhs) : NRMat((T)0,rhs.size()*(rhs.size()+1)/2,rhs.size()*(rhs.size()+1)/2) { if(rhs.getsymmetry() != twoelectronrealmullikanAB ) laerror("fourindex_dense symmetry mismatch"); typename fourindex_ext::iterator p; for(p=rhs.begin(); p!= rhs.end(); ++p) { #ifdef DEBUG unsigned int IJ = SMat_index_1(p->index.indiv.i,p->index.indiv.j); unsigned int KL = SMat_index_1(p->index.indiv.k,p->index.indiv.l); if (IJ<0 || IJ>=(unsigned int)NRMat::nn || KL<0 || KL>=(unsigned int)NRMat::mm) laerror("fourindex_dense index out of range in constructor"); #endif (*this)(p->index.indiv.i,p->index.indiv.j ,p->index.indiv.k,p->index.indiv.l) = p->elem; } } template T& fourindex_dense::operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) { int I = SMat_index_1(i,j); int J = SMat_index_1(k,l); //I,J act as indices of a NRmat #ifdef DEBUG if (*NRMat::count != 1) laerror("lval (i,j,k,l) with count > 1 in fourindex_dense"); if (I<0 || I>=NRMat::nn || J<0 || J>=NRMat::mm) laerror("fourindex_dense index out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif return NRMat::operator()(I,J); } template const T& fourindex_dense::operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) const { int I = SMat_index_1(i,j); int J = SMat_index_1(k,l); //I,J act as indices of a NRSmat #ifdef DEBUG if (I<0 || I>=NRMat::nn || J<0 || J>=NRMat::mm) laerror("fourindex_dense index out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif return NRMat::operator()(I,J); } //////////////////// template class fourindex_dense : public NRSMat { public: fourindex_dense(): NRSMat() {}; explicit fourindex_dense(const int n): NRSMat(n*(n+1)/2) {}; fourindex_dense(const NRSMat &rhs): NRSMat(rhs) {}; //be able to convert the parent class transparently to this fourindex_dense(const T &a, const int n): NRSMat(a,n*(n+1)/2) {}; fourindex_dense(const T *a, const int n): NRSMat(a,n*(n+1)/2) {}; //and also construct it from sparse and externally stored fourindex classes //it seems not possible to nest template just for the two constructors fourindex_dense(const fourindex &rhs); fourindex_dense(const fourindex_ext &rhs); T& operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l); const T& operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) const; void resize(const int n) {(*this).NRSMat::resize(n*(n+1)/2);}; void putext(int f, T thr=1e-15); int nbas() const {return (int)std::sqrt(2*(*this).nrows());}; }; template void fourindex_dense::putext(int f, T thr) { T y; for(int i=1; i<=nbas(); ++i) for(int j=1; j<=i; ++j) for(int k=1; k<=i; ++k) for(int l=1; l<=(i==k?j:k); ++l) { y=(*this)(i,j,k,l); if(abs(y) > thr) { matel4stored x; x.elem= y; x.index.indiv.i=i; x.index.indiv.j=j; x.index.indiv.k=k; x.index.indiv.l=l; if(sizeof(matel4stored) != write(f,&x,sizeof(matel4stored)) ) laerror("write error in putext"); } } } template fourindex_dense::fourindex_dense(const fourindex &rhs) : NRSMat((T)0,rhs.size()*(rhs.size()+1)/2) { if(rhs.getsymmetry() != twoelectronrealmullikan ) laerror("fourindex_dense symmetry mismatch"); typename fourindex::iterator p; #ifdef DEBUG unsigned int IJ = SMat_index_1(p->index.indiv.i,p->index.indiv.j); unsigned int KL = SMat_index_1(p->index.indiv.k,p->index.indiv.l); if (IJ<0 || IJ>=(unsigned int)NRSMat::nn || KL<0 || KL>=(unsigned int)NRSMat::nn) laerror("fourindex_dense index out of range in constructor"); #endif for(p=rhs.begin(); p!= rhs.end(); ++p) (*this)(p->index.indiv.i,p->index.indiv.j,p->index.indiv.k,p->index.indiv.l) = p->elem; } template fourindex_dense::fourindex_dense(const fourindex_ext &rhs) : NRSMat((T)0,rhs.size()*(rhs.size()+1)/2) { if(rhs.getsymmetry() != twoelectronrealmullikan ) laerror("fourindex_dense symmetry mismatch"); typename fourindex_ext::iterator p; for(p=rhs.begin(); p!= rhs.end(); ++p) { #ifdef DEBUG unsigned int IJ = SMat_index_1(p->index.indiv.i,p->index.indiv.j); unsigned int KL = SMat_index_1(p->index.indiv.k,p->index.indiv.l); if (IJ<0 || IJ>=(unsigned int)NRSMat::nn || KL<0 || KL>=(unsigned int)NRSMat::nn) laerror("fourindex_dense index out of range in constructor"); #endif (*this)(p->index.indiv.i,p->index.indiv.j ,p->index.indiv.k,p->index.indiv.l) = p->elem; } } template T& fourindex_dense::operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) { int I = SMat_index_1(i,j); int J = SMat_index_1(k,l); //I,J act as indices of a NRSmat #ifdef DEBUG if (*NRSMat::count != 1) laerror("lval (i,j,k,l) with count > 1 in fourindex_dense"); if (I<0 || I>=NRSMat::nn || J<0 || J>=NRSMat::nn) laerror("fourindex_dense index out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif return NRSMat::v[SMat_index(I,J)]; } template const T& fourindex_dense::operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) const { int I = SMat_index_1(i,j); int J = SMat_index_1(k,l); //I,J act as indices of a NRSmat #ifdef DEBUG if (I<0 || I>=NRSMat::nn || J<0 || J>=NRSMat::nn) laerror("fourindex_dense index out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif return NRSMat::v[SMat_index(I,J)]; } template class fourindex_dense : public NRMat { protected: unsigned int nn; friend class explicit_t2; public: fourindex_dense(): NRMat() {nn=0;}; void resize(const int nnn) {nn=nnn; (*this).NRMat::resize(nn*nn,nn*nn);}; explicit fourindex_dense(const int nnn): NRMat(nnn*nnn,nnn*nnn) {nn=nnn;}; inline T& operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) { #ifdef DEBUG if(i<1||i>nn ||j<1||j>nn|| a<1||a>nn||b<1||b>nn) laerror("nosymmetry fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif return (*this).NRMat::operator() ((j-1)*nn+i-1,(b-1)*nn+a-1); } inline const T& operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) const { #ifdef DEBUG if(i<1||i>nn ||j<1||j>nn|| a<1||a>nn||b<1||b>nn) laerror("nosymmetry fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif return (*this).NRMat::operator() ((j-1)*nn+i-1,(b-1)*nn+a-1); } void print(std::ostream &out) const { unsigned int i,j,a,b; for(i=1; i<=nn; ++i) for(j=1; j<=nn; ++j) for(a=1; a<=nn; ++a) for(b=1; b<=nn; ++b) out << i<<" "< class fourindex_dense : public NRMat { protected: unsigned int noca,nocb,nvra,nvrb; friend class explicit_t2; public: fourindex_dense(): NRMat() {noca=nocb=nvra=nvrb=0;}; void resize(const int nocca, const int noccb, const int nvrta, const int nvrtb) {noca=nocca; nocb=noccb; nvra=nvrta; nvrb=nvrtb; (*this).NRMat::resize(nocca*noccb,nvrta*nvrtb);}; explicit fourindex_dense(const int nocca, const int noccb, const int nvrta, const int nvrtb): NRMat(nocca*noccb,nvrta*nvrtb) {noca=nocca; nocb=noccb; nvra=nvrta; nvrb=nvrtb;}; //here i,a are alpha j,b beta inline T& operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) { #ifdef DEBUG if(i<1||i>noca ||j<1||j>nocb|| a<1||a>nvra||b<1||b>nvrb) laerror("T2IjAb_aces fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif return (*this).NRMat::operator() ((j-1)*noca+i-1,(b-1)*nvra+a-1); } inline const T& operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) const { #ifdef DEBUG if(i<1||i>noca ||j<1||j>nocb|| a<1||a>nvra||b<1||b>nvrb) laerror("T2IjAb_aces fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif return (*this).NRMat::operator() ((j-1)*noca+i-1,(b-1)*nvra+a-1); } void print(std::ostream &out) const { unsigned int i,j,a,b; for(i=1; i<=noca; ++i) for(j=1; j<=nocb; ++j) for(a=1; a<=nvra; ++a) for(b=1; b<=nvrb; ++b) out << i<<" "< class fourindex_dense : public NRSMat { protected: unsigned int nbas; friend class explicit_t2; public: fourindex_dense(): NRSMat() {nbas=0;}; void resize(const int n) {nbas=n; (*this).NRSMat::resize(nbas*nbas);}; explicit fourindex_dense(const int n): NRSMat(n*n) {nbas=n;}; //here i,a are alpha j,b beta inline T& operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) { #ifdef DEBUG if(i<1||i>nbas ||j<1||j>nbas|| a<1||a>nbas||b<1||b>nbas) laerror("antisymtwoelectronrealdiracAB fourindex out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif return (*this).NRSMat::operator() ((j-1)*nbas+i-1,(b-1)*nbas+a-1); } inline const T& operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) const { #ifdef DEBUG if(i<1||i>nbas ||j<1||j>nbas|| a<1||a>nbas||b<1||b>nbas) laerror("antisymtwoelectronrealdiracAB fourindex out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif return (*this).NRSMat::operator() ((j-1)*nbas+i-1,(b-1)*nbas+a-1); } void print(std::ostream &out) const { unsigned int i,j,a,b; for(i=1; i<=nbas; ++i) for(j=1; j<=nbas; ++j) for(a=1; a<=i; ++a) for(b=1; b<= (a class fourindex_dense : public NRMat { protected: unsigned int nocc,nvrt,ntri; friend class explicit_t2; public: fourindex_dense(): NRMat() {nocc=nvrt=ntri=0;}; explicit fourindex_dense(const int noc, const int nvr): NRMat(noc*(noc-1)/2,nvr*(nvr-1)/2) {nocc=noc; nvrt=nvr; ntri=nvr*(nvr-1)/2;}; void resize(const int noc, const int nvr) {(*this).NRMat::resize(noc*(noc-1)/2,nvr*(nvr-1)/2); nocc=noc; nvrt=nvr; ntri=nvr*(nvr-1)/2;}; //we cannot return reference due to the possible sign change //stored values are for i>j a>b inline T operator() (unsigned int i, unsigned int j, unsigned int a, unsigned int b) const { #ifdef DEBUG if(i<1||i>nocc ||j<1||j>nocc|| a<1||a>nvrt||b<1||b>nvrt) laerror("T2ijab_aces fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); #endif int minus=0; if(i==j||a==b) return (T)0; //important, needed if(i::operator() ((i-2)*(i-1)/2+j-1, (a-2)*(a-1)/2+b-1); return (minus&1)? -val:val; } inline void set(unsigned int i, unsigned int j, unsigned int a, unsigned int b, T elem) { #ifdef DEBUG if(i<1||i>nocc ||j<1||j>nocc|| a<1||a>nvrt||b<1||b>nvrt) laerror("T2ijab_aces fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); if(i==j||a==b && elem) laerror("antisymmetry violation in fourindex_dense"); #endif int minus=0; if(i::operator() ((i-2)*(i-1)/2+j-1, (a-2)*(a-1)/2+b-1) = minus? -elem : elem; } inline void add(unsigned int i, unsigned int j, unsigned int a, unsigned int b, T elem) { #ifdef DEBUG if(i<1||i>nocc ||j<1||j>nocc|| a<1||a>nvrt||b<1||b>nvrt) laerror("T2ijab_aces fourindex out of range"); if (!NRMat::v) laerror("access to unallocated fourindex_dense"); if(i==j||a==b && elem) laerror("antisymmetry violation in fourindex_dense"); #endif int minus=0; if(i::operator() ((i-2)*(i-1)/2+j-1, (a-2)*(a-1)/2+b-1) += minus? -elem : elem; } void print(std::ostream &out) const { unsigned int i,j,a,b; for(i=1; i<=nocc; ++i) for(j=1; j class fourindex_dense : public NRSMat { private: int nbas; public: fourindex_dense(): NRSMat() {}; explicit fourindex_dense(const int n): nbas(n), NRSMat(n*(n-1)/2) {}; fourindex_dense(const T &a, const int n): nbas(n), NRSMat(a,n*(n-1)/2) {}; fourindex_dense(const T *a, const int n): nbas(n), NRSMat(a,n*(n-1)/2) {}; //and also construct it from sparse and externally stored fourindex classes //it seems not possible to nest template just for the two constructors fourindex_dense(const fourindex &rhs); fourindex_dense(const fourindex_ext &rhs); void set(unsigned int i, unsigned int j, unsigned int k, unsigned int l, T elem); void add(unsigned int i, unsigned int j, unsigned int k, unsigned int l, T elem); void add_unique(unsigned int i, unsigned int j, unsigned int k, unsigned int l, T elem); const T operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) const; void resize(const int n) {nbas=n; (*this).NRSMat::resize(n*(n-1)/2);}; void print(std::ostream &out) const { unsigned int i,j,k,l; for(i=1; i<=nbas; ++i) for(k=1;k const T fourindex_dense::operator() (unsigned int i, unsigned int j, unsigned int k, unsigned int l) const { int I = ASMat_index_1(i,j); int J = ASMat_index_1(k,l); if (I<0 || J<0) return 0.; int sign=1; if(i=(unsigned int)NRSMat::nn || J>=(unsigned int)NRSMat::nn) laerror("index out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif return sign==1? NRSMat::v[SMat_index(I,J)] : -NRSMat::v[SMat_index(I,J)]; } template void fourindex_dense::set(unsigned int i, unsigned int j, unsigned int k, unsigned int l, T elem) { if(i=NRSMat::nn || J>=NRSMat::nn) laerror("index out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif NRSMat::v[SMat_index(I,J)] = elem; } template void fourindex_dense::add(unsigned int i, unsigned int j, unsigned int k, unsigned int l, T elem) { if(i=NRSMat::nn || J>=NRSMat::nn) laerror("index out of range"); if (!NRSMat::v) laerror("access to unallocated fourindex_dense"); #endif NRSMat::v[SMat_index(I,J)] += elem; } template void fourindex_dense::add_unique(unsigned int i, unsigned int j, unsigned int k, unsigned int l, T elem) { if(i<=j || k<=l) return; int I = ASMat_index_1(i,j); int J = ASMat_index_1(k,l); if (I<0 || J<0 || I::v[SMat_index(I,J)] += elem; } template fourindex_dense::fourindex_dense(const fourindex &rhs) : nbas(rhs.size()), NRSMat((T)0,rhs.size()*(rhs.size()-1)/2) { if(rhs.getsymmetry() != twoelectronrealmullikan ) laerror("fourindex_dense symmetry mismatch"); typename fourindex_ext::piterator p; //we have to run over equivalents in non-canonical order to build the antisymmetrization properly; it could be done less elegantly but more efficiently moving the if's to outer parts of the piterator loop, if needed for(p= const_cast *>(&rhs)->pbegin(); p.notend(); ++p) { I i=p->index.indiv.i; I j=p->index.indiv.j; I k=p->index.indiv.k; I l=p->index.indiv.l; add_unique(i,k,j,l,p->elem); add_unique(i,k,l,j,-p->elem); } } template fourindex_dense::fourindex_dense(const fourindex_ext &rhs) : nbas(rhs.size()), NRSMat((T)0,rhs.size()*(rhs.size()-1)/2) { if(rhs.getsymmetry() != twoelectronrealmullikan ) laerror("fourindex_dense symmetry mismatch"); typename fourindex_ext::piterator p; //we have to run over equivalents in non-canonical order to build the antisymmetrization properly; it could be done less elegantly but more efficiently moving the if's to outer parts of the piterator loop, if needed for(p= const_cast *>(&rhs)->pbegin(); p.notend(); ++p) { I i=p->index.indiv.i; I j=p->index.indiv.j; I k=p->index.indiv.k; I l=p->index.indiv.l; add_unique(i,k,j,l,p->elem); add_unique(i,k,l,j,-p->elem); } } }//namespace //TODO: //implement fourindex_dense for unitary t2 symmetry types #endif /*_fourindex_included*/