diff --git a/Grid/algorithms/iterative/KrylovSchur.h b/Grid/algorithms/iterative/KrylovSchur.h index 6a8b821ba..0c9c305af 100644 --- a/Grid/algorithms/iterative/KrylovSchur.h +++ b/Grid/algorithms/iterative/KrylovSchur.h @@ -338,12 +338,11 @@ class KrylovSchur { RitzFilter ritzFilter; // how to sort evals public: - RealD *shift; bool doEvalCheck = false; KrylovSchur(LinearOperatorBase &_Linop, GridBase *_Grid, RealD _Tolerance, RitzFilter filter = EvalReSmall) : Linop(_Linop), Grid(_Grid), Tolerance(_Tolerance), ritzFilter(filter), u(_Grid), MaxIter(-1), Nm(-1), Nk(-1), Nstop (-1), - evals (0), ritzEstimates (), evecs (), ssq (0.0), rtol (0.0), beta_k (0.0), approxLambdaMax (0.0),shift(NULL) + evals (0), ritzEstimates (), evecs (), ssq (0.0), rtol (0.0), beta_k (0.0), approxLambdaMax (0.0) { u = Zero(); }; @@ -360,34 +359,26 @@ class KrylovSchur { std::vector getEvecs() { return evecs; } /** - * Runs the Krylov-Schur loop. + * Runs the non-harmonic Krylov-Schur loop. * - Runs an Arnoldi step to generate the Rayleigh quotient and Krylov basis. - * - Schur decompose the Rayleigh quotient. - * - Permutes the Rayleigh quotient according to the eigenvalues. - * - Truncate the Krylov-Schur expansion. + * - Schur decompose the Rayleigh quotient. + * - Permutes the Rayleigh quotient according to the eigenvalues. + * - Truncate the Krylov-Schur expansion. */ - void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, RealD *_shift=NULL, bool doubleOrthog = true) { - -// RealD shift_=1.; -// shift = &shift_; - if(_shift) shift = _shift; + void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, bool doubleOrthog = true) { MaxIter = _maxIter; Nm = _Nm; Nk = _Nk; Nstop = _Nstop; - + ssq = norm2(v0); RealD approxLambdaMax = approxMaxEval(v0); rtol = Tolerance * approxLambdaMax; std::cout << GridLogMessage << "Approximate max eigenvalue: " << approxLambdaMax << std::endl; - // rtol = Tolerance; b = Eigen::VectorXcd::Zero(Nm); // start as e_{k+1} b(Nm-1) = 1.0; - // basis = new std::vector (Nm, Grid); - // evecs.reserve(); - int start = 0; Field startVec = v0; littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm); @@ -396,229 +387,210 @@ class KrylovSchur { // Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess) arnoldiIteration(startVec, Nm, start, doubleOrthog); - startVec = u; // original code + startVec = u; start = Nk; - + std::cout << GridLogDebug << "b after Arnoldi " << b << std::endl; - // checkKSDecomposition(); - - RealD gamma; - Field uhat(Grid); - Eigen::MatrixXcd Btilde; - std::vector basis2_s; - Eigen::VectorXcd b_s; -#if 1 -if (shift){ - -if(0){ - Field w(Grid); - - ComplexD coeff,coeff2; - for (int j = 0; j < Nm; j++) { - Linop.Op(basis[j], w); - for (int k = 0; k < Nm; k++) { - coeff2 = innerProduct(basis[k], basis[j]); - coeff = innerProduct(basis[k], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after. - std::cout << GridLogMessage << " Rayleigh "< = " << coeff2 << std::endl; - } - coeff = innerProduct(basis[j], u); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after. - std::cout << GridLogMessage << " u "< basisTmp_s = std::vector (basis2_s.begin(), basis2_s.begin() + Nk); - basis2_s = basisTmp_s; + private: - Eigen::VectorXcd btmp_s = b_s.head(Nk); - b_s = btmp_s; - - Eigen::VectorXcd ghat = g; - ghat = -Q_s * g; + /** + * Non-harmonic restart step: Schur-decompose Rayleigh, reorder by ritzFilter, + * rotate the basis into the Schur vectors, and truncate to the leading Nk. + */ + void nonHarmonicRestart() { + // Perform a Schur decomposition on Rayleigh + ComplexSchurDecomposition schur (Rayleigh, false, ritzFilter); + std::cout << GridLogDebug << "Schur decomp holds? " << schur.checkDecomposition() << std::endl; - Eigen::VectorXcd gtmp_s = ghat.head(Nk); - ghat = gtmp_s; + // Rearrange Schur matrix so wanted evals are on top left (like MATLAB's ordschur) + std::cout << GridLogMessage << "Reordering Schur eigenvalues" << std::endl; + schur.schurReorder(Nk); + std::cout << GridLogMessage << "Schur eigenvalues reordered." << std::endl; - uhat = utilde; - for (int j = 0; j b = Q*b -// Eq.(44) - if(0){ + std::vector basis2; + constructUR(basis2, basis, Qt, Nm,Nk); +// basis = basis2; + + std::cout << GridLogMessage << "*** TRUNCATING FOR RESTART *** " << std::endl; + std::cout << GridLogDebug << "Rayleigh before truncation: " << std::endl << Rayleigh << std::endl; + + Eigen::MatrixXcd RayTmp = Rayleigh(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)); + Rayleigh = RayTmp; + + std::vector basisTmp = std::vector (basis2.begin(), basis2.begin() + Nk); + basis = basisTmp; + + Eigen::VectorXcd btmp = b.head(Nk); + b = btmp; + + std::cout << GridLogDebug << "Rayleigh after truncation: " << std::endl << Rayleigh << std::endl; + + checkKSDecomposition(); + + // Compute eigensystem of Rayleigh. Note the eigenvectors correspond to the sorted eigenvalues. + computeEigensystem(Rayleigh); + std::cout << GridLogMessage << "Eigenvalues (first Nk sorted): " << std::endl << evals << std::endl; + } + + /** + * Harmonic restart step: Schur-decompose the shift-augmented Rayleigh quotient + * so Ritz values close to `shiftVal` are reordered to the top, then rotate and + * truncate exactly as in the non-harmonic case. + */ + void harmonicRestart(RealD shiftVal) { + Eigen::MatrixXcd temp = Rayleigh; + for (int m=0;m= Nstop || i == MaxIter - 1) { + std::cout << GridLogMessage << "Converged with " << Nconv << " / " << Nstop << " eigenvectors on iteration " + << i << "." << std::endl; + std::cout << GridLogMessage << "Eigenvalues: " << std::endl << evals << std::endl; + + if (doEvalCheck) { Field w(Grid); - - ComplexD coeff,coeff2; - for (int j = 0; j < Nk; j++) { - Linop.Op(basis2_s[j], w); - for (int k = 0; k < Nk; k++) { - coeff2 = innerProduct(basis2_s[k], basis2_s[j]); - coeff = innerProduct(basis2_s[k], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after. - std::cout << GridLogMessage << " Btilde "< = " << coeff2 << std::endl; - } - coeff = innerProduct(basis2_s[j], uhat); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after. - coeff2 = innerProduct(uhat,w); - std::cout << GridLogMessage << " uhat "< b = Q*b - - std::vector basis2; - constructUR(basis2, basis, Qt, Nm,Nm); - basis = basis2; -if(0){ - Field w(Grid); - - ComplexD coeff,coeff2; - for (int j = 0; j < Nm; j++) { - Linop.Op(basis[j], w); - for (int k = 0; k < Nm; k++) { - coeff2 = innerProduct(basis[k], basis[j]); - coeff = innerProduct(basis[k], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after. - std::cout << GridLogMessage << " Stilde "< = " << coeff2 << std::endl; - } - coeff = innerProduct(basis[j], u); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after. - std::cout << GridLogMessage << " u"< basisTmp = std::vector (basis.begin(), basis.begin() + Nk); - basis = basisTmp; - - Eigen::VectorXcd btmp = b.head(Nk); - b = btmp; - - std::cout << GridLogDebug << "Rayleigh after truncation: " << std::endl << Rayleigh << std::endl; - - - checkKSDecomposition(); - - // Compute eigensystem of Rayleigh. Note the eigenvectors correspond to the sorted eigenvalues. - computeEigensystem(Rayleigh); - std::cout << GridLogMessage << "Eigenvalues (first Nk sorted): " << std::endl << evals << std::endl; -} - - if(shift){ - Rayleigh = Btilde; - basis= basis2_s; - b = b_s; - beta_k = gamma; - u= uhat; - checkKSDecomposition(); - computeEigensystem(Rayleigh); - std::cout << GridLogMessage << "Eigenvalues (first Nk sorted): " << std::endl << evals << std::endl; - } - - // check convergence and return if needed. - int Nconv = converged(); - std::cout << GridLogMessage << "Number of evecs converged: " << Nconv << std::endl; - if (Nconv >= Nstop || i == MaxIter - 1) { - std::cout << GridLogMessage << "Converged with " << Nconv << " / " << Nstop << " eigenvectors on iteration " - << i << "." << std::endl; - // basisRotate(evecs, Qt, 0, Nk, 0, Nk, Nm); // Think this might have been the issue - std::cout << GridLogMessage << "Eigenvalues: " << std::endl << evals << std::endl; - - if (doEvalCheck) { - Field w(Grid); - for (int k = 0; k < (int)evecs.size(); k++) { - Linop.Op(evecs[k], w); - ComplexD eval_est = toStdCmplx(innerProduct(evecs[k], w)); - w -= eval_est * evecs[k]; - RealD res = std::sqrt(norm2(w)); - std::cout << GridLogMessage << "KrylovSchur: evec[" << k << "]" - << " eval_reported = " << evals[k] - << " eval_est = " << eval_est - << " || A v - eval_est * v || = " << res << std::endl; - } - } - - // writeEigensystem(path); - - return; } + return true; } + return false; } + public: + /** * Constructs the Arnoldi basis for the Krylov space K_n(D, src). (TODO make private) *