mirror of
https://github.com/paboyle/Grid.git
synced 2026-07-17 15:43:27 +01:00
Adding TrueHarmonicBlockKrylovSchur
This commit is contained in:
@@ -89,12 +89,13 @@ NAMESPACE_CHECK(multigrid);
|
||||
#include <Grid/algorithms/iterative/BlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/SplitGridBlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/HarmonicBlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/TrueHarmonicBlockKrylovSchur.h>
|
||||
#include <Grid/algorithms/iterative/Gamma5BlockLanczos.h>
|
||||
//#include <Grid/algorithms/iterative/Gamma5ScalarLanczos.h>
|
||||
#include <Grid/algorithms/iterative/Arnoldi.h>
|
||||
#include <Grid/algorithms/iterative/LanczosBidiagonalization.h>
|
||||
#include <Grid/algorithms/iterative/RestartedLanczosBidiagonalization.h>
|
||||
//#include <Grid/algorithms/iterative/GCR.h>
|
||||
//#include <Grid/algorithms/iterative/MultiSplittingPreconditionedCG.h>
|
||||
#include <Grid/algorithms/iterative/MultiSplittingPreconditionedCG.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -92,6 +92,7 @@ NAMESPACE_BEGIN(Grid);
|
||||
template<class Field>
|
||||
class HarmonicBlockKrylovSchur {
|
||||
|
||||
protected:
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
|
||||
@@ -143,10 +144,12 @@ public:
|
||||
beta_k(0.0), rtol(0.0)
|
||||
{}
|
||||
|
||||
virtual ~HarmonicBlockKrylovSchur() = default;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point
|
||||
//--------------------------------------------------------------------
|
||||
void operator()(const std::vector<Field>& v0, int _maxIter, int _Nm, int _Nk,
|
||||
virtual void operator()(const std::vector<Field>& v0, int _maxIter, int _Nm, int _Nk,
|
||||
int _Nstop, int _Nblock = 1, bool doubleOrthog = true,
|
||||
bool doVerify = false)
|
||||
{
|
||||
@@ -161,6 +164,8 @@ public:
|
||||
if (useParityFlip) divisor *= 2;
|
||||
if (useGamma5) divisor *= 2;
|
||||
assert(Nblock % divisor == 0 && (int)v0.size() >= Nblock / divisor);
|
||||
std::cout << GridLogMessage << "divisor= " << divisor << std::endl;
|
||||
|
||||
}
|
||||
assert(Nm % Nblock == 0);
|
||||
assert(Nk % Nblock == 0);
|
||||
@@ -179,31 +184,8 @@ public:
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
int divisor = (useParityFlip ? 2 : 1) * (useGamma5 ? 2 : 1);
|
||||
int start = 0;
|
||||
std::vector<Field> startBlock;
|
||||
startBlock.reserve(Nblock);
|
||||
for (int i = 0; i < Nblock / divisor; i++) {
|
||||
std::vector<Field> group;
|
||||
group.push_back(v0[i]);
|
||||
if (useParityFlip) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field fp(Grid_);
|
||||
parityFlippedField(group[j], fp);
|
||||
group.push_back(std::move(fp));
|
||||
}
|
||||
}
|
||||
if (useGamma5) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field g5v(Grid_);
|
||||
gamma5Func(group[j], g5v);
|
||||
group.push_back(std::move(g5v));
|
||||
}
|
||||
}
|
||||
for (auto& f : group) startBlock.push_back(std::move(f));
|
||||
}
|
||||
std::vector<Field> startBlock = expandStartBlock(v0);
|
||||
|
||||
for (int iter = 0; iter < MaxIter; iter++) {
|
||||
std::cout << GridLogMessage
|
||||
@@ -448,7 +430,42 @@ public:
|
||||
<< "======== end verify ========" << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Starting-block expansion (parity flip / gamma5 partners)
|
||||
//--------------------------------------------------------------------
|
||||
// Expands Nblock/divisor seed vectors into the full Nblock starting
|
||||
// block, pairing each seed with its parity-flipped and/or gamma5
|
||||
// partners. Requires Nblock, Grid_ and the flags to be set.
|
||||
std::vector<Field> expandStartBlock(const std::vector<Field>& v0)
|
||||
{
|
||||
int divisor = (useParityFlip ? 2 : 1) * (useGamma5 ? 2 : 1);
|
||||
std::vector<Field> startBlock;
|
||||
startBlock.reserve(Nblock);
|
||||
for (int i = 0; i < Nblock / divisor; i++) {
|
||||
std::vector<Field> group;
|
||||
group.push_back(v0[i]);
|
||||
if (useParityFlip) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field fp(Grid_);
|
||||
parityFlippedField(group[j], fp);
|
||||
group.push_back(std::move(fp));
|
||||
}
|
||||
}
|
||||
if (useGamma5) {
|
||||
int n = (int)group.size();
|
||||
for (int j = 0; j < n; j++) {
|
||||
Field g5v(Grid_);
|
||||
gamma5Func(group[j], g5v);
|
||||
group.push_back(std::move(g5v));
|
||||
}
|
||||
}
|
||||
for (auto& f : group) startBlock.push_back(std::move(f));
|
||||
}
|
||||
return startBlock;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block Arnoldi iteration
|
||||
|
||||
@@ -451,18 +451,6 @@ if(0){
|
||||
|
||||
Eigen::MatrixXcd Q_s = schurS.getMatrixQ();
|
||||
Eigen::MatrixXcd Qt_s = Q_s.adjoint(); // TODO should Q be real?
|
||||
#if 0
|
||||
std::cout << GridLogMessage << "Q_s" << Q_s <<std::endl;
|
||||
std::cout << GridLogMessage << "Qt_s" << Qt_s <<std::endl;
|
||||
Eigen::MatrixXcd temp4= Q_s*Qt_s;
|
||||
std::cout << GridLogMessage << "Q_s*Qt_s" << temp4 << std::endl;
|
||||
temp4 = Btilde;
|
||||
std::cout << GridLogMessage << "Btilde" << temp4<< std::endl;
|
||||
temp4 = temp4*Qt_s;
|
||||
std::cout << GridLogMessage << "Btilde*Qt_s" << temp4<< std::endl;
|
||||
temp4 = Q_s*temp4;
|
||||
std::cout << GridLogMessage << "Q_s*Btilde*Qt_s" << temp4<< std::endl;
|
||||
#endif
|
||||
|
||||
Eigen::MatrixXcd S_s = schurS.getMatrixS();
|
||||
|
||||
@@ -470,7 +458,9 @@ if(0){
|
||||
b_s= b;
|
||||
b_s=Q_s*b; // Q is Qt in SlepC, b_s=bhat
|
||||
|
||||
constructUR(basis2_s, basis, Qt_s, Nm);
|
||||
std::cout << GridLogMessage << " constructUR "<< std::endl;
|
||||
constructUR(basis2_s, basis, Qt_s, Nm,Nk);
|
||||
std::cout << GridLogMessage << " constructUR "<< std::endl;
|
||||
|
||||
|
||||
Eigen::MatrixXcd RayTmp_s = Btilde(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
@@ -544,7 +534,7 @@ if (!shift){
|
||||
b = Q * b; // b^\dag = b^\dag * Q^\dag <==> b = Q*b
|
||||
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, Nm);
|
||||
constructUR(basis2, basis, Qt, Nm,Nm);
|
||||
basis = basis2;
|
||||
if(0){
|
||||
Field w(Grid);
|
||||
@@ -861,7 +851,7 @@ if (!shift){
|
||||
// std::cout << GridLogDebug << "Rayleigh in KSDecomposition: " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
std::vector<Field> rotated = basis;
|
||||
constructUR(rotated, basis, Rayleigh, k); // manually rotate
|
||||
constructUR(rotated, basis, Rayleigh, k,k); // manually rotate
|
||||
// Eigen::MatrixXcd Rt = Rayleigh.adjoint();
|
||||
// basisRotate(rotated, Rt, 0, k, 0, k, k); // UR
|
||||
|
||||
@@ -957,7 +947,7 @@ if (!shift){
|
||||
* Note that I believe this is equivalent to basisRotate(U, R.adjoint(), 0, N, 0, N, N), but I'm
|
||||
* not 100% sure (this will be slower and unoptimized though).
|
||||
*/
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field> &U, Eigen::MatrixXcd& R, int N) {
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field> &U, Eigen::MatrixXcd& R, int N, int N2) {
|
||||
Field tmp (Grid);
|
||||
|
||||
UR.clear();
|
||||
@@ -967,6 +957,7 @@ if (!shift){
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
if (i < N2)
|
||||
for (int j = 0; j < N; j++) {
|
||||
std::cout << GridLogDebug << "Adding R("<<j<<", "<<i<<") = " << R(j, i) << " to rotated" << std::endl;
|
||||
std::cout << GridLogDebug << "Norm of U[j] is " << norm2(U[j]) << " to rotated" << std::endl;
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/TrueHarmonicBlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
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_TRUE_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
#define GRID_TRUE_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
|
||||
#include <Grid/algorithms/iterative/HarmonicBlockKrylovSchur.h>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* True harmonic-Ritz block Krylov-Schur eigensolver.
|
||||
*
|
||||
* Difference from the base class
|
||||
* ------------------------------
|
||||
* HarmonicBlockKrylovSchur is a *shift-targeted* Krylov-Schur: it Schur-
|
||||
* decomposes (H - sigma I) and sorts the standard Ritz values by |lambda -
|
||||
* sigma|. For interior eigenvalues of Hermitian indefinite operators the
|
||||
* standard Ritz values can be spurious ("ghosts"), so the sort can select
|
||||
* junk. This variant performs genuine harmonic Ritz extraction instead.
|
||||
*
|
||||
* Harmonic extraction
|
||||
* -------------------
|
||||
* From the exact block Arnoldi relation
|
||||
*
|
||||
* A V = V H + F B^dag (1)
|
||||
*
|
||||
* harmonic Ritz pairs (theta, g) w.r.t. shift sigma satisfy the Petrov-
|
||||
* Galerkin condition (A - sigma)Vg - (theta - sigma)Vg perp (A - sigma)V,
|
||||
* which reduces (with Hs = H - sigma I) to the small eigenproblem
|
||||
*
|
||||
* Hhat g = (theta - sigma) g, Hhat = Hs + Hs^{-H} B B^dag (2)
|
||||
*
|
||||
* Exact thick restart
|
||||
* -------------------
|
||||
* From (2), H g = theta g - Hs^{-H} B (B^dag g), so the residual of every
|
||||
* harmonic pair lies in the Nblock-dimensional column space of the single
|
||||
* block
|
||||
*
|
||||
* W = F - V Hs^{-H} B :
|
||||
* A (V g) - theta (V g) = W (B^dag g) (3)
|
||||
*
|
||||
* Hence {V g_1 .. g_Nk} + cols(W) admits an EXACT truncated Krylov-Schur
|
||||
* relation A Y = Y H_k + What Bc^dag with dense H_k, and block Arnoldi
|
||||
* resumes cleanly from What. Krylov-Schur (Stewart, SIMAX 23(3), 2001)
|
||||
* does not require H to be triangular after restart -- only that the
|
||||
* decomposition holds exactly, which (3) guarantees.
|
||||
*
|
||||
* This is the block analogue of the GMRES-DR restart (Morgan, SISC 24(1),
|
||||
* 2002; block version: Morgan, Appl. Numer. Math. 54, 2005). The exact
|
||||
* composition "block harmonic Krylov-Schur" is a derivation from these
|
||||
* ingredients rather than a published algorithm; the relation (1) is
|
||||
* checked explicitly at every restart when doVerify=true, and identity (3)
|
||||
* was validated to machine precision in a standalone Eigen prototype.
|
||||
*
|
||||
* Caveats
|
||||
* -------
|
||||
* - If sigma is (numerically) an eigenvalue of H, Hs^{-H} is singular;
|
||||
* an assert fires -- perturb the shift slightly.
|
||||
* - Reported eigenvalues come from the eigensystem of the retained H_k
|
||||
* (the true projected Rayleigh quotient Y^dag A Y), as in the base
|
||||
* class, so convergence tests and reporting are inherited unchanged.
|
||||
*
|
||||
* Usage: identical to HarmonicBlockKrylovSchur.
|
||||
*/
|
||||
template<class Field>
|
||||
class TrueHarmonicBlockKrylovSchur : public HarmonicBlockKrylovSchur<Field> {
|
||||
|
||||
typedef HarmonicBlockKrylovSchur<Field> Base;
|
||||
typedef typename Base::CMat CMat;
|
||||
typedef typename Base::CVec CVec;
|
||||
|
||||
// Members of the dependent base class need explicit scope.
|
||||
using Base::Nblock;
|
||||
using Base::Nm;
|
||||
using Base::Nk;
|
||||
using Base::Nstop;
|
||||
using Base::MaxIter;
|
||||
using Base::Tolerance;
|
||||
using Base::shift;
|
||||
using Base::Linop;
|
||||
using Base::Grid_;
|
||||
using Base::ritzFilter;
|
||||
using Base::basis;
|
||||
using Base::H;
|
||||
using Base::F;
|
||||
using Base::B;
|
||||
using Base::beta_k;
|
||||
using Base::rtol;
|
||||
using Base::evals;
|
||||
using Base::littleEvecs;
|
||||
using Base::ritzEstimates;
|
||||
|
||||
using Base::expandStartBlock;
|
||||
using Base::blockArnoldiIteration;
|
||||
using Base::blockQR;
|
||||
using Base::computeEigensystem;
|
||||
using Base::converged;
|
||||
using Base::approxMaxEval;
|
||||
|
||||
public:
|
||||
using Base::evecs;
|
||||
using Base::doEvalCheck;
|
||||
using Base::useParityFlip;
|
||||
using Base::useGamma5;
|
||||
using Base::gamma5Func;
|
||||
using Base::verify;
|
||||
|
||||
TrueHarmonicBlockKrylovSchur(LinearOperatorBase<Field>& _Linop, GridBase* _Grid,
|
||||
RealD _Tolerance, ComplexD _shift = 0.0,
|
||||
RitzFilter _rf = EvalNormSmall)
|
||||
: Base(_Linop, _Grid, _Tolerance, _shift, _rf)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point (same signature and outer structure as the base;
|
||||
// the Schur-sort restart is replaced by harmonicRestart()).
|
||||
//--------------------------------------------------------------------
|
||||
virtual void operator()(const std::vector<Field>& v0, int _maxIter, int _Nm, int _Nk,
|
||||
int _Nstop, int _Nblock = 1, bool doubleOrthog = true,
|
||||
bool doVerify = false) override
|
||||
{
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm;
|
||||
Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
Nblock = _Nblock;
|
||||
|
||||
{
|
||||
int divisor = 1;
|
||||
if (useParityFlip) divisor *= 2;
|
||||
if (useGamma5) divisor *= 2;
|
||||
assert(Nblock % divisor == 0 && (int)v0.size() >= Nblock / divisor);
|
||||
}
|
||||
assert(Nm % Nblock == 0);
|
||||
assert(Nk % Nblock == 0);
|
||||
assert(Nk < Nm);
|
||||
if (useGamma5) assert(gamma5Func && "useGamma5: gamma5Func must be set");
|
||||
|
||||
int N = Nm;
|
||||
|
||||
RealD approxLambdaMax = approxMaxEval(v0[0]);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: approx max eval = " << approxLambdaMax
|
||||
<< ", rtol = " << rtol
|
||||
<< ", shift = " << shift << std::endl;
|
||||
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
int start = 0;
|
||||
std::vector<Field> startBlock = expandStartBlock(v0);
|
||||
|
||||
for (int iter = 0; iter < MaxIter; iter++) {
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: restart iteration " << iter << std::endl;
|
||||
|
||||
// ---- Block Arnoldi: extend from block 'start' to block Nm/Nblock ----
|
||||
blockArnoldiIteration(startBlock, Nm/Nblock, start, doubleOrthog);
|
||||
std::cout << GridLogMessage << "blockArnoldiIteration done " << std::endl;
|
||||
start = Nk/Nblock;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after Arnoldi";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Harmonic extraction + exact thick restart ----
|
||||
harmonicRestart();
|
||||
std::cout << GridLogMessage << "harmonicRestart done " << std::endl;
|
||||
|
||||
// Restart from the residual block W (exact by identity (3))
|
||||
startBlock = F;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after harmonic restart";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Eigensystem of retained H_k = Y^dag A Y for convergence ----
|
||||
CMat Hk = H(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
computeEigensystem(Hk, Nk);
|
||||
|
||||
int Nconv = converged(Nk);
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: converged " << Nconv
|
||||
<< " / " << Nstop << std::endl;
|
||||
|
||||
if (Nconv >= Nstop || iter == MaxIter - 1) {
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: done after " << iter
|
||||
<< " restarts, " << Nconv << " converged." << std::endl;
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << evals.transpose() << 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 << "TrueHarmonicBlockKrylovSchur: evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Harmonic thick restart
|
||||
//--------------------------------------------------------------------
|
||||
// On entry: A V = V H + F B^dag exact, V = basis (Nm fields), F Nblock
|
||||
// residual fields, H/B the Nm-sized small matrices.
|
||||
// On exit: basis = Y (Nk orthonormal fields spanning the harmonic
|
||||
// Ritz space), F = What (Nblock orthonormal fields, perp Y),
|
||||
// H top-left Nk x Nk = Y^dag A Y (dense), B top Nk rows the
|
||||
// new coupling; relation A Y = Y H_k + F B^dag again exact.
|
||||
void harmonicRestart()
|
||||
{
|
||||
int N = Nm;
|
||||
|
||||
// ---- Small harmonic eigenproblem: Hhat g = (theta - sigma) g ----
|
||||
CMat Hs = H - shift * CMat::Identity(N, N);
|
||||
Eigen::FullPivLU<CMat> lu(Hs.adjoint());
|
||||
assert(lu.isInvertible() &&
|
||||
"TrueHarmonicBlockKrylovSchur: H - shift*I singular; perturb the shift");
|
||||
CMat M = lu.solve(B); // N x Nblock : Hs^{-H} B
|
||||
CMat Hhat = Hs + M * B.adjoint(); // N x N
|
||||
|
||||
Eigen::ComplexEigenSolver<CMat> es(Hhat);
|
||||
|
||||
// Sort harmonic values (theta - sigma) by the Ritz filter
|
||||
// (EvalNormSmall -> closest to the shift).
|
||||
ComplexComparator cComp(ritzFilter);
|
||||
std::vector<int> idx(N);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(es.eigenvalues()(a)), toStdCmplx(es.eigenvalues()(b)));
|
||||
});
|
||||
|
||||
CMat G(N, Nk);
|
||||
CVec theta(Nk);
|
||||
for (int k = 0; k < Nk; k++) {
|
||||
G.col(k) = es.eigenvectors().col(idx[k]);
|
||||
theta(k) = es.eigenvalues()(idx[k]) + toStdCmplx(shift);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: harmonic Ritz values nearest shift:" << std::endl;
|
||||
for (int k = 0; k < Nk; k++)
|
||||
std::cout << GridLogMessage << " [" << k << "] " << theta(k) << std::endl;
|
||||
|
||||
// ---- Orthonormalise the little vectors: G = Gt Rg ----
|
||||
Eigen::HouseholderQR<CMat> qr(G);
|
||||
CMat Gt = qr.householderQ() * CMat::Identity(N, Nk);
|
||||
CMat Rg = qr.matrixQR().topLeftCorner(Nk, Nk).template triangularView<Eigen::Upper>();
|
||||
for (int k = 0; k < Nk; k++)
|
||||
assert(std::abs(Rg(k, k)) > 1e-14 &&
|
||||
"TrueHarmonicBlockKrylovSchur: harmonic vectors linearly dependent");
|
||||
CMat RgInv = Rg.inverse();
|
||||
|
||||
// A (V G) = (V G) diag(theta) + W (B^dag G), W = F - V M
|
||||
// => A (V Gt) = (V Gt) T + W Ct
|
||||
CMat T = Rg * theta.asDiagonal() * RgInv; // Nk x Nk
|
||||
CMat Ct = (B.adjoint() * G) * RgInv; // Nblock x Nk
|
||||
|
||||
// ---- Large-field work ----
|
||||
// Y = V Gt : new orthonormal basis (Nk fields)
|
||||
std::vector<Field> Y;
|
||||
constructThin(Y, basis, Gt, Nk);
|
||||
|
||||
// W = F - V M : Nblock residual directions containing every harmonic
|
||||
// residual (identity (3) in the class comment)
|
||||
std::vector<Field> W = F;
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int j = 0; j < N; j++)
|
||||
W[t] -= basis[j] * M(j, t);
|
||||
|
||||
// Orthogonalise W against Y (two passes), recording P = Y^dag W
|
||||
CMat P = CMat::Zero(Nk, Nblock);
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
for (int j = 0; j < Nk; j++) {
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
ComplexD c = innerProduct(Y[j], W[t]);
|
||||
P(j, t) += toStdCmplx(c);
|
||||
W[t] -= c * Y[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block QR of the projected residual: W <- What, returns Rw
|
||||
CMat Rw = blockQR(W);
|
||||
|
||||
// ---- Reassemble the exact Krylov-Schur relation ----
|
||||
// A Y = Y (T + P Ct) + What (Rw Ct)
|
||||
CMat Hk = T + P * Ct; // Nk x Nk, dense
|
||||
CMat Bc = Rw * Ct; // Nblock x Nk
|
||||
|
||||
H = CMat::Zero(N, N);
|
||||
H(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = Hk;
|
||||
B = CMat::Zero(N, Nblock);
|
||||
for (int j = 0; j < Nk; j++)
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
B(j, t) = std::conj(Bc(t, j));
|
||||
|
||||
basis = Y;
|
||||
F = W;
|
||||
beta_k = Bc.norm();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "TrueHarmonicBlockKrylovSchur: beta_k = " << beta_k << std::endl;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Thin basis combination: out[i] = sum_j U[j] * R(j,i), i < ncol
|
||||
//--------------------------------------------------------------------
|
||||
void constructThin(std::vector<Field>& out, const std::vector<Field>& U,
|
||||
const CMat& R, int ncol)
|
||||
{
|
||||
out.clear();
|
||||
Field tmp(Grid_);
|
||||
for (int i = 0; i < ncol; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)U.size(); j++)
|
||||
tmp += U[j] * R(j, i);
|
||||
out.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_TRUE_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
@@ -334,13 +334,13 @@ int main (int argc, char ** argv)
|
||||
|
||||
// Run KrylovSchur and Arnoldi on a Hermitian matrix
|
||||
RealD shift=LanParams.shift;
|
||||
#if 0
|
||||
#if 1
|
||||
std::cout << GridLogMessage << "Running Krylov Schur" << std::endl;
|
||||
KrylovSchur KrySchur (Dwilson, UGrid, resid,EvalImNormSmall);
|
||||
// KrySchur(src[0], maxIter, Nm, Nk, Nstop);
|
||||
KrySchur.doEvalCheck=true;
|
||||
// KrySchur(src[0], maxIter, Nm, Nk, Nstop,&shift);
|
||||
KrySchur(src[0], maxIter, Nm, Nk, Nstop);
|
||||
KrySchur(src[0], maxIter, Nm, Nk, Nstop,&shift);
|
||||
// KrySchur(src[0], maxIter, Nm, Nk, Nstop);
|
||||
std::cout << GridLogMessage << "KrylovSchur evec.size= " << KrySchur.evecs.size()<< std::endl;
|
||||
#else
|
||||
std::cout << GridLogMessage << "Running BlockKrylovSchur" << std::endl;
|
||||
@@ -349,10 +349,19 @@ int main (int argc, char ** argv)
|
||||
bool if_verify=false;
|
||||
if(LanParams.verify) if_verify=true;
|
||||
// BlockKrylovSchur KrySchur (Dwilson, UGrid, resid,EvalImNormSmall);
|
||||
HarmonicBlockKrylovSchur KrySchur (Dwilson, UGrid, resid,shift,EvalNormSmall);
|
||||
bool useTrueHarmonic = GridCmdOptionExists(argv, argv+argc, std::string("--true-harmonic"));
|
||||
HarmonicBlockKrylovSchur<FermionField> KrySchurShift (Dwilson, UGrid, resid,shift,EvalImNormSmall);
|
||||
TrueHarmonicBlockKrylovSchur<FermionField> KrySchurTrue (Dwilson, UGrid, resid,shift,EvalImNormSmall);
|
||||
HarmonicBlockKrylovSchur<FermionField>& KrySchur = useTrueHarmonic
|
||||
? static_cast<HarmonicBlockKrylovSchur<FermionField>&>(KrySchurTrue)
|
||||
: KrySchurShift;
|
||||
std::cout << GridLogMessage
|
||||
<< (useTrueHarmonic ? "Using TrueHarmonicBlockKrylovSchur (harmonic Ritz)"
|
||||
: "Using HarmonicBlockKrylovSchur (shift-sorted Ritz)")
|
||||
<< std::endl;
|
||||
KrySchur.doEvalCheck=true;
|
||||
KrySchur.useParityFlip=true; std::cout << GridLogMessage << "useParityFlip= " <<KrySchur.useParityFlip<< std::endl;
|
||||
KrySchur.useGamma5=true; std::cout << GridLogMessage << "useGamma5= " <<KrySchur.useGamma5<< std::endl;
|
||||
// KrySchur.useParityFlip=true; std::cout << GridLogMessage << "useParityFlip= " <<KrySchur.useParityFlip<< std::endl;
|
||||
// KrySchur.useGamma5=true; std::cout << GridLogMessage << "useGamma5= " <<KrySchur.useGamma5<< std::endl;
|
||||
KrySchur.gamma5Func = [](const FermionField& v, FermionField& out) {
|
||||
Gamma g5(Gamma::Algebra::Gamma5);
|
||||
out = g5 * v;
|
||||
|
||||
Reference in New Issue
Block a user