/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./lib/algorithms/iterative/ImplicitlyRestartedBlockLanczosCoarse.h Copyright (C) 2023 Author: Peter Boyle Author: Yong-Chull Jang Author: Chulwoo Jung 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ #pragma once #include //memset NAMESPACE_BEGIN(Grid); #define Glog std::cout << GridLogMessage ///////////////////////////////////////////////////////////// // Implicitly restarted block lanczos ///////////////////////////////////////////////////////////// template class ImplicitlyRestartedBlockLanczosCoarse { private: std::string cname = std::string("ImplicitlyRestartedBlockLanczosCoarse"); int MaxIter; // Max iterations int Nstop; // Number of evecs checked for convergence int Nu; // Number of vecs in the unit block int Nk; // Number of converged sought int Nm; // total number of vectors int Nblock_k; // Nk/Nu int Nblock_m; // Nm/Nu int Nconv_test_interval; // Number of skipped vectors when checking a convergence RealD eresid; IRBLdiagonalisation diagonalisation; //////////////////////////////////// // Embedded objects //////////////////////////////////// SortEigen _sort; LinearOperatorBase &_Linop; OperatorFunction &_poly; GridBase * f_grid; GridBase * mrhs_grid; int mrhs; ///////////////////////// // BLAS objects ///////////////////////// int Nevec_acc; // Number of eigenvectors stored in the buffer evec_acc void VectorPoly(std::vector &in,std::vector &out) { Field mrhs_in(mrhs_grid); Field mrhs_out(mrhs_grid); for(int r=0;r= in.size()) rrr = 0; InsertSlice(in[rrr],mrhs_in,rr,0); } _poly(_Linop,mrhs_in,mrhs_out); for(int rr=0;rr &Linop, // op GridBase * f_Grid, GridBase * mrhs_Grid, int _mrhs, OperatorFunction & poly, // polynomial int _Nstop, // really sought vecs int _Nconv_test_interval, // conv check interval int _Nu, // vecs in the unit block int _Nk, // sought vecs int _Nm, // total vecs RealD _eresid, // resid in lmd deficit int _MaxIter, // Max iterations IRBLdiagonalisation _diagonalisation = IRBLdiagonaliseWithEigen) : _Linop(Linop), _poly(poly),f_grid(f_Grid), mrhs_grid(mrhs_Grid), Nstop(_Nstop), Nconv_test_interval(_Nconv_test_interval), mrhs(_mrhs), Nu(_Nu), Nk(_Nk), Nm(_Nm), Nblock_m(_Nm/_Nu), Nblock_k(_Nk/_Nu), eresid(_eresid), MaxIter(_MaxIter), diagonalisation(_diagonalisation), Nevec_acc(_Nu) { assert( (Nk%Nu==0) && (Nm%Nu==0) ); }; //////////////////////////////// // Helpers //////////////////////////////// static RealD normalize(Field& v, int if_print=0) { RealD nn = norm2(v); nn = sqrt(nn); v = v * (1.0/nn); return nn; } void orthogonalize(Field& w, std::vector& evec, int k, int if_print=0) { typedef typename Field::scalar_type MyComplex; ComplexD ip; for(int j=0; j 1e-14) Glog<<"orthogonalize before: "< 1e-14) Glog<<"orthogonalize after: "<& evec, int k) { orthogonalize(w, evec, k,1); } void orthogonalize(std::vector& w, int _Nu, std::vector& evec, int k, int if_print=0) { typedef typename Field::scalar_type MyComplex; MyComplex ip; for(int j=0; j& evec, int k, int Nu) { typedef typename Field::scalar_type MyComplex; MyComplex ip; for(int j=0; j& eval, std::vector& evec, const std::vector& src, int& Nconv, LanczosType Impl) { switch (Impl) { case LanczosType::irbl: calc_irbl(eval,evec,src,Nconv); break; case LanczosType::rbl: calc_rbl(eval,evec,src,Nconv); break; } } void calc_irbl(std::vector& eval, std::vector& evec, const std::vector& src, int& Nconv) { std::string fname = std::string(cname+"::calc_irbl()"); GridBase *grid = evec[0].Grid(); assert(grid == src[0].Grid()); assert( Nu = src.size() ); Glog << std::string(74,'*') << std::endl; Glog << fname + " starting iteration 0 / "<< MaxIter<< std::endl; Glog << std::string(74,'*') << std::endl; Glog <<" -- seek Nk = "<< Nk <<" vectors"<< std::endl; Glog <<" -- accept Nstop = "<< Nstop <<" vectors"<< std::endl; Glog <<" -- total Nm = "<< Nm <<" vectors"<< std::endl; Glog <<" -- size of eval = "<< eval.size() << std::endl; Glog <<" -- size of evec = "<< evec.size() << std::endl; if ( diagonalisation == IRBLdiagonaliseWithEigen ) { Glog << "Diagonalisation is Eigen "<< std::endl; #ifdef USE_LAPACK } else if ( diagonalisation == IRBLdiagonaliseWithLAPACK ) { Glog << "Diagonalisation is LAPACK "<< std::endl; #endif } else { abort(); } Glog << std::string(74,'*') << std::endl; assert(Nm == evec.size() && Nm == eval.size()); std::vector> lmd(Nu,std::vector(Nm,0.0)); std::vector> lme(Nu,std::vector(Nm,0.0)); std::vector> lmd2(Nu,std::vector(Nm,0.0)); std::vector> lme2(Nu,std::vector(Nm,0.0)); std::vector eval2(Nm); std::vector resid(Nk); Eigen::MatrixXcd Qt = Eigen::MatrixXcd::Zero(Nm,Nm); Eigen::MatrixXcd Q = Eigen::MatrixXcd::Zero(Nm,Nm); std::vector Iconv(Nm); std::vector B(Nm,grid); // waste of space replicating std::vector f(Nu,grid); std::vector f_copy(Nu,grid); Field v(grid); Nconv = 0; RealD beta_k; // set initial vector for (int i=0; i& eval, std::vector& evec, const std::vector& src, int& Nconv) { std::string fname = std::string(cname+"::calc_rbl()"); GridBase *grid = evec[0].Grid(); assert(grid == src[0].Grid()); assert( Nu = src.size() ); int Np = (Nm-Nk); if (Np > 0 && MaxIter > 1) Np /= MaxIter; int Nblock_p = Np/Nu; for(int i=0;i< evec.size();i++) evec[0].Advise()=AdviseInfrequentUse; Glog << std::string(74,'*') << std::endl; Glog << fname + " starting iteration 0 / "<< MaxIter<< std::endl; Glog << std::string(74,'*') << std::endl; Glog <<" -- seek (min) Nk = "<< Nk <<" vectors"<< std::endl; Glog <<" -- seek (inc) Np = "<< Np <<" vectors"<< std::endl; Glog <<" -- seek (max) Nm = "<< Nm <<" vectors"<< std::endl; Glog <<" -- accept Nstop = "<< Nstop <<" vectors"<< std::endl; Glog <<" -- size of eval = "<< eval.size() << std::endl; Glog <<" -- size of evec = "<< evec.size() << std::endl; if ( diagonalisation == IRBLdiagonaliseWithEigen ) { Glog << "Diagonalisation is Eigen "<< std::endl; #ifdef USE_LAPACK } else if ( diagonalisation == IRBLdiagonaliseWithLAPACK ) { Glog << "Diagonalisation is LAPACK "<< std::endl; #endif } else { abort(); } Glog << std::string(74,'*') << std::endl; assert(Nm == evec.size() && Nm == eval.size()); std::vector> lmd(Nu,std::vector(Nm,0.0)); std::vector> lme(Nu,std::vector(Nm,0.0)); std::vector> lmd2(Nu,std::vector(Nm,0.0)); std::vector> lme2(Nu,std::vector(Nm,0.0)); std::vector eval2(Nk); std::vector resid(Nm); Eigen::MatrixXcd Qt = Eigen::MatrixXcd::Zero(Nm,Nm); Eigen::MatrixXcd Q = Eigen::MatrixXcd::Zero(Nm,Nm); std::vector Iconv(Nm); // int Ntest=Nu; // std::vector B(Nm,grid); // waste of space replicating std::vector B(1,grid); // waste of space replicating std::vector f(Nu,grid); std::vector f_copy(Nu,grid); Field v(grid); Nconv = 0; // RealD beta_k; // set initial vector for (int i=0; i Btmp(Nstop,grid); // waste of space replicating for(int i=0; i>& lmd, std::vector>& lme, std::vector& evec, std::vector& w, std::vector& w_copy, int b) { const RealD tiny = 1.0e-20; int Nu = w.size(); int Nm = evec.size(); assert( b < Nm/Nu ); // converts block index to full indicies for an interval [L,R) int L = Nu*b; int R = Nu*(b+1); Real beta; assert((Nu%mrhs)==0); std::vector in(mrhs,f_grid); std::vector out(mrhs,f_grid); // unnecessary copy. Can or should it be avoided? int k_start = 0; while ( k_start < Nu) { Glog << "k_start= "<0) { for (int u=0; u& eval, std::vector>& lmd, std::vector>& lme, int Nu, int Nk, int Nm, Eigen::MatrixXcd & Qt, // Nm x Nm GridBase *grid) { assert( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk <= Nm ); Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk); for ( int u=0; u eigensolver(BlockTriDiag); for (int i = 0; i < Nk; i++) { eval[Nk-1-i] = eigensolver.eigenvalues()(i); } for (int i = 0; i < Nk; i++) { for (int j = 0; j < Nk; j++) { Qt(j,Nk-1-i) = eigensolver.eigenvectors()(j,i); //Qt(Nk-1-i,j) = eigensolver.eigenvectors()(i,j); //Qt(i,j) = eigensolver.eigenvectors()(i,j); } } } #ifdef USE_LAPACK void diagonalize_lapack(std::vector& eval, std::vector>& lmd, std::vector>& lme, int Nu, int Nk, int Nm, Eigen::MatrixXcd & Qt, // Nm x Nm GridBase *grid) { Glog << "diagonalize_lapack: Nu= "<_Nprocessors; int node = grid->_processor; int interval = (NN/total)+1; double vl = 0.0, vu = 0.0; MKL_INT il = interval*node+1 , iu = interval*(node+1); if (iu > NN) iu=NN; Glog << "node "<= il-1; i--){ evals_tmp[i] = evals_tmp[i - (il-1)]; if (il>1) evals_tmp[i-(il-1)]=0.; for (int j = 0; j< NN; j++){ evec_tmp[i*NN+j] = evec_tmp[(i - (il-1))*NN+j]; if (il>1) { evec_tmp[(i-(il-1))*NN+j].imag=0.; evec_tmp[(i-(il-1))*NN+j].real=0.; } } } } { grid->GlobalSumVector(evals_tmp,NN); grid->GlobalSumVector((double*)evec_tmp,2*NN*NN); } } for (int i = 0; i < Nk; i++) eval[Nk-1-i] = evals_tmp[i]; for (int i = 0; i < Nk; i++) { for (int j = 0; j < Nk; j++) { // Qt(j,Nk-1-i) = eigensolver.eigenvectors()(j,i); Qt(j,Nk-1-i)=std::complex ( evec_tmp[i*Nk+j].real, evec_tmp[i*Nk+j].imag); // ( evec_tmp[(Nk-1-j)*Nk+Nk-1-i].real, // evec_tmp[(Nk-1-j)*Nk+Nk-1-i].imag); } } if (1){ Eigen::SelfAdjointEigenSolver eigensolver(BlockTriDiag); for (int i = 0; i < Nk; i++) { Glog << "eval = "<& eval, std::vector>& lmd, std::vector>& lme, int Nu, int Nk, int Nm, Eigen::MatrixXcd & Qt, GridBase *grid) { Qt = Eigen::MatrixXcd::Identity(Nm,Nm); if ( diagonalisation == IRBLdiagonaliseWithEigen ) { diagonalize_Eigen(eval,lmd,lme,Nu,Nk,Nm,Qt,grid); #ifdef USE_LAPACK } else if ( diagonalisation == IRBLdiagonaliseWithLAPACK ) { diagonalize_lapack(eval,lmd,lme,Nu,Nk,Nm,Qt,grid); #endif } else { assert(0); } } void unpackHermitBlockTriDiagMatToEigen( std::vector>& lmd, std::vector>& lme, int Nu, int Nb, int Nk, int Nm, Eigen::MatrixXcd& M) { // Glog << "unpackHermitBlockTriDiagMatToEigen() begin" << '\n'; assert( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk <= Nm ); M = Eigen::MatrixXcd::Zero(Nk,Nk); // rearrange for ( int u=0; u>& lmd, std::vector>& lme, int Nu, int Nb, int Nk, int Nm, Eigen::MatrixXcd& M) { // Glog << "packHermitBlockTriDiagMatfromEigen() begin" << '\n'; assert( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk <= Nm ); // rearrange for ( int u=0; u QRD(Mtmp); Q = QRD.householderQ(); R = QRD.matrixQR(); // upper triangular part is the R matrix. // lower triangular part used to represent series // of Q sequence. // Glog << "shiftedQRDecompEigen() Housholder & QR" << '\n'; // equivalent operation of Qprod *= Q //M = Eigen::MatrixXcd::Zero(Nm,Nm); //for (int i=0; i Nm) kmax = Nm; for (int k=i; ki) M(i,j) = conj(M(j,i)); // if (i-j > Nu || j-i > Nu) M(i,j) = 0.; // } //} // Glog << "shiftedQRDecompEigen() end" <