From 1965f4f65371225f1ebf1d24dfbfa6d4eb7d6b59 Mon Sep 17 00:00:00 2001 From: Jiri Pittner Date: Fri, 12 Dec 2025 15:54:32 +0100 Subject: [PATCH] lanczos: first working version --- lanczos.h | 104 ++++++++++++++++++++++++++++++++++++++++++------------ t.cc | 67 ++++++++++++++++++++--------------- 2 files changed, 120 insertions(+), 51 deletions(-) diff --git a/lanczos.h b/lanczos.h index d5d1799..e13fb5b 100644 --- a/lanczos.h +++ b/lanczos.h @@ -24,6 +24,11 @@ #include "nonclass.h" #include "auxstorage.h" +//TODO: +//@@@convergece test by residual norm rather than by energy difference +//@@@implement restart when Krylov space is exceeded +//@@@implement inital guess of more than 1 vector (also in davidson) + namespace LA { //Lanczos diagonalization of hermitean matrix @@ -36,7 +41,7 @@ namespace LA { template extern void lanczos(const Matrix &bigmat, NRVec &eivals, NRVec *eivecs, const char *eivecsfile, int nroots=1, const bool verbose=0, const double eps=1e-6, - const bool incore=1, int maxit=100, const int maxkrylov = 500, + const bool incore=1, int maxit=100, const int maxkrylov = 1000, void (*initguess)(NRVec &)=NULL, const typename LA_traits::normtype *target=NULL) { if(!bigmat.issymmetric()) laerror("lanczos only for hermitean matrices"); @@ -46,19 +51,16 @@ if ( n!= (int)bigmat.ncols()) laerror("non-square matrix in lanczos"); if(eivals.size() vec1(n),vec2(n); -NRVec::normtype> r(maxkrylov); NRVec *v0,*v1; AuxStorage *s0,*s1; if(incore) { v0 = new NRVec[maxkrylov]; - v1 = new NRVec[maxkrylov]; } else { s0 = new AuxStorage; - s1 = new AuxStorage; } int i,j; @@ -67,8 +69,6 @@ if(nroots>=maxkrylov) nroots =maxkrylov-1; int nroot=0; int oldnroot; -//@@@@@@@@@@ -#if 0 //default guess based on lowest diagonal element of the matrix if(initguess) initguess(vec1); @@ -81,36 +81,96 @@ else vec1[j]=1; } +NRVec::normtype> alpha(maxkrylov),beta(maxkrylov-1); + //initial step vec1.normalize(); -bigmat.gemv(0,vec2,'n',1,vec1); //avoid bigmat.operator*(vec), since that needs to allocate another n-sized vector if(incore) v0[0]=vec1; else s0->put(vec1,0); -if(incore) v1[0]=vec2; else s1->put(vec2,0); - +bigmat.gemv(0,vec2,'n',1,vec1); //avoid bigmat.operator*(vec), since that needs to allocate another n-sized vector +{ +T tmp=vec2*vec1; +alpha[0]= LA_traits::realpart(tmp); +if(LA_traits::imagpart(tmp)>1e-6) laerror("matrix probably not hermitian in lanczos"); +} +vec2.axpy(-alpha[0],vec1); +NRVec::normtype> r(1); r[0]=alpha[0]; +NRVec::normtype> rold; +NRMat::normtype> smallV; +int krylovsize=1; //iterative Lanczos int it=0; -for(k=2; k<=maxkrylov;++k) +for(j=1; jmaxit) {std::cout <<"too many interations in lanczos\n"; flag=1; goto finished;} + ++krylovsize; + //extend the Krylov space (last w vector expected in vec2, last v vector in vec1) + beta[j-1] = vec2.norm(); + if(beta[j-1] > 0) + { + vec2 /= beta[j-1]; + } + else + { + laerror("zero norm in lanczos"); + //could generate an arbitrary vector and orthonormalize it + } + if(incore) v0[j]=vec2; else s0->put(vec2,j); + vec1 *= -beta[j-1]; + bigmat.gemv(1,vec1,'n',1,vec2); + { + T tmp=vec1*vec2; + alpha[j]= LA_traits::realpart(tmp); + if(LA_traits::imagpart(tmp)>1e-6) laerror("matrix probably not hermitian in lanczos"); + } + vec1.axpy(-alpha[j],vec2); + vec1.swap(vec2); //move new w vector to vec2, new v vector to vec1 + + //diagonalize the tridiagonal + smallV.resize(j+1,j+1); + rold=r; + r=alpha.subvector(0,j); + NRVec::normtype> rr=beta.subvector(0,j-1); + diagonalize3(r,rr,&smallV); if(target) //resort eigenpairs by distance from the target { - NRVec::normtype> key(krylovsize+1); - for(int i=0; i<=krylovsize; ++i) key[i] = abs(r[i] - *target); - NRPerm p(krylovsize+1); + NRVec::normtype> key(j+1); + for(int i=0; i<=j; ++i) key[i] = abs(r[i] - *target); + NRPerm p(j+1); key.sort(0,p); - NRVec::normtype> rp(maxkrylov); - NRMat smallVp(maxkrylov,maxkrylov); - for(int i=0; i<=krylovsize; ++i) + NRVec::normtype> rp(j+1); + NRMat::normtype> smallVp(j+1,j+1); + for(int i=0; i<=j; ++i) { rp[i]= r[p[i+1]-1]; - for(int j=0; j<=krylovsize; ++j) smallVp(j,i) = smallV(j,p[i+1]-1); + for(int k=0; k<=j; ++k) smallVp(k,i) = smallV(k,p[i+1]-1); } r = rp; smallV = smallVp; } + if(verbose) + { + for(int iroot=0; iroot<=std::min(krylovsize,nroots-1); ++iroot) + { + std::cout <<"Lanczos: iter="<nroots) + { + bool conv=true; + for(int iroot=0; irooteps) conv=false; + if(conv) + { + flag=0; + goto converged; + } + } } flag=1; goto finished; @@ -125,7 +185,7 @@ for(nroot=0; nrootget(vec2,j); vec1.axpy(smallV(j,nroot),incore?v0[j]:vec2); @@ -141,12 +201,10 @@ for(nroot=0; nroot>n >>m; +bool which; +cin>>n >>m>>which ; NRSMat > a(n); a.randomize(.1); @@ -4522,9 +4557,10 @@ cout <<"Exact energies "< > r(m); NRVec > *eivecs = new NRVec >[m]; -davidson(a,r,eivecs,NULL,m,true,1e-5,true,10*n,n); +if(which) lanczos(a,r,eivecs,NULL,m,true,1e-5,true,10*n,n); +else davidson(a,r,eivecs,NULL,m,true,1e-5,true,10*n,n); -cout <<"Davidson energies " <>n >>m; -NRSMat a(n); -NRVec rr(n); - -for(int i=0;i aa; -NRMat vv(n,n); -aa=a; diagonalize(aa,rr,&vv); -NRVec r(m); -NRVec *eivecs = new NRVec[m]; -double target= 0; -cout <<"Exact energies " <&))NULL,&target); -cout <<"Davidson energies " <