/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./lib/algorithms/iterative/BlockConjugateGradient.h Copyright (C) 2017 Author: Azusa Yamaguchi Author: Peter Boyle 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 */ #ifndef GRID_BLOCK_CONJUGATE_GRADIENT_H #define GRID_BLOCK_CONJUGATE_GRADIENT_H namespace Grid { enum BlockCGtype { BlockCG, BlockCGrQ, CGmultiRHS }; ////////////////////////////////////////////////////////////////////////// // Block conjugate gradient. Dimension zero should be the block direction ////////////////////////////////////////////////////////////////////////// template class BlockConjugateGradient : public OperatorFunction { public: typedef typename Field::scalar_type scomplex; int blockDim ; int Nblock; BlockCGtype CGtype; bool ErrorOnNoConverge; // throw an assert when the CG fails to converge. // Defaults true. RealD Tolerance; Integer MaxIterations; Integer IterationsToComplete; //Number of iterations the CG took to finish. Filled in upon completion BlockConjugateGradient(BlockCGtype cgtype,int _Orthog,RealD tol, Integer maxit, bool err_on_no_conv = true) : Tolerance(tol), CGtype(cgtype), blockDim(_Orthog), MaxIterations(maxit), ErrorOnNoConverge(err_on_no_conv) {}; //////////////////////////////////////////////////////////////////////////////////////////////////// // Thin QR factorisation (google it) //////////////////////////////////////////////////////////////////////////////////////////////////// void ThinQRfact (Eigen::MatrixXcd &m_rr, Eigen::MatrixXcd &C, Eigen::MatrixXcd &Cinv, Field & Q, const Field & R) { int Orthog = blockDim; // First dimension is block dim; this is an assumption //////////////////////////////////////////////////////////////////////////////////////////////////// //Dimensions // R_{ferm x Nblock} = Q_{ferm x Nblock} x C_{Nblock x Nblock} -> ferm x Nblock // // Rdag R = m_rr = Herm = L L^dag <-- Cholesky decomposition (LLT routine in Eigen) // // Q C = R => Q = R C^{-1} // // Want Ident = Q^dag Q = C^{-dag} R^dag R C^{-1} = C^{-dag} L L^dag C^{-1} = 1_{Nblock x Nblock} // // Set C = L^{dag}, and then Q^dag Q = ident // // Checks: // Cdag C = Rdag R ; passes. // QdagQ = 1 ; passes //////////////////////////////////////////////////////////////////////////////////////////////////// sliceInnerProductMatrix(m_rr,R,R,Orthog); //////////////////////////////////////////////////////////////////////////////////////////////////// // Cholesky from Eigen // There exists a ldlt that is documented as more stable //////////////////////////////////////////////////////////////////////////////////////////////////// Eigen::MatrixXcd L = m_rr.llt().matrixL(); C = L.adjoint(); Cinv = C.inverse(); //////////////////////////////////////////////////////////////////////////////////////////////////// // Q = R C^{-1} // // Q_j = R_i Cinv(i,j) // // NB maddMatrix conventions are Right multiplication X[j] a[j,i] already //////////////////////////////////////////////////////////////////////////////////////////////////// // FIXME:: make a sliceMulMatrix to avoid zero vector sliceMulMatrix(Q,Cinv,R,Orthog); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Call one of several implementations //////////////////////////////////////////////////////////////////////////////////////////////////// void operator()(LinearOperatorBase &Linop, const Field &Src, Field &Psi) { if ( CGtype == BlockCGrQ ) { BlockCGrQsolve(Linop,Src,Psi); } else if (CGtype == BlockCG ) { BlockCGsolve(Linop,Src,Psi); } else if (CGtype == CGmultiRHS ) { CGmultiRHSsolve(Linop,Src,Psi); } else { assert(0); } } //////////////////////////////////////////////////////////////////////////// // BlockCGrQ implementation: //-------------------------- // X is guess/Solution // B is RHS // Solve A X_i = B_i ; i refers to Nblock index //////////////////////////////////////////////////////////////////////////// void BlockCGrQsolve(LinearOperatorBase &Linop, const Field &B, Field &X) { int Orthog = blockDim; // First dimension is block dim; this is an assumption Nblock = B._grid->_fdimensions[Orthog]; std::cout< residuals(Nblock); std::vector ssq(Nblock); sliceNorm(ssq,B,Orthog); RealD sssum=0; for(int b=0;b Thin QR factorisation (google it) * for k: * Z = AD * M = [D^dag Z]^{-1} * X = X + D MC * QS = Q - ZM * D = Q + D S^dag * C = S C */ /////////////////////////////////////// // Initial block: initial search dir is guess /////////////////////////////////////// std::cout << GridLogMessage<<"BlockCGrQ algorithm initialisation " < Thin QR factorisation (google it) Linop.HermOp(X, AD); tmp = B - AD; ThinQRfact (m_rr, m_C, m_Cinv, Q, tmp); D=Q; std::cout << GridLogMessage<<"BlockCGrQ computed initial residual and QR fact " < max_resid ) max_resid = rr; } std::cout << GridLogIterative << "\titeration "< &Linop, const Field &Src, Field &Psi) { int Orthog = blockDim; // First dimension is block dim; this is an assumption Nblock = Src._grid->_fdimensions[Orthog]; std::cout< residuals(Nblock); std::vector ssq(Nblock); sliceNorm(ssq,Src,Orthog); RealD sssum=0; for(int b=0;b max_resid ) max_resid = rr; } if ( max_resid < Tolerance*Tolerance ) { SolverTimer.Stop(); std::cout << GridLogMessage<<"BlockCG converged in "< &Linop, const Field &Src, Field &Psi) { int Orthog = blockDim; // First dimension is block dim Nblock = Src._grid->_fdimensions[Orthog]; std::cout< v_pAp(Nblock); std::vector v_rr (Nblock); std::vector v_rr_inv(Nblock); std::vector v_alpha(Nblock); std::vector v_beta(Nblock); // Initial residual computation & set up std::vector residuals(Nblock); std::vector ssq(Nblock); sliceNorm(ssq,Src,Orthog); RealD sssum=0; for(int b=0;b max_resid ) max_resid = rr; } if ( max_resid < Tolerance*Tolerance ) { SolverTimer.Stop(); std::cout << GridLogMessage<<"MultiRHS solver converged in " <