mirror of
https://github.com/paboyle/Grid.git
synced 2026-07-21 19:13:28 +01:00
Compare commits
95
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01c3189d17 | ||
|
|
f1249685aa | ||
|
|
722d68b0d9 | ||
|
|
c1284e080a | ||
|
|
d2189d59e0 | ||
|
|
e862dd662b | ||
|
|
42ff77cec8 | ||
|
|
d11ad4d2f0 | ||
|
|
9ebf2fec79 | ||
|
|
ab0d6d3706 | ||
|
|
4aa96a16c5 | ||
|
|
bb99123e6d | ||
|
|
9e8cba3944 | ||
|
|
d5cab295b2 | ||
|
|
5a67f0602c | ||
|
|
d57c49d73d | ||
|
|
5754c965d5 | ||
|
|
64ee6e90c9 | ||
|
|
7752e7ead1 | ||
|
|
f14be5e331 | ||
|
|
5a77a4a052 | ||
|
|
83b752bdd3 | ||
|
|
34a6ba3475 | ||
|
|
819f706778 | ||
|
|
3b28e3c98f | ||
|
|
a696953485 | ||
|
|
aecc50869c | ||
|
|
99be36c891 | ||
|
|
af1fb41d55 | ||
|
|
b4c77ca762 | ||
|
|
82a8f1342e | ||
|
|
d4b685cf2d | ||
|
|
84707cc3a8 | ||
|
|
88ea24687f | ||
|
|
167f94e86c | ||
|
|
09aa843984 | ||
|
|
24752002fa | ||
|
|
f3223021fd | ||
|
|
4e1d95d3bb | ||
|
|
2ed38f63ca | ||
|
|
80d2a8d88d | ||
|
|
2ac5431401 | ||
|
|
3e71cac7ae | ||
|
|
e8e7ef08fc | ||
|
|
5c00fe6bef | ||
|
|
3175788f97 | ||
|
|
6f1788bb38 | ||
|
|
dcda74f924 | ||
|
|
df4c2a082b | ||
|
|
88611659a3 | ||
|
|
504b85dfc0 | ||
|
|
43ea83e5e1 | ||
|
|
376150c3df | ||
|
|
842e0391e7 | ||
|
|
17e3799bcc | ||
|
|
985ab70f85 | ||
|
|
1e85081986 | ||
|
|
3876fe5a29 | ||
|
|
6692425aa2 | ||
|
|
d5ac4fc67f | ||
|
|
3538faf449 | ||
|
|
0b457b9d52 | ||
|
|
fe0ab5f1a9 | ||
|
|
caa66418bd | ||
|
|
786496f22e | ||
|
|
68af1bba67 | ||
|
|
bf2a715ef7 | ||
|
|
4042ebf1bf | ||
|
|
82f35001ff | ||
|
|
fa30c791aa | ||
|
|
612049f1dd | ||
|
|
0b92ef990c | ||
|
|
82d411ca7b | ||
|
|
597086a031 | ||
|
|
b210ddf9a7 | ||
|
|
c5d02e5799 | ||
|
|
9dcd7ca761 | ||
|
|
c1e5ef9476 | ||
|
|
6fd71aea9d | ||
|
|
a18b0d496c | ||
|
|
19f0737b98 | ||
|
|
16d3c9cf75 | ||
|
|
3b9fc72451 | ||
|
|
99644f5d0a | ||
|
|
7780d88d26 | ||
|
|
2bf9179d2c | ||
|
|
c606f5dca0 | ||
|
|
632f5916c7 | ||
|
|
9057694895 | ||
|
|
5e85aef19d | ||
|
|
2b6d40c7e1 | ||
|
|
33b80c4e8e | ||
|
|
8419cc5c64 | ||
|
|
2cc6deb8e0 | ||
|
|
19d0590579 |
@@ -55,6 +55,7 @@ NAMESPACE_CHECK(approx);
|
||||
#include <Grid/algorithms/blas/MomentumProject.h>
|
||||
NAMESPACE_CHECK(deflation);
|
||||
#include <Grid/algorithms/iterative/ConjugateGradient.h>
|
||||
//#include <Grid/algorithms/iterative/EigCG.h>
|
||||
NAMESPACE_CHECK(ConjGrad);
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
NAMESPACE_CHECK(BiCGSTAB);
|
||||
@@ -75,6 +76,7 @@ NAMESPACE_CHECK(BiCGSTAB);
|
||||
#include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h>
|
||||
#include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h>
|
||||
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
|
||||
#include <Grid/algorithms/iterative/SimpleLanczos.h>
|
||||
#include <Grid/algorithms/iterative/PowerMethod.h>
|
||||
#include <Grid/algorithms/iterative/AdefGeneric.h>
|
||||
#include <Grid/algorithms/iterative/AdefMrhs.h>
|
||||
@@ -83,4 +85,17 @@ NAMESPACE_CHECK(PowerMethod);
|
||||
NAMESPACE_CHECK(multigrid);
|
||||
#include <Grid/algorithms/FFT.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/KrylovSchur.h>
|
||||
#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>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/Arnoldi.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@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_ARNOLDI_H
|
||||
#define GRID_ARNOLDI_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
//Moved to KrylovSchur
|
||||
#if 0
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* Options for which Ritz values to keep in implicit restart.
|
||||
*/
|
||||
enum RitzFilter {
|
||||
EvalNormSmall, // Keep evals with smallest norm
|
||||
EvalNormLarge, // Keep evals with largest norm
|
||||
EvalReSmall, // Keep evals with smallest real part
|
||||
EvalReLarge // Keep evals with largest real part
|
||||
};
|
||||
|
||||
// Select comparison function from RitzFilter
|
||||
struct ComplexComparator
|
||||
{
|
||||
RitzFilter f;
|
||||
ComplexComparator (RitzFilter _f) : f(_f) {}
|
||||
bool operator()(std::complex<double> z1, std::complex<double> z2) {
|
||||
switch (f) {
|
||||
RealD tmp1, tmp2;
|
||||
tmp1=std::abs(std::imag(z1));
|
||||
tmp2=std::abs(std::imag(z2));
|
||||
case EvalNormSmall:
|
||||
return std::abs(z1) < std::abs(z2);
|
||||
case EvalNormLarge:
|
||||
return std::abs(z1) > std::abs(z2);
|
||||
// Terrible hack
|
||||
// return std::abs(std::real(z1)) < std::abs(std::real(z2));
|
||||
// if ( std::abs(std::real(z1)) >4.) tmp1 +=1.;
|
||||
// if ( std::abs(std::real(z2)) >4.) tmp2 +=1.;
|
||||
case EvalReSmall:
|
||||
return tmp1 < tmp2;
|
||||
// return std::abs(std::imag(z1)) < std::abs(std::imag(z2));
|
||||
case EvalReLarge:
|
||||
return tmp1 > tmp2;
|
||||
// return std::abs(std::real(z1)) > std::abs(std::real(z2));
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
=======
|
||||
>>>>>>> 68af1bba67dd62881ead5ab1e54962a5486a0791
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of the Arnoldi algorithm.
|
||||
*/
|
||||
template<class Field>
|
||||
class Arnoldi {
|
||||
|
||||
private:
|
||||
|
||||
std::string cname = std::string("Arnoldi");
|
||||
int MaxIter; // Max iterations
|
||||
RealD Tolerance;
|
||||
RealD ssq;
|
||||
RealD rtol;
|
||||
int Nm; // Number of basis vectors to track (equals MaxIter if no restart)
|
||||
int Nk; // Number of basis vectors to keep every restart (equals -1 if no restart)
|
||||
int Nstop; // Stop after converging Nstop eigenvectors.
|
||||
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
RealD approxLambdaMax;
|
||||
RealD beta_k;
|
||||
Field f;
|
||||
std::vector<Field> basis; // orthonormal Arnoldi basis
|
||||
Eigen::MatrixXcd Hess; // Hessenberg matrix of size Nbasis (after construction)
|
||||
Eigen::MatrixXcd Qt; // Transpose of basis rotation which projects out high modes.
|
||||
|
||||
Eigen::VectorXcd evals; // evals of Hess
|
||||
Eigen::MatrixXcd littleEvecs; // Nm x Nm evecs matrix
|
||||
std::vector<Field> evecs; // Vector of evec fields
|
||||
|
||||
RitzFilter ritzFilter; // how to sort evals
|
||||
|
||||
public:
|
||||
|
||||
Arnoldi(LinearOperatorBase<Field> &_Linop, GridBase *_Grid, RealD _Tolerance, RitzFilter filter = EvalReSmall)
|
||||
: Linop(_Linop), Grid(_Grid), Tolerance(_Tolerance), ritzFilter(filter), f(_Grid), MaxIter(-1), Nm(-1), Nk(-1),
|
||||
Nstop (-1), evals (0), evecs (), ssq (0.0), rtol (0.0), beta_k (0.0), approxLambdaMax (0.0)
|
||||
{
|
||||
f = Zero();
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the Arnoldi loop with(out) implicit restarting. For each iteration:
|
||||
* - Runs an Arnoldi step.
|
||||
* - Computes the eigensystem of the Hessenberg matrix.
|
||||
* - Performs implicit restarting.
|
||||
*/
|
||||
void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, bool doubleOrthog = false) {
|
||||
MaxIter = _maxIter;
|
||||
Nm = _Nm; Nk = _Nk;
|
||||
Nstop = _Nstop;
|
||||
|
||||
ssq = norm2(v0);
|
||||
RealD approxLambdaMax = approxMaxEval(v0);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
|
||||
ComplexComparator compareComplex (ritzFilter);
|
||||
std::cout << GridLogMessage << "Comparing Ritz values with: " << ritzFilter << std::endl;
|
||||
|
||||
int start = 1;
|
||||
Field startVec = v0;
|
||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
for (int i = 0; i < MaxIter; i++) {
|
||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
||||
|
||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
||||
startVec = f;
|
||||
|
||||
// compute eigensystem and sort evals
|
||||
// compute_eigensystem();
|
||||
compute_eigensystem(Hess);
|
||||
std::cout << GridLogMessage << "Eigenvalues after Arnoldi step: " << std::endl << evals << std::endl;
|
||||
|
||||
std::sort(evals.begin(), evals.end(), compareComplex);
|
||||
std::cout << GridLogMessage << "Ritz values after sorting (first Nk preserved): " << std::endl << evals << std::endl;
|
||||
// SU(N)::tepidConfiguration
|
||||
|
||||
// Implicit restart to de-weight unwanted eigenvalues
|
||||
implicitRestart(_Nm, _Nk); // probably can delete _Nm and _Nk from function args
|
||||
start = Nk;
|
||||
|
||||
// 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);
|
||||
std::cout << GridLogMessage << "Eigenvalues [first " << Nconv << " converged]: " << std::endl << evals << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the maximum eigenvalue of Linop.Op to normalize the residual and test for convergence.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Field& v0
|
||||
* Source field to start with. Must have non-zero norm.
|
||||
* int MAX_ITER (default = 50)
|
||||
* Maximum number of iterations for power approximation.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* RealD lamApprox
|
||||
* Approximation of largest eigenvalue.
|
||||
*/
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50) {
|
||||
assert (norm2(v0) > 1e-8); // must have relatively large source norm to start
|
||||
RealD lamApprox = 0.0;
|
||||
RealD denom = 1.0; RealD num = 1.0;
|
||||
Field v0cp (Grid); Field tmp (Grid);
|
||||
v0cp = v0;
|
||||
denom = std::sqrt(norm2(v0cp));
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(v0cp, tmp); // CAREFUL: do not do Op(tmp, tmp)
|
||||
v0cp = tmp;
|
||||
num = std::sqrt(norm2(v0cp)); // num = |A^{n+1} v0|
|
||||
lamApprox = num / denom; // lam = |A^{n+1} v0| / |A^n v0|
|
||||
std::cout << GridLogDebug << "Approx for max eval: " << lamApprox << std::endl;
|
||||
denom = num; // denom = |A^{n} v0|
|
||||
}
|
||||
return lamApprox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the Arnoldi basis for the Krylov space K_n(D, src). (TODO make private)
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* v0 : Field&
|
||||
* Source to generate Krylov basis.
|
||||
* Nm : int
|
||||
* Final size of the basis desired. If the basis becomes complete before a basis of size Nm is constructed
|
||||
* (determined by relative tolerance Tolerance), stops iteration there.
|
||||
* doubleOrthog : bool (default = false)
|
||||
* Whether to double orthogonalize the basis (for numerical cancellations) or not.
|
||||
* start : int (default = 0)
|
||||
* If non-zero, assumes part of the Arnoldi basis has already been constructed.
|
||||
*/
|
||||
void arnoldiIteration(const Field& v0, int Nm, int start = 1, bool doubleOrthog = false)
|
||||
{
|
||||
|
||||
ComplexD coeff;
|
||||
Field w (Grid); // A acting on last Krylov vector.
|
||||
|
||||
if (start == 1) { // initialize everything that we need.
|
||||
RealD v0Norm = 1 / std::sqrt(ssq);
|
||||
basis.push_back(v0Norm * v0); // normalized source
|
||||
|
||||
Hess = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
f = Zero();
|
||||
} else {
|
||||
assert( start == basis.size() ); // should be starting at the end of basis (start = Nk)
|
||||
Eigen::MatrixXcd HessCp = Hess;
|
||||
Hess = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
Hess(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = HessCp;
|
||||
}
|
||||
|
||||
// Construct next Arnoldi vector by normalizing w_i = Dv_i - \sum_j v_j h_{ji}
|
||||
for (int i = start - 1; i < Nm; i++) {
|
||||
|
||||
Linop.Op(basis.back(), w);
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
coeff = innerProduct(basis[j], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after.
|
||||
Hess(j, i) = coeff;
|
||||
w -= coeff * basis[j];
|
||||
}
|
||||
|
||||
if (doubleOrthog) {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
// add w_i to the pile
|
||||
if (i < Nm - 1) {
|
||||
coeff = std::sqrt(norm2(w));
|
||||
Hess(i+1, i) = coeff;
|
||||
basis.push_back(
|
||||
(1.0/coeff) * w
|
||||
);
|
||||
}
|
||||
|
||||
// after iterations, update f and beta_k = ||f||
|
||||
f = w; // make sure f is not normalized
|
||||
beta_k = std::sqrt(norm2(f)); // beta_k = ||f_k|| determines convergence.
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "|f|^2 after Arnoldi step = " << norm2(f) << std::endl;
|
||||
std::cout << GridLogDebug << "Computed Hessenberg matrix = " << std::endl << Hess << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the eigensystem of the linear operator by computing the eigensystem of
|
||||
* the Hessenberg matrix. Assumes that the Hessenberg matrix has already been constructed (by
|
||||
* calling the operator() function).
|
||||
*
|
||||
* TODO implement in parent class eventually.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Eigen::MatrixXcd& S
|
||||
* Schur matrix (upper triangular) similar to original Rayleigh quotient.
|
||||
*/
|
||||
void compute_eigensystem(Eigen::MatrixXcd& S)
|
||||
{
|
||||
|
||||
std::cout << GridLogMessage << "Computing eigenvalues." << std::endl;
|
||||
|
||||
evecs.clear();
|
||||
|
||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
||||
es.compute(S);
|
||||
evals = es.eigenvalues();
|
||||
littleEvecs = es.eigenvectors();
|
||||
|
||||
// Convert evecs to lattice fields
|
||||
for (int k = 0; k < evals.size(); k++) {
|
||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
||||
Field tmp (basis[0].Grid());
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
tmp = tmp + vec[j] * basis[j];
|
||||
}
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << std::endl << evals << std::endl;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the factorization DV = V^\dag H + f e^\dag with the last-computed
|
||||
* V, H, f.
|
||||
*/
|
||||
// RealD verifyFactorization() {
|
||||
// int k = basis.size(); // number of basis vectors, also the size of H.
|
||||
// std::vector<Field> factorized (k, Zero());
|
||||
// Field tmp (FGrid); tmp = Zero();
|
||||
// for (int i = 0; i < basis.size(); i++) {
|
||||
// Linop.Op(basis[i], tmp);
|
||||
// }
|
||||
// // basisRotate(basis, Q, 0, Nk, 0, Nk, Nm);
|
||||
// // Linop.Op(, )
|
||||
// }
|
||||
|
||||
/* Getters */
|
||||
Eigen::MatrixXcd getHessenbergMat() { return Hess; }
|
||||
Field getF() { return f; }
|
||||
std::vector<Field> getBasis() { return basis; }
|
||||
Eigen::VectorXcd getEvals() { return evals; }
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
|
||||
/**
|
||||
* Implements implicit restarting for Arnoldi. Assumes eigenvalues are sorted.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* int _Nm
|
||||
* Size of basis to keep (Hessenberg is MxM).
|
||||
* int Nk
|
||||
* Number of basis vectors to keep at each restart.
|
||||
*/
|
||||
void implicitRestart(int _Nm, int _Nk) {
|
||||
assert ( _Nk <= _Nm );
|
||||
Nm = _Nm; Nk = _Nk;
|
||||
int Np = Nm - Nk; // keep Nk smallest (or largest, depends on sort function) evecs
|
||||
|
||||
std::cout << GridLogMessage << "Computing QR Factorizations." << std::endl;
|
||||
|
||||
Eigen::MatrixXcd Q = Eigen::MatrixXcd::Identity(Nm, Nm);
|
||||
Eigen::MatrixXcd Qi (Nm, Nm);
|
||||
Eigen::MatrixXcd R (Nm, Nm);
|
||||
|
||||
for (int i = Nk; i < Nm; i++) { // keep the first Nk eigenvalues and iterate through the last Np. Should loop Np times
|
||||
|
||||
// Useful debugging output
|
||||
std::cout << GridLogDebug << "Computing QR factorization for i = " << i << std::endl;
|
||||
std::cout << GridLogDebug << "Eval shift = " << evals[i] << std::endl;
|
||||
std::cout << GridLogDebug << "Hess before rotation: " << Hess << std::endl;
|
||||
|
||||
// QR factorize
|
||||
Eigen::HouseholderQR<Eigen::MatrixXcd> QR (Hess - evals[i] * Eigen::MatrixXcd::Identity(Nm, Nm));
|
||||
Qi = QR.householderQ();
|
||||
Q = Q * Qi;
|
||||
Hess = Qi.adjoint() * Hess * Qi;
|
||||
|
||||
std::cout << GridLogDebug << "Qt up to i = " << Q.transpose() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
std::cout << GridLogDebug << "Hess after all rotations: " << std::endl << Hess << std::endl;
|
||||
|
||||
// form Arnoldi vector f: f is normal to the basis vectors and its norm \beta is used to determine the Ritz estimate.
|
||||
std::complex<double> beta = Hess(Nk, Nk-1);
|
||||
std::complex<double> sigma = Q(Nm-1, Nk-1);
|
||||
f = basis[Nk] * beta + f * sigma;
|
||||
RealD betak = std::sqrt(norm2(f));
|
||||
std::cout << GridLogMessage << "|f|^2 after implicit restart = " << norm2(f) << std::endl;
|
||||
|
||||
// Rotate basis by Qt
|
||||
Qt = Q.transpose();
|
||||
basisRotate(basis, Qt, 0, Nk + 1, 0, Nm, Nm);
|
||||
|
||||
// rotate
|
||||
basisRotate(evecs, Qt, 0, Nk + 1, 0, Nm, Nm);
|
||||
|
||||
// Truncate the basis and restart
|
||||
basis = std::vector<Field> (basis.begin(), basis.begin() + Nk);
|
||||
// evecs = std::vector<Field> (evecs.begin(), evecs.begin() + Nk);
|
||||
Hess = Hess(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
|
||||
std::cout << "evecs size: " << evecs.size() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of Arnoldi eigenvectors that have converged. An eigenvector s is considered converged
|
||||
* for a tolerance epsilon if
|
||||
* r(s) := |\beta e_m^T s| < epsilon
|
||||
* where beta is the norm of f_{m+1}.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* int : Number of converged eigenvectors.
|
||||
*/
|
||||
int converged() {
|
||||
int Nconv = 0;
|
||||
for (int k = 0; k < evecs.size(); k++) {
|
||||
RealD emTs = std::abs(littleEvecs(Nm - 1, k)); // e_m^T s
|
||||
RealD ritzEstimate = beta_k * emTs;
|
||||
// TODO should be ritzEstimate < Tolerance * lambda_max
|
||||
std::cout << GridLogMessage << "Ritz estimate for evec " << k << " = " << ritzEstimate << std::endl;
|
||||
if (ritzEstimate < rtol) {
|
||||
Nconv++;
|
||||
}
|
||||
}
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -0,0 +1,878 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/BlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
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_BLOCKED_KRYLOV_SCHUR_H
|
||||
#define GRID_BLOCKED_KRYLOV_SCHUR_H
|
||||
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Block (block-Arnoldi) restarted Krylov-Schur eigensolver for
|
||||
* general non-Hermitian operators.
|
||||
*
|
||||
* Algorithm
|
||||
* ---------
|
||||
* Uses a block Arnoldi factorisation of block size Nblock:
|
||||
*
|
||||
* A V_k = V_k H_k + F_k B_k^dag
|
||||
*
|
||||
* where
|
||||
* V_k = Nm orthonormal basis vectors (stored flat in basis[])
|
||||
* H_k = Nm x Nm upper block-Hessenberg Rayleigh quotient
|
||||
* F_k = Nblock residual vectors (the next block beyond V_k)
|
||||
* B_k = Nm x Nblock coupling matrix (non-zero only in last Nblock rows)
|
||||
*
|
||||
* Each block Arnoldi step applies A to each of the Nblock vectors in the
|
||||
* current block, orthogonalises against all previous basis vectors, and
|
||||
* reduces the residual block to upper-triangular form via Householder QR
|
||||
* (implemented here as modified Gram-Schmidt within the block).
|
||||
*
|
||||
* The restart is a thick restart via the Schur decomposition of H_k:
|
||||
* H_k = Q^dag S Q
|
||||
* The leading Nk Schur vectors (chosen by RitzFilter) are retained,
|
||||
* the basis and Rayleigh quotient are truncated, and block Arnoldi continues
|
||||
* from the (Nk/Nblock)-th block.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Nblock : block size p
|
||||
* Nm : total Krylov dimension (must be divisible by Nblock)
|
||||
* Nk : total vectors to keep after each restart (must be divisible by Nblock, Nk < Nm)
|
||||
* Nstop : declare convergence when this many eigenpairs have converged
|
||||
* MaxIter : maximum number of outer (restart) iterations
|
||||
* Tolerance : relative convergence tolerance (||r|| < Tolerance * |lambda_max|)
|
||||
*/
|
||||
template<class Field>
|
||||
class BlockKrylovSchur {
|
||||
|
||||
protected:
|
||||
//--------------------------------------------------------------------
|
||||
// Types
|
||||
//--------------------------------------------------------------------
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Parameters (set by operator())
|
||||
//--------------------------------------------------------------------
|
||||
int Nblock; // block size
|
||||
int Nm; // total Krylov dimension (multiple of Nblock)
|
||||
int Nk; // total vectors retained after restart (multiple of Nblock)
|
||||
int Nstop;
|
||||
int MaxIter;
|
||||
RealD Tolerance;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Internal state
|
||||
//--------------------------------------------------------------------
|
||||
LinearOperatorBase<Field>& Linop;
|
||||
GridBase* Grid_;
|
||||
RitzFilter ritzFilter;
|
||||
|
||||
// Prefix used in log messages; derived classes override in their constructor
|
||||
// (e.g. "HarmonicBlockKrylovSchur") so the shared code reports the right name.
|
||||
std::string className = "BlockKrylovSchur";
|
||||
|
||||
// Flat storage: basis[s*Nblock + t] is the t-th vector of block s
|
||||
// After construction: basis has Nm entries
|
||||
std::vector<Field> basis;
|
||||
|
||||
// Rayleigh quotient Nm x Nm
|
||||
CMat H;
|
||||
|
||||
// Residual block: Nblock vectors (the (Nm/Nblock+1)-th block, unnormalised before
|
||||
// QR; normalised and orthogonalised as part of block Arnoldi)
|
||||
std::vector<Field> F;
|
||||
|
||||
// Coupling matrix B: Nm x Nblock.
|
||||
// In exact arithmetic only the last Nblock rows are non-zero:
|
||||
// B(Nm - Nblock + t, s) = H_{Nm/Nblock+1, Nm/Nblock}(t, s) (the subdiagonal block)
|
||||
// We keep it as a full matrix for generality after restarts.
|
||||
CMat B;
|
||||
|
||||
RealD beta_k; // Frobenius norm of the last subdiagonal block
|
||||
RealD rtol; // absolute tolerance = Tolerance * approxLambdaMax
|
||||
|
||||
// Output
|
||||
CVec evals;
|
||||
CMat littleEvecs; // Nm columns
|
||||
std::vector<RealD> ritzEstimates;
|
||||
|
||||
public:
|
||||
std::vector<Field> evecs;
|
||||
bool doEvalCheck = false;
|
||||
// When true (and Nblock even), only Nblock/2 starting vectors are required.
|
||||
// The remaining Nblock/2 slots are filled by pairing each supplied vector
|
||||
// with its parity-flipped partner (sign negated on odd checkerboard sites).
|
||||
bool useParityFlip = false;
|
||||
// Same pairing behaviour but the partner is produced by gamma5Func(v).
|
||||
// gamma5Func must be set by the caller before operator() is called.
|
||||
// It is a std::function so that BlockKrylovSchur.h does not need to know
|
||||
// about the Grid QCD Gamma class (which is defined at a later include layer).
|
||||
bool useGamma5 = false;
|
||||
std::function<void(const Field&, Field&)> gamma5Func;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------------------------------------
|
||||
BlockKrylovSchur(LinearOperatorBase<Field>& _Linop, GridBase* _Grid,
|
||||
RealD _Tolerance, RitzFilter _rf = EvalReSmall)
|
||||
: Linop(_Linop), Grid_(_Grid), Tolerance(_Tolerance), ritzFilter(_rf),
|
||||
Nblock(-1), Nm(-1), Nk(-1), Nstop(-1), MaxIter(-1),
|
||||
beta_k(0.0), rtol(0.0)
|
||||
{}
|
||||
|
||||
virtual ~BlockKrylovSchur() = default;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Run the blocked Krylov-Schur algorithm.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* v0 : block of Nblock starting vectors (size >= Nblock)
|
||||
* _maxIter : maximum outer (restart) iterations
|
||||
* _Nm : total Krylov dimension (must be divisible by _Nblock)
|
||||
* _Nk : total vectors to keep after restart (must be divisible by _Nblock, _Nk < _Nm)
|
||||
* _Nstop : stop after _Nstop eigenvalues converged
|
||||
* _Nblock : block size
|
||||
*/
|
||||
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)
|
||||
{
|
||||
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);
|
||||
std::cout << GridLogMessage << className << ": divisor = " << divisor << std::endl;
|
||||
}
|
||||
assert(Nm % Nblock == 0);
|
||||
assert(Nk % Nblock == 0);
|
||||
assert(Nk < Nm);
|
||||
if (useGamma5) assert(gamma5Func && "useGamma5: gamma5Func must be set");
|
||||
preRun(); // hook: derived classes add parameter assertions here
|
||||
|
||||
int N = Nm; // total Krylov dimension
|
||||
|
||||
// Approximate largest eigenvalue for tolerance normalisation
|
||||
RealD approxLambdaMax = approxMaxEval(v0[0]);
|
||||
rtol = Tolerance * approxLambdaMax;
|
||||
std::cout << GridLogMessage << className << ": approx max eval = "
|
||||
<< approxLambdaMax << ", rtol = " << rtol << std::endl;
|
||||
|
||||
// Initialise
|
||||
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 << className << ": restart iteration " << iter << std::endl;
|
||||
|
||||
// ---- Block Arnoldi: extend from block start to block Nm/Nblock ----
|
||||
blockArnoldiIteration(startBlock, Nm/Nblock, start, doubleOrthog);
|
||||
|
||||
// After first full cycle start from block Nk/Nblock
|
||||
start = Nk/Nblock;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after Arnoldi";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Restart rotation: compute Q and the rotated+reordered Rayleigh
|
||||
// quotient Hnew. Base class uses the plain Schur decomposition of
|
||||
// H; derived classes (e.g. HarmonicBlockKrylovSchur) override this
|
||||
// to target a shift.
|
||||
CMat Q, Hnew;
|
||||
restartRotation(Q, Hnew);
|
||||
CMat Qt = Q.adjoint();
|
||||
|
||||
// Rotate Krylov basis: basis_new[i] = sum_j basis[j] * Qt(j,i)
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, N);
|
||||
basis = basis2;
|
||||
|
||||
// Update B and H
|
||||
B = Q * B;
|
||||
H = Hnew;
|
||||
|
||||
// ---- Truncate to Nk ----
|
||||
int Nkeep = Nk;
|
||||
|
||||
CMat Htmp = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
H = CMat::Zero(N, N);
|
||||
H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep)) = Htmp;
|
||||
|
||||
std::vector<Field> basisTmp(basis.begin(), basis.begin() + Nkeep);
|
||||
basis = basisTmp;
|
||||
|
||||
CMat Btmp = B(Eigen::seqN(0, Nkeep), Eigen::all);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
B(Eigen::seqN(0, Nkeep), Eigen::all) = Btmp;
|
||||
|
||||
// beta_k = Frobenius norm of the effective coupling
|
||||
beta_k = Btmp.norm();
|
||||
std::cout << GridLogMessage << className << ": beta_k = " << beta_k << std::endl;
|
||||
|
||||
// Restart: the new starting block is F (the residual block from Arnoldi)
|
||||
startBlock = F;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after restart+truncation";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Compute eigensystem of truncated H for convergence check ----
|
||||
CMat Hk = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
computeEigensystem(Hk, Nkeep);
|
||||
|
||||
int Nconv = converged(Nkeep);
|
||||
std::cout << GridLogMessage << className << ": converged " << Nconv
|
||||
<< " / " << Nstop << std::endl;
|
||||
|
||||
if (Nconv >= Nstop || iter == MaxIter - 1) {
|
||||
std::cout << GridLogMessage << className << ": done after " << iter
|
||||
<< " restarts, " << Nconv << " converged." << std::endl;
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << 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 << className << ": evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
CVec getEvals() { return evals; }
|
||||
std::vector<RealD> getRitzEstimates() { return ritzEstimates; }
|
||||
|
||||
// Negate the field on all odd-parity (checkerboard) sites.
|
||||
// Uses pickCheckerboard/setCheckerboard so the SIMD layout is handled
|
||||
// correctly and the negation runs in parallel.
|
||||
static void parityFlippedField(const Field& v, Field& out) {
|
||||
GridCartesian* cgrid = dynamic_cast<GridCartesian*>(v.Grid());
|
||||
assert(cgrid != nullptr);
|
||||
GridRedBlackCartesian rbgrid(cgrid);
|
||||
Field veven(&rbgrid), vodd(&rbgrid);
|
||||
pickCheckerboard(Even, veven, v);
|
||||
pickCheckerboard(Odd, vodd, v);
|
||||
vodd = -vodd;
|
||||
setCheckerboard(out, veven);
|
||||
setCheckerboard(out, vodd);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Verification: print H and B, check A V = V H + F B^dag explicitly
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Checks the block Arnoldi / Krylov-Schur decomposition
|
||||
*
|
||||
* A V = V H + F B^dag (KS)
|
||||
*
|
||||
* by explicit operator applications. For each basis vector j:
|
||||
*
|
||||
* w_j = A basis[j]
|
||||
*
|
||||
* The nBasis × nBasis matrix M of inner products is computed:
|
||||
*
|
||||
* M[i, j] = <basis[i] | A basis[j]>
|
||||
*
|
||||
* and compared column-by-column against H. Separately, the nBasis × Nblock
|
||||
* residual coupling matrix R is computed:
|
||||
*
|
||||
* R[j, t] = <basis[j] | F[t]> * ||F[t]|| (scaled by F-block norms)
|
||||
*
|
||||
* but since F is already normalised, R[j,t] = <basis[j] | F[t]>.
|
||||
*
|
||||
* The KS relation for column j reads:
|
||||
* w_j = sum_i basis[i] H[i,j] + sum_t F[t] B[j,t]*
|
||||
* so the deviation in column j is
|
||||
* dev_j = w_j - sum_i basis[i] M[i,j] (should be zero for exact arithmetic)
|
||||
* augmented by the F B^dag term in the last block.
|
||||
*
|
||||
* Prints:
|
||||
* - H (current Rayleigh quotient, nBasis × nBasis)
|
||||
* - B (coupling matrix, nBasis × Nblock)
|
||||
* - M (explicit inner product matrix <V | A V>)
|
||||
* - max |H[i,j] - M[i,j]| (should be O(machine epsilon))
|
||||
* - for each basis column j: || A v_j - V H[:,j] - F B[j,:]^* ||
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* label : string printed at the start (e.g. "after restart 2")
|
||||
*/
|
||||
void verify(const std::string& label = "")
|
||||
{
|
||||
int nBasis = (int)basis.size();
|
||||
int nF = (int)F.size();
|
||||
|
||||
if (nBasis == 0) {
|
||||
std::cout << GridLogMessage
|
||||
<< className << "::verify [" << label
|
||||
<< "]: basis is empty." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Full allocated dimension of H (= Nm); may be larger than nBasis after restart+truncation
|
||||
int Nfull = (int)H.rows();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== " << className << "::verify [" << label << "] ========" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " nBasis = " << nBasis << " Nfull = " << Nfull
|
||||
<< " Nblock = " << Nblock << " nF = " << nF << std::endl;
|
||||
|
||||
// ---- Print H (full Nm x Nm matrix) ----
|
||||
// After restart+truncation nBasis < Nfull; entries outside [0:nBasis,0:nBasis]
|
||||
// should be ~0 — printing the full matrix makes this visible.
|
||||
std::cout << GridLogMessage << "H (" << Nfull << " x " << Nfull << "):" << std::endl;
|
||||
for (int i = 0; i < Nfull; i++) {
|
||||
for (int j = 0; j < Nfull; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << H(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Print B (full Nm x Nblock matrix) ----
|
||||
std::cout << GridLogMessage << "B (" << Nfull << " x " << nF << "):" << std::endl;
|
||||
for (int i = 0; i < Nfull; i++) {
|
||||
for (int t = 0; t < nF; t++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << B(i, t);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Compute M[i,j] = <basis[i] | A basis[j]> ----
|
||||
CMat M = CMat::Zero(nBasis, nBasis);
|
||||
Field w(Grid_);
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
M(i, j) = toStdCmplx(innerProduct(basis[i], w));
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "M = <V|AV> (" << nBasis << " x " << nBasis << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << M(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- max |H - M| (for the nBasis x nBasis Rayleigh-quotient block) ----
|
||||
RealD maxHM = 0.0;
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
maxHM = std::max(maxHM, std::abs(H(i,j) - M(i,j)));
|
||||
std::cout << GridLogMessage
|
||||
<< " max |H[i,j] - M[i,j]| (i,j < nBasis) = " << maxHM << std::endl;
|
||||
|
||||
// ---- Check entries of H OUTSIDE the nBasis x nBasis block ----
|
||||
// These live in rows i >= nBasis or columns j >= nBasis.
|
||||
// They must be ~0 after restart+truncation (before the coupling rows are
|
||||
// re-filled by the next blockArnoldiIteration call).
|
||||
// During a full Arnoldi run (nBasis == Nfull) this loop is a no-op.
|
||||
RealD maxOutside = 0.0;
|
||||
for (int i = 0; i < Nfull; i++)
|
||||
for (int j = 0; j < Nfull; j++)
|
||||
if (i >= nBasis || j >= nBasis)
|
||||
maxOutside = std::max(maxOutside, std::abs(H(i,j)));
|
||||
if (Nfull > nBasis)
|
||||
std::cout << GridLogMessage
|
||||
<< " max |H[i,j]| outside [0:" << nBasis << ",0:" << nBasis
|
||||
<< "] block = " << maxOutside
|
||||
<< " (should be ~0 after restart+truncation)" << std::endl;
|
||||
|
||||
// ---- Check orthonormality of basis ----
|
||||
CMat G = CMat::Zero(nBasis, nBasis);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
G(i, j) = toStdCmplx(innerProduct(basis[i], basis[j]));
|
||||
CMat Gerr = G - CMat::Identity(nBasis, nBasis);
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i|V_j> - delta_ij| = " << Gerr.cwiseAbs().maxCoeff() << std::endl;
|
||||
|
||||
// ---- Per-column residual: || A v_j - V H[:,j] - F B[j,:]^* || ----
|
||||
// For each basis vector j, compute A v_j then subtract V H[:,j] and F B[j,:]^*
|
||||
RealD maxColDev = 0.0;
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
|
||||
// subtract V H[:,j]
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
w -= basis[i] * H(i, j);
|
||||
|
||||
// subtract F B[j,:]^* (F[t] * conj(B[j,t]))
|
||||
for (int t = 0; t < nF; t++)
|
||||
w -= F[t] * std::conj(B(j, t));
|
||||
|
||||
RealD dev = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage
|
||||
<< " || A v[" << j << "] - V H[:,j] - F B[j,:]* || = " << dev << std::endl;
|
||||
maxColDev = std::max(maxColDev, dev);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max column deviation = " << maxColDev << std::endl;
|
||||
|
||||
// ---- Check F block orthogonality against basis ----
|
||||
if (nF > 0) {
|
||||
RealD maxFV = 0.0;
|
||||
for (int t = 0; t < nF; t++)
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
RealD ip = std::abs(toStdCmplx(innerProduct(basis[i], F[t])));
|
||||
maxFV = std::max(maxFV, ip);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i | F_t>| (should be ~0) = " << maxFV << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== end verify ========" << std::endl;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Extension hooks for derived classes
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Apply the operator (or poly-filtered operator) to the current Arnoldi
|
||||
* block and return the results in W.
|
||||
*
|
||||
* Base implementation: W[t] = Linop.Op(basis[kBase + t]).
|
||||
* Derived classes (e.g. SplitGridBlockKrylovSchur) override this to
|
||||
* apply poly(A) via Grid_split / Grid_unsplit instead.
|
||||
*/
|
||||
virtual void applyBlock(std::vector<Field>& W, int kBase)
|
||||
{
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
Linop.Op(basis[kBase + t], W[t]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once at the start of operator(), after parameters are set.
|
||||
* Base implementation is a no-op; derived classes override to add
|
||||
* parameter assertions (e.g. Nblock % mrhs == 0).
|
||||
*/
|
||||
virtual void preRun() {}
|
||||
|
||||
/**
|
||||
* Compute the restart rotation for the current Rayleigh quotient H.
|
||||
*
|
||||
* Returns Q (unitary) and Hnew = Q H Q^dag reordered so that the wanted
|
||||
* Nk eigenvalues occupy the leading block, ready for truncation.
|
||||
*
|
||||
* Base implementation: the plain Schur decomposition of H, reordered by
|
||||
* ritzFilter. HarmonicBlockKrylovSchur overrides this to Schur-decompose
|
||||
* the shifted quotient (H - sigma I) so that eigenvalues nearest sigma are
|
||||
* selected.
|
||||
*/
|
||||
virtual void restartRotation(CMat& Q, CMat& Hnew)
|
||||
{
|
||||
ComplexSchurDecomposition schur(H, false, ritzFilter);
|
||||
std::cout << GridLogMessage << className << ": Schur decomposed." << std::endl;
|
||||
schur.schurReorder(Nk);
|
||||
std::cout << GridLogMessage << className << ": Schur reordered." << std::endl;
|
||||
Q = schur.getMatrixQ();
|
||||
Hnew = schur.getMatrixS();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Starting-block expansion (parity flip / gamma5 partners)
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Expand Nblock/divisor seed vectors into the full Nblock starting block by
|
||||
* pairing each seed with its parity-flipped and/or gamma5 partners. Order:
|
||||
* {v, flip(v)} then doubled by {., g5(.)} giving {v, flip(v), g5(v),
|
||||
* g5(flip(v))} when both flags are set. 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;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block Arnoldi iteration
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Extends the block Arnoldi factorisation from block index 'start' to
|
||||
* block index 'Nm'.
|
||||
*
|
||||
* On entry (start > 0): basis[0..start*Nblock-1] already set,
|
||||
* H[0..start*Nblock-1, 0..start*Nblock-1] already set,
|
||||
* B[start*Nblock-1, :] set (coupling from prior residual block).
|
||||
* startBlock = the normalised residual block F from the previous cycle.
|
||||
*
|
||||
* On entry (start == 0): initialises everything from startBlock.
|
||||
*/
|
||||
void blockArnoldiIteration(std::vector<Field>& startBlock, int endBlock,
|
||||
int startIdx, bool doubleOrthog)
|
||||
{
|
||||
int N = Nm;
|
||||
|
||||
if (startIdx == 0) {
|
||||
basis.clear();
|
||||
F.clear();
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
// Orthonormalise starting block via modified Gram-Schmidt
|
||||
std::vector<Field> V0 = startBlock;
|
||||
blockOrthonormalise(V0);
|
||||
for (auto& v : V0) basis.push_back(v);
|
||||
} else {
|
||||
// Append residual block (startBlock = F_old) to basis.
|
||||
// The truncated KS relation after restart is:
|
||||
//
|
||||
// A V_k = V_k S_k + F_old B_old^dag (*)
|
||||
//
|
||||
// where V_k = basis[0:Nkeep], S_k is stored in H[0:Nkeep,0:Nkeep],
|
||||
// B_old = B[0:Nkeep,:], F_old = startBlock.
|
||||
//
|
||||
// Once F_old is appended as basis[Nkeep:Nkeep+Nblock], (*) becomes
|
||||
// a statement about the extended H matrix:
|
||||
//
|
||||
// H[Nkeep+t, j] = (B_old^dag)[t,j] = conj(B_old[j,t])
|
||||
// for t=0..Nblock-1, j=0..Nkeep-1
|
||||
//
|
||||
// These entries are the "restart coupling rows" that connect the new
|
||||
// block to all retained Schur vectors and must be set before Arnoldi
|
||||
// continues, otherwise A V_k = V_k H[:,0:Nkeep] would be missing the
|
||||
// F_old B_old^dag term for those columns.
|
||||
|
||||
int Nkeep = startIdx * Nblock;
|
||||
for (auto& v : startBlock) basis.push_back(v);
|
||||
|
||||
// Fill restart coupling rows into H
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
H(Nkeep + t, j) = std::conj(B(j, t));
|
||||
|
||||
// Zero out B for the retained columns now that the coupling is in H
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
B(j, t) = 0.0;
|
||||
}
|
||||
|
||||
// Main block Arnoldi loop
|
||||
for (int k = startIdx; k < endBlock; k++) {
|
||||
blockArnoldiStep(k, doubleOrthog);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* One block Arnoldi step: extends by one block (Nblock vectors).
|
||||
*
|
||||
* Computes block column k of H and the next basis block V_{k+1}.
|
||||
*
|
||||
* Layout of basis (flat):
|
||||
* basis[j*Nblock + t] = t-th vector of j-th block, j = 0..k
|
||||
*
|
||||
* After this call:
|
||||
* H[i, k*Nblock : (k+1)*Nblock] filled for i = 0..(k+1)*Nblock - 1
|
||||
* basis[k*Nblock .. (k+1)*Nblock - 1] normalised (already set on entry)
|
||||
* F = residual block (to become V_{k+1} after this step if k < Nm-1)
|
||||
*
|
||||
* If k < Nm-1, also:
|
||||
* H[(k+1)*Nblock : (k+2)*Nblock, k*Nblock : (k+1)*Nblock] = subdiag block (from QR of residual)
|
||||
* basis extended by Nblock (the normalised residual vectors)
|
||||
*/
|
||||
void blockArnoldiStep(int k, bool doubleOrthog)
|
||||
{
|
||||
int kBase = k * Nblock; // first flat index of current block
|
||||
int prevN = kBase + Nblock; // number of basis vectors so far after this step
|
||||
int N = Nm;
|
||||
|
||||
// W[t] = op(basis[kBase + t]) — dispatches to applyBlock() virtual
|
||||
std::vector<Field> W(Nblock, Field(Grid_));
|
||||
applyBlock(W, kBase);
|
||||
|
||||
// Orthogonalise W against all current basis vectors (full reorthogonalisation)
|
||||
// H[i, kBase + t] = <basis[i] | W[t]>
|
||||
for (int pass = 0; pass < (doubleOrthog ? 2 : 1); pass++) {
|
||||
for (int i = 0; i < prevN; i++) {
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
ComplexD coeff = innerProduct(basis[i], W[t]);
|
||||
if (pass == 0)
|
||||
H(i, kBase + t) = toStdCmplx(coeff);
|
||||
else
|
||||
H(i, kBase + t) += toStdCmplx(coeff);
|
||||
W[t] -= coeff * basis[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store residual block F
|
||||
F = W;
|
||||
|
||||
if (k == Nm/Nblock - 1) {
|
||||
// Last block: compute coupling matrix B for KS decomp.
|
||||
//
|
||||
// blockQR modifies F in-place (F → Q orthonormal) and returns R
|
||||
// such that W_orig[t] = sum_s F[s] * R[s,t] (W_orig = F_after * R).
|
||||
//
|
||||
// The KS relation for column j = kBase+t requires the coefficient of F[s]
|
||||
// to be (B†)[s,j] = conj(B[j,s]). Matching with R[s,t]:
|
||||
// conj(B[kBase+t, s]) = R[s,t] → B[kBase+t, s] = conj(R[s,t])
|
||||
//
|
||||
// Equivalently the last Nblock rows of B are R^H (Hermitian conjugate of R).
|
||||
// Note: for Nblock=1, R is scalar real positive, so this reduces to B = R. ✓
|
||||
CMat R = blockQR(F); // F is modified in-place to become Q; returns R
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int s = 0; s < Nblock; s++)
|
||||
B(kBase + t, s) = std::conj(R(s, t)); // B_block = R^H
|
||||
|
||||
beta_k = R.norm();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not last block: QR-decompose residual to get V_{k+1}
|
||||
CMat R = blockQR(F); // F orthonormalised in-place, R is upper triangular
|
||||
|
||||
// Subdiagonal block of H: H[(k+1)*Nblock : (k+2)*Nblock, kBase : kBase+Nblock] = R
|
||||
int nextBase = (k + 1) * Nblock;
|
||||
for (int i = 0; i < Nblock; i++)
|
||||
for (int j = 0; j < Nblock; j++)
|
||||
H(nextBase + i, kBase + j) = R(i, j);
|
||||
|
||||
// Append normalised residual block to basis
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
basis.push_back(F[t]);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block QR via modified Gram-Schmidt within the block
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Given a block of Nblock vectors W (not necessarily orthonormal),
|
||||
* orthonormalises them in-place and returns the upper-triangular R
|
||||
* such that W_in = W_out * R.
|
||||
*
|
||||
* Handles (near-)linear dependence by zeroing vectors below threshold.
|
||||
*/
|
||||
CMat blockQR(std::vector<Field>& W)
|
||||
{
|
||||
CMat R = CMat::Zero(Nblock, Nblock);
|
||||
const RealD deflThresh = 1e-14;
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
// Orthogonalise W[j] against W[0..j-1]
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD coeff = innerProduct(W[i], W[j]);
|
||||
R(i, j) = toStdCmplx(coeff);
|
||||
W[j] -= coeff * W[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(W[j]));
|
||||
R(j, j) = nrm;
|
||||
if (nrm > deflThresh) {
|
||||
W[j] *= (1.0 / nrm);
|
||||
} else {
|
||||
// deflation: zero this vector
|
||||
W[j] = Zero();
|
||||
std::cout << GridLogMessage
|
||||
<< className << ": deflation at block column " << j
|
||||
<< " (norm = " << nrm << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Orthonormalise a block against itself (no prior basis)
|
||||
//--------------------------------------------------------------------
|
||||
void blockOrthonormalise(std::vector<Field>& V)
|
||||
{
|
||||
for (int j = 0; j < (int)V.size(); j++) {
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD c = innerProduct(V[i], V[j]);
|
||||
V[j] -= c * V[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(V[j]));
|
||||
assert(nrm > 1e-14);
|
||||
V[j] *= (1.0 / nrm);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Basis rotation: UR[i] = sum_j U[j] * R(j, i)
|
||||
//--------------------------------------------------------------------
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field>& U,
|
||||
CMat& R, int N)
|
||||
{
|
||||
UR.clear();
|
||||
Field tmp(Grid_);
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < N; j++)
|
||||
tmp += U[j] * R(j, i);
|
||||
UR.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Eigensystem of the truncated Rayleigh quotient
|
||||
//--------------------------------------------------------------------
|
||||
void computeEigensystem(CMat& Hk, int Nkeep)
|
||||
{
|
||||
Eigen::ComplexEigenSolver<CMat> es;
|
||||
es.compute(Hk);
|
||||
|
||||
// Sort to match schurReorder ordering.
|
||||
int n = es.eigenvalues().size();
|
||||
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)));
|
||||
});
|
||||
|
||||
evals.resize(n);
|
||||
littleEvecs.resize(n, n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
evals(k) = es.eigenvalues()(idx[k]);
|
||||
littleEvecs.col(k) = es.eigenvectors().col(idx[k]);
|
||||
}
|
||||
|
||||
evecs.clear();
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec vec = littleEvecs.col(k);
|
||||
Field tmp(Grid_);
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)basis.size() && j < Nkeep; j++)
|
||||
tmp += vec[j] * basis[j];
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Convergence check
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* An eigenpair (lambda, y) is converged if the Ritz estimate
|
||||
* r = || B^dag y ||
|
||||
* satisfies r < rtol. Here B is the (Nkeep x Nblock) coupling matrix
|
||||
* and y is the little eigenvector (Nkeep-vector) of H.
|
||||
*/
|
||||
int converged(int Nkeep)
|
||||
{
|
||||
ritzEstimates.clear();
|
||||
int Nconv = 0;
|
||||
|
||||
CMat Bk = B(Eigen::seqN(0, Nkeep), Eigen::all); // Nkeep x Nblock
|
||||
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec yk = littleEvecs.col(k); // Nkeep-vector
|
||||
CVec Bty = Bk.adjoint() * yk; // Nblock-vector
|
||||
RealD res = Bty.norm();
|
||||
ritzEstimates.push_back(res);
|
||||
std::cout << GridLogMessage << className << ": Ritz estimate[" << k
|
||||
<< "] = " << res << " eval = " << evals[k] << std::endl;
|
||||
if (res < rtol) Nconv++;
|
||||
else break;
|
||||
}
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Approximate maximum eigenvalue via power iteration
|
||||
//--------------------------------------------------------------------
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50)
|
||||
{
|
||||
assert(norm2(v0) > 1e-8);
|
||||
RealD lam = 0.0, denom = std::sqrt(norm2(v0));
|
||||
Field vcur(Grid_), vtmp(Grid_);
|
||||
vcur = v0;
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(vcur, vtmp);
|
||||
vcur = vtmp;
|
||||
RealD num = std::sqrt(norm2(vcur));
|
||||
lam = num / denom;
|
||||
denom = num;
|
||||
}
|
||||
return lam;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_BLOCKED_KRYLOV_SCHUR_H
|
||||
@@ -0,0 +1,277 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ConjugateGradientTimeslice.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Azusa Yamaguchi <ayamaguc@staffmail.ed.ac.uk>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
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_CONJUGATE_GRADIENT_TIMESLICE_H
|
||||
#define GRID_CONJUGATE_GRADIENT_TIMESLICE_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Base classes for iterative processes based on operators
|
||||
// single input vec, single output vec.
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Simple modification of conjugate gradient that outputs the residual as a function
|
||||
* of time, in order to study the large wavelength behavior of the solver.
|
||||
*/
|
||||
|
||||
|
||||
template <class Field>
|
||||
class ConjugateGradientTimeslice : public OperatorFunction<Field> {
|
||||
public:
|
||||
|
||||
using OperatorFunction<Field>::operator();
|
||||
|
||||
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
|
||||
RealD TrueResidual;
|
||||
|
||||
ConjugateGradientTimeslice(RealD tol, Integer maxit, bool err_on_no_conv = true)
|
||||
: Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
ErrorOnNoConverge(err_on_no_conv)
|
||||
{};
|
||||
|
||||
virtual void LogIteration(int k,RealD a,RealD b){
|
||||
// std::cout << "ConjugageGradient::LogIteration() "<<std::endl;
|
||||
};
|
||||
virtual void LogBegin(void){
|
||||
std::cout << "ConjugageGradient::LogBegin() "<<std::endl;
|
||||
};
|
||||
|
||||
void operator()(LinearOperatorBase<Field> &Linop, const Field &src, Field &psi) {
|
||||
|
||||
this->LogBegin();
|
||||
|
||||
GRID_TRACE("ConjugateGradientTimeslice");
|
||||
GridStopWatch PreambleTimer;
|
||||
GridStopWatch ConstructTimer;
|
||||
GridStopWatch NormTimer;
|
||||
GridStopWatch AssignTimer;
|
||||
PreambleTimer.Start();
|
||||
psi.Checkerboard() = src.Checkerboard();
|
||||
|
||||
conformable(psi, src);
|
||||
|
||||
RealD cp, c, a, d, b, ssq, qq;
|
||||
//RealD b_pred;
|
||||
|
||||
// Was doing copies
|
||||
ConstructTimer.Start();
|
||||
Field p (src.Grid());
|
||||
Field mmp(src.Grid());
|
||||
Field r (src.Grid());
|
||||
ConstructTimer.Stop();
|
||||
|
||||
// Initial residual computation & set up
|
||||
NormTimer.Start();
|
||||
ssq = norm2(src); // Norm of source vector ||b||^2
|
||||
|
||||
ssqtx = localNorm2(src); // Norm |b(x, t)|^2 as a field
|
||||
std::vector<RealD> ssqt; // Norm of source not summed over time slices, ssq(t) = \sum_x |b(x, t)|^2
|
||||
sliceSum(ssqtx, ssqt, Tdir); // TODO make sure Tdir is globally defined
|
||||
|
||||
RealD guess = norm2(psi); // Norm of initial guess ||psi||^2
|
||||
NormTimer.Stop();
|
||||
assert(std::isnan(guess) == 0);
|
||||
AssignTimer.Start();
|
||||
if ( guess == 0.0 ) {
|
||||
r = src;
|
||||
p = r;
|
||||
a = ssq;
|
||||
} else {
|
||||
Linop.HermOpAndNorm(psi, mmp, d, b); //
|
||||
r = src - mmp; // Initial residual r0 = b - A guess
|
||||
p = r; // initial conj vector p0 = r0
|
||||
a = norm2(p);
|
||||
}
|
||||
cp = a;
|
||||
AssignTimer.Stop();
|
||||
|
||||
// Handle trivial case of zero src
|
||||
if (ssq == 0.){
|
||||
psi = Zero();
|
||||
IterationsToComplete = 1;
|
||||
TrueResidual = 0.;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: guess " << guess << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: src " << ssq << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: mp " << d << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: mmp " << b << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: cp,r " << cp << std::endl;
|
||||
std::cout << GridLogIterative << std::setprecision(8) << "ConjugateGradient: p " << a << std::endl;
|
||||
|
||||
RealD rsq = Tolerance * Tolerance * ssq;
|
||||
|
||||
// Check if guess is really REALLY good :)
|
||||
if (cp <= rsq) {
|
||||
TrueResidual = std::sqrt(a/ssq);
|
||||
std::cout << GridLogMessage << "ConjugateGradient guess is converged already " << std::endl;
|
||||
IterationsToComplete = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << GridLogIterative << std::setprecision(8)
|
||||
<< "ConjugateGradient: k=0 residual " << cp << " target " << rsq << std::endl;
|
||||
|
||||
PreambleTimer.Stop();
|
||||
GridStopWatch LinalgTimer;
|
||||
GridStopWatch InnerTimer;
|
||||
GridStopWatch AxpyNormTimer;
|
||||
GridStopWatch LinearCombTimer;
|
||||
GridStopWatch MatrixTimer;
|
||||
GridStopWatch SolverTimer;
|
||||
|
||||
RealD usecs = -usecond();
|
||||
SolverTimer.Start();
|
||||
int k;
|
||||
for (k = 1; k <= MaxIterations; k++) {
|
||||
|
||||
GridStopWatch IterationTimer;
|
||||
IterationTimer.Start();
|
||||
c = cp;
|
||||
|
||||
MatrixTimer.Start();
|
||||
Linop.HermOp(p, mmp); // Computes mmp = Ap
|
||||
MatrixTimer.Stop();
|
||||
|
||||
LinalgTimer.Start();
|
||||
|
||||
InnerTimer.Start();
|
||||
ComplexD dc = innerProduct(p,mmp); // p^\dagger A p
|
||||
InnerTimer.Stop();
|
||||
d = dc.real();
|
||||
a = c / d;
|
||||
|
||||
// What is axpy? Some accelerator or something? Check Lattice_arith.h
|
||||
AxpyNormTimer.Start();
|
||||
|
||||
// axpy_norm computes ax+by for vectors x and y compatible with a GPU. Here b is set to 1 (see the function in Lattice_reduction.h).
|
||||
// The first argument passes r by reference, so it stores r --> -a * Ap + 1 * r, i.e. it performs an update on
|
||||
// r_k --> r_{k+1} = r_k - \alpha_k A p_k. The function returns the norm squared of the first variable, i.e. ||r_{k+1}||^2.
|
||||
cp = axpy_norm(r, -a, mmp, r);
|
||||
AxpyNormTimer.Stop();
|
||||
b = cp / c;
|
||||
|
||||
LinearCombTimer.Start();
|
||||
{
|
||||
autoView( psi_v , psi, AcceleratorWrite);
|
||||
autoView( p_v , p, AcceleratorWrite);
|
||||
autoView( r_v , r, AcceleratorWrite);
|
||||
accelerator_for(ss,p_v.size(), Field::vector_object::Nsimd(),{
|
||||
coalescedWrite(psi_v[ss], a * p_v(ss) + psi_v(ss));
|
||||
coalescedWrite(p_v[ss] , b * p_v(ss) + r_v (ss));
|
||||
});
|
||||
}
|
||||
LinearCombTimer.Stop();
|
||||
LinalgTimer.Stop();
|
||||
LogIteration(k,a,b);
|
||||
|
||||
IterationTimer.Stop();
|
||||
if ( (k % 500) == 0 ) {
|
||||
std::cout << GridLogMessage << "ConjugateGradient: Iteration " << k
|
||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << std::endl;
|
||||
} else {
|
||||
std::cout << GridLogIterative << "ConjugateGradient: Iteration " << k
|
||||
<< " residual " << sqrt(cp/ssq) << " target " << Tolerance << " took " << IterationTimer.Elapsed() << std::endl;
|
||||
}
|
||||
|
||||
// Stopping condition
|
||||
if (cp <= rsq) {
|
||||
usecs +=usecond();
|
||||
SolverTimer.Stop();
|
||||
Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
p = mmp - src;
|
||||
GridBase *grid = src.Grid();
|
||||
RealD DwfFlops = (1452. )*grid->gSites()*4*k
|
||||
+ (8+4+8+4+4)*12*grid->gSites()*k; // CG linear algebra
|
||||
RealD srcnorm = std::sqrt(norm2(src));
|
||||
RealD resnorm = std::sqrt(norm2(p));
|
||||
RealD true_residual = resnorm / srcnorm;
|
||||
std::cout << GridLogMessage << "ConjugateGradient Converged on iteration " << k
|
||||
<< "\tComputed residual " << std::sqrt(cp / ssq)
|
||||
<< "\tTrue residual " << true_residual
|
||||
<< "\tTarget " << Tolerance << std::endl;
|
||||
|
||||
// GridLogMessage logs the message to the terminal output; GridLogPerformance probably writes to a log file?
|
||||
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver Elapsed " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "Time breakdown "<<std::endl;
|
||||
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
|
||||
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
|
||||
|
||||
IterationsToComplete = k;
|
||||
TrueResidual = true_residual;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Failed. Calculate true residual before giving up
|
||||
// Linop.HermOpAndNorm(psi, mmp, d, qq);
|
||||
// p = mmp - src;
|
||||
//TrueResidual = sqrt(norm2(p)/ssq);
|
||||
// TrueResidual = 1;
|
||||
|
||||
std::cout << GridLogMessage << "ConjugateGradient did NOT converge "<<k<<" / "<< MaxIterations
|
||||
<<" residual "<< std::sqrt(cp / ssq)<< std::endl;
|
||||
SolverTimer.Stop();
|
||||
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage << "Solver breakdown "<<std::endl;
|
||||
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogMessage<< "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
|
||||
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
|
||||
|
||||
if (ErrorOnNoConverge) assert(0);
|
||||
IterationsToComplete = k;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,230 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
γ5-Scalar Lanczos algorithm for γ5-Hermitian operators (Wilson Dirac).
|
||||
|
||||
Single-vector Lanczos using the γ5-inner product (u,v)_{γ5} = u†γ5v.
|
||||
Builds the Krylov space {q, D_W q, D_W² q, ...} and projects D_W onto it,
|
||||
producing a real tridiagonal matrix T_m whose eigenvalues approximate those
|
||||
of D_W. Complex conjugate pairs of D_W eigenvalues appear as complex
|
||||
eigenvalues of the real non-symmetric T_m.
|
||||
|
||||
Recurrence (G_k = sign(q_k†γ5 q_k) = ±1):
|
||||
r_k = D_W q_k − α_k q_k − β_sup[k-1] q_{k-1}
|
||||
β_sub[k] = √|r_k†γ5 r_k| (lower subdiag T_{k+1,k})
|
||||
β_sup[k] = β_sub[k] G_{k+1}/G_k (upper superdiag T_{k,k+1})
|
||||
q_{k+1} = r_k / β_sub[k]
|
||||
|
||||
T_m is real but not symmetric in general; eigenvalues via EigenSolver.
|
||||
|
||||
*************************************************************************************/
|
||||
#ifndef GRID_GAMMA5_SCALAR_LANCZOS_H
|
||||
#define GRID_GAMMA5_SCALAR_LANCZOS_H
|
||||
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <iomanip>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
template<class Field>
|
||||
class Gamma5ScalarLanczos {
|
||||
public:
|
||||
using Gamma5Func = std::function<void(const Field&, Field&)>;
|
||||
|
||||
private:
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
typedef Eigen::MatrixXd RMat;
|
||||
|
||||
LinearOperatorBase<Field>& Linop;
|
||||
GridBase* Grid_;
|
||||
Gamma5Func applyGamma5;
|
||||
RealD Tolerance;
|
||||
int nSteps;
|
||||
|
||||
std::vector<Field> basis;
|
||||
std::vector<RealD> alpha_; // diagonal of T_m
|
||||
std::vector<RealD> beta_sub_; // lower subdiagonal: T_{k+1,k}
|
||||
std::vector<RealD> beta_sup_; // upper superdiagonal: T_{k,k+1}
|
||||
std::vector<int> gsign_; // G_k = ±1
|
||||
|
||||
CVec evals_;
|
||||
std::vector<Field> evecs_;
|
||||
std::vector<RealD> residuals_;
|
||||
|
||||
public:
|
||||
bool doEvalCheck = false;
|
||||
|
||||
Gamma5ScalarLanczos(LinearOperatorBase<Field>& op, GridBase* grid,
|
||||
Gamma5Func g5, RealD tol = 1e-8)
|
||||
: Linop(op), Grid_(grid), applyGamma5(g5), Tolerance(tol), nSteps(0)
|
||||
{}
|
||||
|
||||
CVec getEvals() { return evals_; }
|
||||
std::vector<Field> getEvecs() { return evecs_; }
|
||||
std::vector<RealD> getResiduals() { return residuals_; }
|
||||
|
||||
// v0 : starting vector
|
||||
// maxSteps : maximum Lanczos steps
|
||||
// Nstop : target Ritz pairs to return (0 = all)
|
||||
// reorthog : full γ5-reorthogonalisation at each step
|
||||
// filter : eigenvalue selection criterion
|
||||
void operator()(const Field& v0, int maxSteps, int Nstop,
|
||||
bool reorthog = false, RitzFilter filter = EvalImNormSmall)
|
||||
{
|
||||
basis.clear(); alpha_.clear(); beta_sub_.clear(); beta_sup_.clear(); gsign_.clear();
|
||||
nSteps = 0;
|
||||
|
||||
// Normalise starting vector in γ5-norm: q_0 = v0 / √|v0†γ5 v0|
|
||||
Field q(Grid_); q = v0;
|
||||
Field g5q(Grid_);
|
||||
applyGamma5(q, g5q);
|
||||
RealD omega = std::real(toStdCmplx(innerProduct(g5q, q)));
|
||||
RealD beta0 = std::sqrt(std::abs(omega));
|
||||
assert(beta0 > 1e-14 && "starting vector has zero γ5-norm");
|
||||
q *= (1.0 / beta0);
|
||||
gsign_.push_back(omega >= 0 ? +1 : -1);
|
||||
basis.push_back(q);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: start (v0†γ5 v0)=" << omega
|
||||
<< " G[0]=" << gsign_[0] << std::endl;
|
||||
|
||||
for (int k = 0; k < maxSteps; k++) {
|
||||
// ── Apply operator ─────────────────────────────────────────────────
|
||||
Field p(Grid_);
|
||||
Linop.Op(basis[k], p);
|
||||
|
||||
// ── α_k = (q_k†γ5 D_W q_k) / G_k (real) ─────────────────────────
|
||||
applyGamma5(basis[k], g5q);
|
||||
RealD a = std::real(toStdCmplx(innerProduct(g5q, p))) / RealD(gsign_[k]);
|
||||
alpha_.push_back(a);
|
||||
|
||||
// ── r = D_W q_k − α_k q_k − β_sup[k-1] q_{k-1} ───────────────────
|
||||
Field r(Grid_); r = p;
|
||||
r -= basis[k] * a;
|
||||
if (k > 0)
|
||||
r -= basis[k-1] * beta_sup_[k-1];
|
||||
|
||||
// ── Optional γ5-reorthogonalisation ────────────────────────────────
|
||||
if (reorthog) {
|
||||
for (int i = 0; i <= k; i++) {
|
||||
applyGamma5(basis[i], g5q);
|
||||
ComplexD c = toStdCmplx(innerProduct(g5q, r)) / RealD(gsign_[i]);
|
||||
r -= basis[i] * c;
|
||||
}
|
||||
}
|
||||
|
||||
// ── β_sub[k] = √|r†γ5 r| ───────────────────────────────────────────
|
||||
applyGamma5(r, g5q);
|
||||
omega = std::real(toStdCmplx(innerProduct(g5q, r)));
|
||||
RealD beta = std::sqrt(std::abs(omega));
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: k=" << k
|
||||
<< " alpha=" << a
|
||||
<< " beta=" << beta
|
||||
<< " G[k]=" << gsign_[k]
|
||||
<< " (r†γ5r)=" << omega << std::endl;
|
||||
|
||||
beta_sub_.push_back(beta);
|
||||
|
||||
if (beta < Tolerance) {
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: invariant subspace at step " << k << std::endl;
|
||||
nSteps = k + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
int G_next = (omega >= 0) ? +1 : -1;
|
||||
gsign_.push_back(G_next);
|
||||
// β_sup[k] = T_{k,k+1} = β_sub[k] * G_{k+1} / G_k
|
||||
beta_sup_.push_back(beta * RealD(G_next) / RealD(gsign_[k]));
|
||||
|
||||
Field qnew(Grid_); qnew = r; qnew *= (1.0 / beta);
|
||||
basis.push_back(qnew);
|
||||
nSteps = k + 1;
|
||||
}
|
||||
|
||||
if (nSteps == 0) return;
|
||||
computeRitzPairs(nSteps, Nstop, filter);
|
||||
}
|
||||
|
||||
private:
|
||||
void computeRitzPairs(int m, int Nstop, RitzFilter filter)
|
||||
{
|
||||
// Assemble real tridiagonal T_m
|
||||
RMat Tm = RMat::Zero(m, m);
|
||||
for (int k = 0; k < m; k++) {
|
||||
Tm(k, k) = alpha_[k];
|
||||
if (k + 1 < m) {
|
||||
Tm(k+1, k) = beta_sub_[k];
|
||||
Tm(k, k+1) = beta_sup_[k];
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5ScalarLanczos: T_m (" << m << "×" << m << "):" << std::endl;
|
||||
for (int i = 0; i < m; i++) {
|
||||
for (int j = 0; j < m; j++)
|
||||
std::cout << " " << std::setprecision(8) << std::setw(16) << Tm(i,j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
Eigen::EigenSolver<RMat> es(Tm);
|
||||
CVec lambdas = es.eigenvalues();
|
||||
CMat Y = es.eigenvectors();
|
||||
|
||||
// Sort by filter criterion
|
||||
ComplexComparator cComp(filter);
|
||||
std::vector<int> idx(m);
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::sort(idx.begin(), idx.end(), [&](int a, int b){
|
||||
return cComp(toStdCmplx(lambdas(a)), toStdCmplx(lambdas(b)));
|
||||
});
|
||||
|
||||
int Nout = (Nstop > 0 && Nstop < m) ? Nstop : m;
|
||||
evals_.resize(Nout);
|
||||
evecs_.clear(); evecs_.reserve(Nout);
|
||||
residuals_.clear(); residuals_.reserve(Nout);
|
||||
|
||||
// Trailing β for residual estimate: β_sub[m-1] * |y_j[m-1]|
|
||||
RealD beta_trail = (m - 1 < (int)beta_sub_.size()) ? beta_sub_[m-1] : 0.0;
|
||||
|
||||
for (int ji = 0; ji < Nout; ji++) {
|
||||
int j = idx[ji];
|
||||
evals_(ji) = lambdas(j);
|
||||
CVec yj = Y.col(j);
|
||||
|
||||
// Ritz vector: u_j = Σ_k q_k y_j[k]
|
||||
Field uj(Grid_); uj = Zero();
|
||||
for (int k = 0; k < m && k < (int)basis.size(); k++)
|
||||
uj += basis[k] * toStdCmplx(yj(k));
|
||||
evecs_.push_back(uj);
|
||||
|
||||
RealD res = beta_trail * std::abs(toStdCmplx(yj(m-1)));
|
||||
if (!std::isfinite(res)) res = std::numeric_limits<RealD>::infinity();
|
||||
residuals_.push_back(res);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< " Ritz[" << std::setw(3) << ji << "]"
|
||||
<< " lambda=" << evals_(ji)
|
||||
<< " |res|=" << res;
|
||||
|
||||
if (doEvalCheck) {
|
||||
Field w(Grid_);
|
||||
Linop.Op(uj, w);
|
||||
w -= uj * evals_(ji);
|
||||
RealD actual = std::sqrt(norm2(w));
|
||||
std::cout << " ||D_W u - λu||=" << actual;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_GAMMA5_SCALAR_LANCZOS_H
|
||||
@@ -0,0 +1,719 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/HarmonicBlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
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_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
#define GRID_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Block shift-targeted Krylov-Schur eigensolver.
|
||||
*
|
||||
* Algorithm
|
||||
* ---------
|
||||
* Uses a block Arnoldi factorisation:
|
||||
*
|
||||
* A V = V H + F B^dag (1)
|
||||
*
|
||||
* with V orthonormal (Nm columns), H the Nm×Nm block upper-Hessenberg
|
||||
* Rayleigh quotient, F the Nblock residual vectors and B the Nm×Nblock
|
||||
* coupling matrix.
|
||||
*
|
||||
* Thick restart
|
||||
* -------------
|
||||
* To target eigenvalues near shift σ, the Schur decomposition is computed
|
||||
* for the shifted Rayleigh quotient:
|
||||
*
|
||||
* (H - σI) = Q^dag S Q (2)
|
||||
*
|
||||
* Sorting the Schur values of (H - σI) by smallest |S(i,i)| = |λ - σ| and
|
||||
* retaining the leading Nk is equivalent to selecting the Ritz values of H
|
||||
* closest to σ. Since Q diagonalises H - σI (and hence H itself), the
|
||||
* rotated Rayleigh quotient is exactly upper triangular:
|
||||
*
|
||||
* H_new = Q H Q^dag = S + σI (upper triangular) (3)
|
||||
*
|
||||
* Truncation to Nk is therefore exact: the off-diagonal coupling block
|
||||
* H_new[Nk:, :Nk] = 0 by triangularity. The Krylov-Schur relation after
|
||||
* restart is exact and block Arnoldi resumes cleanly from F.
|
||||
*
|
||||
* Convergence
|
||||
* -----------
|
||||
* Convergence is declared when || B^H y_k || < Tolerance * approxLambdaMax
|
||||
* for each Ritz pair (λ_k, y_k) of the truncated H.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* shift : target shift σ (default 0.0); Schur values sorted by |λ - σ|
|
||||
* Nblock : block size p
|
||||
* Nm : total Krylov dimension (must be divisible by Nblock)
|
||||
* Nk : total vectors to retain after each restart (must be divisible by Nblock, Nk < Nm)
|
||||
* Nstop : stop when this many eigenpairs converge
|
||||
* MaxIter : maximum outer (restart) iterations
|
||||
* Tolerance: relative convergence tolerance
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
* HarmonicBlockKrylovSchur<Field> hbks(LinOp, Grid, tol, shift, EvalNormSmall);
|
||||
* std::vector<Field> v0(Nblock, Field(Grid));
|
||||
* // fill v0 with random starting vectors
|
||||
* hbks(v0, maxIter, Nm, Nk, Nstop, Nblock);
|
||||
* auto evals = hbks.getEvals();
|
||||
* auto evecs = hbks.getEvecs();
|
||||
*/
|
||||
template<class Field>
|
||||
class HarmonicBlockKrylovSchur {
|
||||
|
||||
protected:
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
typedef Eigen::VectorXcd CVec;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Parameters
|
||||
//--------------------------------------------------------------------
|
||||
int Nblock;
|
||||
int Nm; // total Krylov dimension (multiple of Nblock)
|
||||
int Nk; // total vectors retained after restart (multiple of Nblock)
|
||||
int Nstop;
|
||||
int MaxIter;
|
||||
RealD Tolerance;
|
||||
ComplexD shift; // target shift σ
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Internal state
|
||||
//--------------------------------------------------------------------
|
||||
LinearOperatorBase<Field>& Linop;
|
||||
GridBase* Grid_;
|
||||
RitzFilter ritzFilter;
|
||||
|
||||
std::vector<Field> basis; // Nm*Nblock flat basis
|
||||
CMat H; // (Nm*Nblock)² block-Hessenberg Rayleigh quotient
|
||||
std::vector<Field> F; // Nblock residual vectors
|
||||
CMat B; // (Nm*Nblock) × Nblock coupling matrix
|
||||
RealD beta_k;
|
||||
RealD rtol;
|
||||
|
||||
CVec evals;
|
||||
CMat littleEvecs;
|
||||
std::vector<RealD> ritzEstimates;
|
||||
|
||||
public:
|
||||
std::vector<Field> evecs;
|
||||
bool doEvalCheck = false;
|
||||
bool useParityFlip = false;
|
||||
bool useGamma5 = false;
|
||||
std::function<void(const Field&, Field&)> gamma5Func;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------------------------------------
|
||||
HarmonicBlockKrylovSchur(LinearOperatorBase<Field>& _Linop, GridBase* _Grid,
|
||||
RealD _Tolerance, ComplexD _shift = 0.0,
|
||||
RitzFilter _rf = EvalNormSmall)
|
||||
: Linop(_Linop), Grid_(_Grid), Tolerance(_Tolerance), shift(_shift),
|
||||
ritzFilter(_rf),
|
||||
Nblock(-1), Nm(-1), Nk(-1), Nstop(-1), MaxIter(-1),
|
||||
beta_k(0.0), rtol(0.0)
|
||||
{}
|
||||
|
||||
virtual ~HarmonicBlockKrylovSchur() = default;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Main entry point
|
||||
//--------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
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);
|
||||
std::cout << GridLogMessage << "divisor= " << divisor << std::endl;
|
||||
|
||||
}
|
||||
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
|
||||
<< "HarmonicBlockKrylovSchur: 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
|
||||
<< "HarmonicBlockKrylovSchur: restart iteration " << iter << std::endl;
|
||||
|
||||
// ---- Block Arnoldi: extend from block 'start' to block Nm/Nblock ----
|
||||
blockArnoldiIteration(startBlock, Nm/Nblock, start, doubleOrthog);
|
||||
start = Nk/Nblock;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after Arnoldi";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Schur decompose (H - σI) to select Schur vectors closest to σ ----
|
||||
// Sorting the Schur values of (H - σI) by |S(i,i)| = |λ - σ| gives the
|
||||
// Ritz values of H nearest the target shift without any matrix inversion.
|
||||
// Because Q diagonalises (H - σI), it also diagonalises H:
|
||||
// H_new = Q H Q^dag = S + σI (upper triangular)
|
||||
// Truncation is therefore exact.
|
||||
CMat Hshift = H - shift * CMat::Identity(N, N);
|
||||
ComplexSchurDecomposition schur(Hshift, false, ritzFilter);
|
||||
schur.schurReorder(Nk);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: Ritz values nearest shift (first Nk):" << std::endl;
|
||||
CMat S = schur.getMatrixS();
|
||||
for (int i = 0; i < Nk; i++)
|
||||
std::cout << GridLogMessage << " [" << i << "] " << S(i, i) + shift << std::endl;
|
||||
|
||||
CMat Q = schur.getMatrixQ();
|
||||
CMat Qt = Q.adjoint();
|
||||
|
||||
// ---- Rotate Krylov basis ----
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, N);
|
||||
basis = basis2;
|
||||
|
||||
// ---- Update H and B ----
|
||||
// H_new = S + σI is upper triangular; off-diagonal block H_new[Nk:,:Nk] = 0
|
||||
H = S + shift * CMat::Identity(N, N);
|
||||
B = Q * B;
|
||||
|
||||
// ---- Truncate to Nk (exact: H upper triangular) ----
|
||||
int Nkeep = Nk;
|
||||
|
||||
CMat Htmp = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
H = CMat::Zero(N, N);
|
||||
H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep)) = Htmp;
|
||||
|
||||
std::vector<Field> basisTmp(basis.begin(), basis.begin() + Nkeep);
|
||||
basis = basisTmp;
|
||||
|
||||
CMat Btmp = B(Eigen::seqN(0, Nkeep), Eigen::all);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
B(Eigen::seqN(0, Nkeep), Eigen::all) = Btmp;
|
||||
|
||||
beta_k = Btmp.norm();
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: beta_k = " << beta_k << std::endl;
|
||||
|
||||
// Restart from F (exact: no discarded-basis correction needed)
|
||||
startBlock = F;
|
||||
|
||||
if (doVerify) {
|
||||
std::string lbl = "iter " + std::to_string(iter) + " after restart+truncation";
|
||||
verify(lbl);
|
||||
}
|
||||
|
||||
// ---- Eigensystem of truncated H for convergence ----
|
||||
CMat Hk = H(Eigen::seqN(0, Nkeep), Eigen::seqN(0, Nkeep));
|
||||
computeEigensystem(Hk, Nkeep);
|
||||
|
||||
int Nconv = converged(Nkeep);
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: converged " << Nconv
|
||||
<< " / " << Nstop << std::endl;
|
||||
|
||||
if (Nconv >= Nstop || iter == MaxIter - 1) {
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: 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 << "HarmonicBlockKrylovSchur: evec[" << k << "]"
|
||||
<< " eval_reported = " << evals[k]
|
||||
<< " eval_est = " << eval_est
|
||||
<< " || A v - eval_est * v || = " << res << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
CVec getEvals() { return evals; }
|
||||
std::vector<RealD> getRitzEstimates() { return ritzEstimates; }
|
||||
|
||||
static void parityFlippedField(const Field& v, Field& out) {
|
||||
GridCartesian* cgrid = dynamic_cast<GridCartesian*>(v.Grid());
|
||||
assert(cgrid != nullptr);
|
||||
GridRedBlackCartesian rbgrid(cgrid);
|
||||
Field veven(&rbgrid), vodd(&rbgrid);
|
||||
pickCheckerboard(Even, veven, v);
|
||||
pickCheckerboard(Odd, vodd, v);
|
||||
vodd = -vodd;
|
||||
setCheckerboard(out, veven);
|
||||
setCheckerboard(out, vodd);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Verification: check A V = V H + F B^dag explicitly
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Checks the block Arnoldi / Krylov-Schur decomposition
|
||||
*
|
||||
* A V = V H + F B^dag (KS)
|
||||
*
|
||||
* by explicit operator applications. H here is the standard Rayleigh
|
||||
* quotient, so the KS relation is the same as for BlockKrylovSchur.
|
||||
*
|
||||
* Prints:
|
||||
* - H (current Rayleigh quotient, nBasis × nBasis)
|
||||
* - B (coupling matrix, nBasis × Nblock)
|
||||
* - M (explicit inner product matrix <V | A V>)
|
||||
* - max |H[i,j] - M[i,j]| (should be O(machine epsilon))
|
||||
* - max |<V_i|V_j> - delta_ij| (orthonormality check)
|
||||
* - for each basis column j: || A v_j - V H[:,j] - F B[j,:]^* ||
|
||||
* - max |<V_i | F_t>| (F orthogonal to basis)
|
||||
*/
|
||||
void verify(const std::string& label = "")
|
||||
{
|
||||
int nBasis = (int)basis.size();
|
||||
int nF = (int)F.size();
|
||||
|
||||
if (nBasis == 0) {
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur::verify [" << label
|
||||
<< "]: basis is empty." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== HarmonicBlockKrylovSchur::verify [" << label << "] ========" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " nBasis = " << nBasis << " Nblock = " << Nblock
|
||||
<< " nF = " << nF << std::endl;
|
||||
|
||||
// ---- Print H ----
|
||||
std::cout << GridLogMessage << "H (" << nBasis << " x " << nBasis << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << H(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Print B ----
|
||||
std::cout << GridLogMessage << "B (" << nBasis << " x " << nF << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int t = 0; t < nF; t++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << B(i, t);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- Compute M[i,j] = <basis[i] | A basis[j]> ----
|
||||
CMat M = CMat::Zero(nBasis, nBasis);
|
||||
Field w(Grid_);
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
M(i, j) = toStdCmplx(innerProduct(basis[i], w));
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "M = <V|AV> (" << nBasis << " x " << nBasis << "):" << std::endl;
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
std::cout << " " << std::setprecision(4) << std::setw(14) << M(i, j);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// ---- max |H - M| ----
|
||||
RealD maxHM = 0.0;
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
maxHM = std::max(maxHM, std::abs(H(i,j) - M(i,j)));
|
||||
std::cout << GridLogMessage
|
||||
<< " max |H[i,j] - M[i,j]| = " << maxHM << std::endl;
|
||||
|
||||
// ---- Check orthonormality of basis ----
|
||||
CMat G = CMat::Zero(nBasis, nBasis);
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
for (int j = 0; j < nBasis; j++)
|
||||
G(i, j) = toStdCmplx(innerProduct(basis[i], basis[j]));
|
||||
CMat Gerr = G - CMat::Identity(nBasis, nBasis);
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i|V_j> - delta_ij| = " << Gerr.cwiseAbs().maxCoeff() << std::endl;
|
||||
|
||||
// ---- Per-column residual: || A v_j - V H[:,j] - F B[j,:]^* || ----
|
||||
RealD maxColDev = 0.0;
|
||||
for (int j = 0; j < nBasis; j++) {
|
||||
Linop.Op(basis[j], w);
|
||||
|
||||
// subtract V H[:,j]
|
||||
for (int i = 0; i < nBasis; i++)
|
||||
w -= basis[i] * H(i, j);
|
||||
|
||||
// subtract F B[j,:]^* (F[t] * conj(B[j,t]))
|
||||
for (int t = 0; t < nF; t++)
|
||||
w -= F[t] * std::conj(B(j, t));
|
||||
|
||||
RealD dev = std::sqrt(norm2(w));
|
||||
std::cout << GridLogMessage
|
||||
<< " || A v[" << j << "] - V H[:,j] - F B[j,:]* || = " << dev << std::endl;
|
||||
maxColDev = std::max(maxColDev, dev);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max column deviation = " << maxColDev << std::endl;
|
||||
|
||||
// ---- Check F block orthogonality against basis ----
|
||||
if (nF > 0) {
|
||||
RealD maxFV = 0.0;
|
||||
for (int t = 0; t < nF; t++)
|
||||
for (int i = 0; i < nBasis; i++) {
|
||||
RealD ip = std::abs(toStdCmplx(innerProduct(basis[i], F[t])));
|
||||
maxFV = std::max(maxFV, ip);
|
||||
}
|
||||
std::cout << GridLogMessage
|
||||
<< " max |<V_i | F_t>| (should be ~0) = " << maxFV << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== end verify ========" << std::endl;
|
||||
}
|
||||
|
||||
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
|
||||
//--------------------------------------------------------------------
|
||||
void blockArnoldiIteration(std::vector<Field>& startBlock, int endBlock,
|
||||
int startIdx, bool doubleOrthog)
|
||||
{
|
||||
int N = Nm;
|
||||
|
||||
if (startIdx == 0) {
|
||||
basis.clear();
|
||||
F.clear();
|
||||
H = CMat::Zero(N, N);
|
||||
B = CMat::Zero(N, Nblock);
|
||||
|
||||
std::vector<Field> V0 = startBlock;
|
||||
blockOrthonormalise(V0);
|
||||
for (auto& v : V0) basis.push_back(v);
|
||||
} else {
|
||||
// Append the new starting block to the retained basis.
|
||||
//
|
||||
// The exact truncated KS relation is A V_k = V_k H_k + F B_k^dag,
|
||||
// so the coupling rows are H[Nkeep+t, j] = conj(B_k[j,t]).
|
||||
// Since H_new = S + σI is upper triangular, the off-diagonal block
|
||||
// H_new[Nkeep:, :Nkeep] = 0 and the restart from F is exact.
|
||||
int Nkeep = startIdx * Nblock;
|
||||
for (auto& v : startBlock) basis.push_back(v);
|
||||
|
||||
// Fill restart coupling rows into H (exact: H_new is upper triangular,
|
||||
// so B encodes the only non-zero coupling to the new block).
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
H(Nkeep + t, j) = std::conj(B(j, t));
|
||||
|
||||
// Zero out B for the retained columns now that the coupling is in H
|
||||
for (int j = 0; j < Nkeep; j++)
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
B(j, t) = 0.0;
|
||||
}
|
||||
|
||||
for (int k = startIdx; k < endBlock; k++)
|
||||
blockArnoldiStep(k, doubleOrthog);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// One block Arnoldi step
|
||||
//--------------------------------------------------------------------
|
||||
void blockArnoldiStep(int k, bool doubleOrthog)
|
||||
{
|
||||
int kBase = k * Nblock;
|
||||
int prevN = kBase + Nblock;
|
||||
int N = Nm;
|
||||
|
||||
std::vector<Field> W(Nblock, Field(Grid_));
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
Linop.Op(basis[kBase + t], W[t]);
|
||||
|
||||
// Full reorthogonalisation against all current basis vectors
|
||||
for (int pass = 0; pass < (doubleOrthog ? 2 : 1); pass++) {
|
||||
for (int i = 0; i < prevN; i++) {
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
ComplexD coeff = innerProduct(basis[i], W[t]);
|
||||
if (pass == 0)
|
||||
H(i, kBase + t) = toStdCmplx(coeff);
|
||||
else
|
||||
H(i, kBase + t) += toStdCmplx(coeff);
|
||||
W[t] -= coeff * basis[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
F = W;
|
||||
|
||||
if (k == Nm/Nblock - 1) {
|
||||
// Last block: record coupling in B as R^H (Hermitian conjugate of QR factor)
|
||||
// KS relation requires B[kBase+t, s] = conj(R[s,t])
|
||||
CMat R = blockQR(F);
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
for (int s = 0; s < Nblock; s++)
|
||||
B(kBase + t, s) = std::conj(R(s, t)); // B_block = R^H
|
||||
beta_k = R.norm();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not last: QR the residual, extend basis
|
||||
CMat R = blockQR(F);
|
||||
|
||||
int nextBase = (k + 1) * Nblock;
|
||||
for (int i = 0; i < Nblock; i++)
|
||||
for (int j = 0; j < Nblock; j++)
|
||||
H(nextBase + i, kBase + j) = R(i, j);
|
||||
|
||||
for (int t = 0; t < Nblock; t++)
|
||||
basis.push_back(F[t]);
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Block QR (modified Gram-Schmidt within the block)
|
||||
//--------------------------------------------------------------------
|
||||
CMat blockQR(std::vector<Field>& W)
|
||||
{
|
||||
CMat R = CMat::Zero(Nblock, Nblock);
|
||||
const RealD deflThresh = 1e-14;
|
||||
|
||||
for (int j = 0; j < Nblock; j++) {
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD coeff = innerProduct(W[i], W[j]);
|
||||
R(i, j) = toStdCmplx(coeff);
|
||||
W[j] -= coeff * W[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(W[j]));
|
||||
R(j, j) = nrm;
|
||||
if (nrm > deflThresh) {
|
||||
W[j] *= (1.0 / nrm);
|
||||
} else {
|
||||
W[j] = Zero();
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: deflation at block column " << j
|
||||
<< " (norm = " << nrm << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Orthonormalise starting block
|
||||
//--------------------------------------------------------------------
|
||||
void blockOrthonormalise(std::vector<Field>& V)
|
||||
{
|
||||
for (int j = 0; j < (int)V.size(); j++) {
|
||||
for (int i = 0; i < j; i++) {
|
||||
ComplexD c = innerProduct(V[i], V[j]);
|
||||
V[j] -= c * V[i];
|
||||
}
|
||||
RealD nrm = std::sqrt(norm2(V[j]));
|
||||
assert(nrm > 1e-14);
|
||||
V[j] *= (1.0 / nrm);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Basis rotation: UR[i] = sum_j U[j] * R(j,i)
|
||||
//--------------------------------------------------------------------
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field>& U,
|
||||
CMat& R, int N)
|
||||
{
|
||||
UR.clear();
|
||||
Field tmp(Grid_);
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < N; j++)
|
||||
tmp += U[j] * R(j, i);
|
||||
UR.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Eigensystem of the truncated H (not Hhat)
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Eigenvalues of H_k are the standard Ritz values in the retained
|
||||
* subspace. After convergence has been declared via harmonic estimates,
|
||||
* the final reported eigenvalues and vectors come from H_k (not Hhat_k),
|
||||
* since H_k contains the true projected operator.
|
||||
*/
|
||||
void computeEigensystem(CMat& Hk, int Nkeep)
|
||||
{
|
||||
Eigen::ComplexEigenSolver<CMat> es;
|
||||
es.compute(Hk);
|
||||
|
||||
// Sort to match schurReorder ordering.
|
||||
int n = es.eigenvalues().size();
|
||||
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)));
|
||||
});
|
||||
|
||||
evals.resize(n);
|
||||
littleEvecs.resize(n, n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
evals(k) = es.eigenvalues()(idx[k]);
|
||||
littleEvecs.col(k) = es.eigenvectors().col(idx[k]);
|
||||
}
|
||||
|
||||
evecs.clear();
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec vec = littleEvecs.col(k);
|
||||
Field tmp(Grid_);
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)basis.size() && j < Nkeep; j++)
|
||||
tmp += vec[j] * basis[j];
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Convergence check
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Ritz estimate for eigenpair k: || B^H y_k ||
|
||||
* where y_k is the k-th eigenvector of the truncated H.
|
||||
* The same bound applies whether using Ritz or harmonic Ritz restart.
|
||||
*/
|
||||
int converged(int Nkeep)
|
||||
{
|
||||
ritzEstimates.clear();
|
||||
int Nconv = 0;
|
||||
|
||||
CMat Bk = B(Eigen::seqN(0, Nkeep), Eigen::all);
|
||||
|
||||
for (int k = 0; k < Nkeep; k++) {
|
||||
CVec yk = littleEvecs.col(k);
|
||||
CVec Bty = Bk.adjoint() * yk;
|
||||
RealD res = Bty.norm();
|
||||
ritzEstimates.push_back(res);
|
||||
std::cout << GridLogMessage
|
||||
<< "HarmonicBlockKrylovSchur: Ritz estimate[" << k
|
||||
<< "] = " << res << " eval = " << evals[k] << std::endl;
|
||||
if (res < rtol) Nconv++;
|
||||
else break;
|
||||
}
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Approximate maximum eigenvalue (power iteration)
|
||||
//--------------------------------------------------------------------
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50)
|
||||
{
|
||||
assert(norm2(v0) > 1e-8);
|
||||
RealD lam = 0.0, denom = std::sqrt(norm2(v0));
|
||||
Field vcur(Grid_), vtmp(Grid_);
|
||||
vcur = v0;
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(vcur, vtmp);
|
||||
vcur = vtmp;
|
||||
RealD num = std::sqrt(norm2(vcur));
|
||||
lam = num / denom;
|
||||
denom = num;
|
||||
}
|
||||
return lam;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_HARMONIC_BLOCKED_KRYLOV_SCHUR_H
|
||||
@@ -53,6 +53,18 @@ enum IRLdiagonalisation {
|
||||
IRLdiagonaliseWithEigen
|
||||
};
|
||||
|
||||
enum IRLeigsort {
|
||||
IRLeigsortMax,
|
||||
IRLeigsortSqMin
|
||||
};
|
||||
|
||||
#if 0
|
||||
bool square_comp(RealD a, RealD b){
|
||||
if (a*a<b*b) return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Field> class ImplicitlyRestartedLanczosHermOpTester : public ImplicitlyRestartedLanczosTester<Field>
|
||||
{
|
||||
public:
|
||||
@@ -119,9 +131,10 @@ class ImplicitlyRestartedLanczos {
|
||||
/////////////////////////
|
||||
// Constructor
|
||||
/////////////////////////
|
||||
|
||||
public:
|
||||
public:
|
||||
IRLeigsort EigSort;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// PAB:
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -154,6 +167,7 @@ public:
|
||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||
eresid(_eresid), betastp(_betastp),
|
||||
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
||||
EigSort(IRLeigsortMax),
|
||||
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
||||
|
||||
ImplicitlyRestartedLanczos(LinearFunction<Field> & PolyOp,
|
||||
@@ -170,6 +184,7 @@ public:
|
||||
Nstop(_Nstop) , Nk(_Nk), Nm(_Nm),
|
||||
eresid(_eresid), betastp(_betastp),
|
||||
MaxIter(_MaxIter) , MinRestart(_MinRestart),
|
||||
EigSort(IRLeigsortMax),
|
||||
orth_period(_orth_period), diagonalisation(_diagonalisation) { };
|
||||
|
||||
////////////////////////////////
|
||||
@@ -316,8 +331,12 @@ until convergence
|
||||
// sorting
|
||||
//////////////////////////////////
|
||||
eval2_copy = eval2;
|
||||
// if (EigSort==IRLeigsortMax)
|
||||
// std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),square_comp);
|
||||
// else
|
||||
std::partial_sort(eval2.begin(),eval2.begin()+Nm,eval2.end(),std::greater<RealD>());
|
||||
std::cout<<GridLogIRL <<" evals sorted "<<std::endl;
|
||||
// eval2_copy = eval2;
|
||||
const int chunk=8;
|
||||
for(int io=0; io<k2;io+=chunk){
|
||||
std::cout<<GridLogIRL << "eval "<< std::setw(3) << io ;
|
||||
@@ -333,6 +352,7 @@ until convergence
|
||||
//////////////////////////////////
|
||||
Qt = Eigen::MatrixXd::Identity(Nm,Nm);
|
||||
for(int ip=k2; ip<Nm; ++ip){
|
||||
// std::cout<<GridLogIRL <<"QR decompose "<<eval2[ip]<<std::endl;
|
||||
QR_decomp(eval,lme,Nm,Nm,Qt,eval2[ip],k1,Nm);
|
||||
}
|
||||
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
|
||||
@@ -375,7 +395,8 @@ until convergence
|
||||
|
||||
// power of two search pattern; not every evalue in eval2 is assessed.
|
||||
int allconv =1;
|
||||
for(int jj = 1; jj<=Nstop; jj*=2){
|
||||
// for(int jj = 1; jj<=Nstop; jj*=2){
|
||||
for(int jj = 1; jj<=Nstop; jj++){
|
||||
int j = Nstop-jj;
|
||||
RealD e = eval2_copy[j]; // Discard the evalue
|
||||
basisRotateJ(B,evec,Qt,j,0,Nk,Nm);
|
||||
|
||||
@@ -0,0 +1,994 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/KrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@bnl.gov>
|
||||
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_KRYLOVSCHUR_H
|
||||
#define GRID_KRYLOVSCHUR_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
#if defined(GRID_CUDA) || defined(GRID_HIP)
|
||||
using thrust::abs;
|
||||
using thrust::conj;
|
||||
inline std::complex<double> toStdCmplx(const ComplexD& c) { return {c.real(), c.imag()}; }
|
||||
#else
|
||||
using std::abs;
|
||||
using std::conj;
|
||||
inline const ComplexD& toStdCmplx(const ComplexD& c) { return c; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Small dense coefficient matrix of ComplexD (thrust::complex on GPU builds)
|
||||
* for passing to basisRotate, whose device kernel cannot multiply Grid tensors
|
||||
* by std::complex. Must be namespace-scope: nvcc rejects function-local types
|
||||
* as template arguments to functions containing extended device lambdas.
|
||||
*/
|
||||
struct KSCoeffMat {
|
||||
std::vector<ComplexD> a; int n;
|
||||
KSCoeffMat(int _n) : a(_n*_n), n(_n) {}
|
||||
ComplexD& operator()(int i, int j) { return a[i*n+j]; }
|
||||
const ComplexD& operator()(int i, int j) const { return a[i*n+j]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for which Ritz values to keep in implicit restart. TODO move this and utilities into a new file
|
||||
*/
|
||||
enum RitzFilter {
|
||||
EvalNormSmall, // Keep evals with smallest norm
|
||||
EvalNormLarge, // Keep evals with largest norm
|
||||
EvalReSmall, // Keep evals with smallest real part
|
||||
EvalReLarge, // Keep evals with largest real part
|
||||
EvalReNormLarge, // Keep evals with largest real part
|
||||
EvalImSmall, // Keep evals with smallest imaginary part
|
||||
EvalImLarge, // Keep evals with largest imaginary part
|
||||
EvalImNormSmall, // Keep evals with smallest |imaginary| part
|
||||
EvalImNormLarge, // Keep evals with largest |imaginary| part
|
||||
};
|
||||
|
||||
/** Selects the RitzFilter corresponding to the input string. */
|
||||
inline RitzFilter selectRitzFilter(std::string s) {
|
||||
if (s == "EvalNormSmall") { return EvalNormSmall; } else
|
||||
if (s == "EvalNormLarge") { return EvalNormLarge; } else
|
||||
if (s == "EvalReSmall") { return EvalReSmall; } else
|
||||
if (s == "EvalReLarge") { return EvalReLarge; } else
|
||||
if (s == "EvalReNormLarge") { return EvalReNormLarge; } else
|
||||
if (s == "EvalImSmall") { return EvalImSmall; } else
|
||||
if (s == "EvalImLarge") { return EvalImLarge; } else
|
||||
if (s == "EvalImNormSmall") { return EvalImNormSmall; } else
|
||||
if (s == "EvalImNormLarge") { return EvalImNormLarge; } else
|
||||
{ assert(0); }
|
||||
}
|
||||
|
||||
/** Returns a string saying which RitzFilter it is. */
|
||||
inline std::string rfToString(RitzFilter RF) {
|
||||
switch (RF) {
|
||||
case EvalNormSmall:
|
||||
return "EvalNormSmall";
|
||||
case EvalNormLarge:
|
||||
return "EvalNormLarge";
|
||||
case EvalReSmall:
|
||||
return "EvalReSmall";
|
||||
case EvalReLarge:
|
||||
return "EvalReLarge";
|
||||
case EvalReNormLarge:
|
||||
return "EvalReNormLarge";
|
||||
case EvalImSmall:
|
||||
return "EvalImSmall";
|
||||
case EvalImLarge:
|
||||
return "EvalImLarge";
|
||||
case EvalImNormSmall:
|
||||
return "EvalImNormSmall";
|
||||
case EvalImNormLarge:
|
||||
return "EvalImNormLarge";
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Select comparison function from RitzFilter
|
||||
struct ComplexComparator
|
||||
{
|
||||
RitzFilter RF;
|
||||
ComplexComparator (RitzFilter _rf) : RF(_rf) {}
|
||||
bool operator()(std::complex<double> z1, std::complex<double> z2) {
|
||||
RealD tmp1=std::abs(std::imag(z1));
|
||||
RealD tmp2=std::abs(std::imag(z2));
|
||||
if ( std::abs(std::real(z1)) >2.) tmp1 += 100.;
|
||||
if ( std::abs(std::real(z2)) >2.) tmp2 += 100.;
|
||||
switch (RF) {
|
||||
case EvalNormSmall:
|
||||
return std::abs(z1) < std::abs(z2);
|
||||
case EvalNormLarge:
|
||||
return std::abs(z1) > std::abs(z2);
|
||||
case EvalReSmall:
|
||||
return std::real(z1) < std::real(z2); // DELETE THE ABS HERE!!!
|
||||
case EvalReLarge:
|
||||
return std::real(z1) > std::real(z2);
|
||||
case EvalReNormLarge:
|
||||
return std::abs(std::real(z1)) < std::abs(std::real(z2));
|
||||
case EvalImSmall:
|
||||
return std::imag(z1) < std::imag(z2);
|
||||
case EvalImLarge:
|
||||
return std::imag(z1) > std::imag(z2);
|
||||
case EvalImNormSmall:
|
||||
// return std::abs(std::imag(z1)) < std::abs(std::imag(z2));
|
||||
return tmp1 < tmp2 ;
|
||||
case EvalImNormLarge:
|
||||
return std::abs(std::imag(z1)) > std::abs(std::imag(z2));
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes a complex Schur decomposition of a complex matrix A using Eigen's matrix library. The Schur decomposition,
|
||||
* A = Q^\dag S Q
|
||||
* factorizes A into a unitary matrix Q and an upper triangular matrix S. The eigenvalues of A lie on the diagonal of the upper triangular matrix S.
|
||||
* The Schur decomposition is not unique: in particular, any ordering of the eigenvalues of A can be used as the diagonal of the matrix S.
|
||||
* This class supports eigenvalue reordering by swapping two adjacent eigenvalues with a unitary transformation.
|
||||
*/
|
||||
class ComplexSchurDecomposition {
|
||||
|
||||
private:
|
||||
|
||||
typedef Eigen::MatrixXcd CMat;
|
||||
|
||||
CMat A; // Matrix to decompose, A = Q^\dag S Q
|
||||
CMat Q; // Unitary matrix Q
|
||||
CMat S; // Upper triangular matrix S
|
||||
|
||||
// Placeholders for Givens rotation
|
||||
CMat Givens; // Givens rotation
|
||||
ComplexD s; // off-diagonal element
|
||||
ComplexD lam1; // First eval for swap
|
||||
ComplexD lam2; // Second eval for swap
|
||||
ComplexD phi; // phase of s
|
||||
RealD r; // norm of s and lam2 - lam1
|
||||
|
||||
int Nm; // size of matrix problem
|
||||
|
||||
ComplexComparator cCompare; // function to sort the Schur matrix.
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* If the input matrix _A is in Hessenberg form (upper triangular + first subdiagonal non-zero), then the Schur
|
||||
* decomposition is easier to compute.
|
||||
*/
|
||||
ComplexSchurDecomposition(CMat _A, bool isHess, RitzFilter ritzFilter = EvalReSmall) : A(_A), Nm (_A.rows()), cCompare (ritzFilter)
|
||||
{
|
||||
Eigen::ComplexSchur<CMat> schur (Nm);
|
||||
if (isHess) {
|
||||
schur.computeFromHessenberg(_A, CMat::Identity(Nm, Nm), true);
|
||||
} else {
|
||||
schur.compute(_A, true);
|
||||
}
|
||||
S = schur.matrixT();
|
||||
Q = schur.matrixU().adjoint(); // Eigen computes A = Q S Q^\dag, we want A = Q^\dag S Q
|
||||
}
|
||||
|
||||
// Getters
|
||||
int getNm() { return Nm; } // size of matrix problem
|
||||
CMat getMatrixA() { return A; } // matrix for decomposition
|
||||
CMat getMatrixQ() { return Q; } // unitary matrix Q
|
||||
CMat getMatrixS() { return S; } // Schur matrix (upper triangular) S
|
||||
CMat getRitz() { return S.diagonal(); }
|
||||
|
||||
/**
|
||||
* Checks the Schur decomposition A = Q^\dag S Q holds for the computed matrices. Returns if the relative
|
||||
* Frobenius norm || A - Q^\dag S Q || / || A || is less than rtol.
|
||||
*/
|
||||
bool checkDecomposition(RealD rtol = 1e-8) {
|
||||
RealD Anorm = A.norm();
|
||||
if (Anorm < rtol) {
|
||||
std::cout << GridLogMessage << "Zero matrix" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cout << GridLogDebug << "S = " << std::endl << S << std::endl;
|
||||
std::cout << GridLogDebug << "Q = " << std::endl << Q << std::endl;
|
||||
|
||||
CMat A2 = Q.adjoint() * S * Q;
|
||||
std::cout << GridLogDebug << "Q^dag S Q = " << std::endl << A2 << std::endl;
|
||||
RealD dA = (A - A2).norm() / Anorm;
|
||||
return (dA < rtol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the components on the diagonal of the Schur matrix at index i with index i + 1.
|
||||
* Updates the orthogonal matrix Q accordingly.
|
||||
*/
|
||||
void swapEvals(int i) {
|
||||
|
||||
assert(0 <= i && i <= Nm - 1); // can only swap blocks with upper left index between 0 and Nm - 1
|
||||
|
||||
// get parameters for rotation
|
||||
s = S(i, i+1);
|
||||
lam1 = S(i, i);
|
||||
lam2 = S(i+1, i+1);
|
||||
phi = s / abs(s);
|
||||
r = std::sqrt(std::pow(abs(s), 2) + std::pow(abs(lam2 - lam1), 2));
|
||||
|
||||
// // Original code which performs Givens rotations by manual matrix multiplication
|
||||
// // compute Givens rotation corresponding to these parameters
|
||||
// Givens = CMat::Identity(Nm, Nm);
|
||||
// Givens(i, i) = std::abs(s) / r;
|
||||
// Givens(i+1, i+1) = Givens(i, i);
|
||||
// Givens(i, i+1) = (phi / r) * std::conj(lam2 - lam1);
|
||||
// Givens(i+1, i) = -std::conj(Givens(i, i+1));
|
||||
// // rotate Schur matrix and unitary change of basis matrix Q
|
||||
// S = Givens * S * Givens.adjoint();
|
||||
// Q = Givens * Q;
|
||||
|
||||
// Modified code
|
||||
Givens = CMat::Identity(2, 2);
|
||||
Givens(0, 0) = abs(s) / r;
|
||||
Givens(1, 1) = Givens(0, 0);
|
||||
Givens(0, 1) = (phi / r) * conj(lam2 - lam1);
|
||||
Givens(1, 0) = -conj(Givens(0, 1));
|
||||
|
||||
// TODO: make sure these are correct
|
||||
Eigen::MatrixXcd tmp;
|
||||
tmp = Givens * S.block(i, 0, 2, Nm);
|
||||
S.block(i, 0, 2, Nm) = tmp;
|
||||
|
||||
// S_{:, i:i+2} = S.block<Nm, 2>(0, i);
|
||||
tmp = S.block(0, i, Nm, 2) * Givens.adjoint();
|
||||
S.block(0, i, Nm, 2) = tmp;
|
||||
|
||||
// Q_{i:i+2, :} = Q.block<2, Nm>(i, 0);
|
||||
tmp = Givens * Q.block(i, 0, 2, Nm);
|
||||
Q.block(i, 0, 2, Nm) = tmp;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders a Schur matrix &Schur to have the Ritz values that we would like to keep for
|
||||
* restart as the first Nk elements on the diagonal.
|
||||
*
|
||||
* This algorithm is implemented as Nk iterations of a a reverse bubble sort with comparator compare.
|
||||
* TODO: pass in compare function as an argument, default to compare with <.
|
||||
*/
|
||||
// void schurReorder(int Nk, std::function compare) {
|
||||
void schurReorder(int Nk) {
|
||||
std::cout << GridLogMessage << "schurReorder start " << std::endl;
|
||||
int nswap=0;
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
for (int k = 0; k <= Nm - 2; k++) {
|
||||
int idx = Nm - 2 - k;
|
||||
// TODO use RitzFilter enum here
|
||||
// if (std::abs(S(idx, idx)) < std::abs(S(idx+1, idx+1))) { // sort by largest modulus of eigenvalue
|
||||
// if (std::real(S(idx, idx)) > std::real(S(idx+1, idx+1))) { // sort by smallest real eigenvalue
|
||||
if ( cCompare(S(idx+1, idx+1), S(idx, idx)) ) { // sort by largest modulus of eigenvalue
|
||||
swapEvals(idx);
|
||||
nswap++;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << GridLogMessage << "schurReorder end "<< nswap << std::endl;
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
std::cout << GridLogMessage << "schurR "<<i<<" "<<S(i,i) << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void schurReorderBlock() {
|
||||
// TODO method stub
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// template<class Field>
|
||||
// inline void writeFile(const Field &field, const std::string &fname) {
|
||||
// emptyUserRecord record;
|
||||
// ScidacWriter WR(field.Grid()->IsBoss());
|
||||
// WR.open(fname);
|
||||
// WR.writeScidacFieldRecord(field, record, 0); // 0 = Lexico
|
||||
// WR.close();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Implementation of the Krylov-Schur algorithm.
|
||||
*/
|
||||
template<class Field>
|
||||
class KrylovSchur {
|
||||
|
||||
private:
|
||||
|
||||
std::string cname = std::string("KrylovSchur");
|
||||
int MaxIter; // Max iterations
|
||||
RealD Tolerance;
|
||||
RealD ssq;
|
||||
RealD rtol;
|
||||
int Nm; // Number of basis vectors to track (equals MaxIter if no restart)
|
||||
int Nk; // Number of basis vectors to keep every restart (equals -1 if no restart)
|
||||
int Nstop; // Stop after converging Nstop eigenvectors.
|
||||
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
RealD approxLambdaMax;
|
||||
RealD beta_k;
|
||||
Field u; // Residual vector perpendicular to Krylov space (u_{k+1} in notes)
|
||||
Eigen::VectorXcd b; // b vector in Schur decomposition (e_{k+1} in Arnoldi).
|
||||
std::vector<Field> basis; // orthonormal Krylov basis
|
||||
Eigen::MatrixXcd Rayleigh; // Rayleigh quotient of size Nbasis (after construction)
|
||||
Eigen::MatrixXcd Qt; // Transpose of basis rotation which projects out high modes.
|
||||
|
||||
Eigen::VectorXcd evals; // evals of Rayleigh quotient
|
||||
std::vector<RealD> ritzEstimates; // corresponding ritz estimates for evals
|
||||
Eigen::MatrixXcd littleEvecs; // Nm x Nm evecs matrix
|
||||
|
||||
RitzFilter ritzFilter; // how to sort evals
|
||||
|
||||
public:
|
||||
bool doEvalCheck = false;
|
||||
|
||||
KrylovSchur(LinearOperatorBase<Field> &_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)
|
||||
{
|
||||
u = Zero();
|
||||
};
|
||||
std::vector<Field> evecs; // Vector of evec fields
|
||||
|
||||
|
||||
/* Getters */
|
||||
int getNk() { return Nk; }
|
||||
Eigen::MatrixXcd getRayleighQuotient() { return Rayleigh; }
|
||||
Field getU() { return u; }
|
||||
std::vector<Field> getBasis() { return basis; }
|
||||
Eigen::VectorXcd getEvals() { return evals; }
|
||||
std::vector<RealD> getRitzEstimates() { return ritzEstimates; }
|
||||
std::vector<Field> getEvecs() { return evecs; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
|
||||
b = Eigen::VectorXcd::Zero(Nm); // start as e_{k+1}
|
||||
b(Nm-1) = 1.0;
|
||||
|
||||
int start = 0;
|
||||
Field startVec = v0;
|
||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
for (int i = 0; i < MaxIter; i++) {
|
||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
||||
|
||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
||||
startVec = u;
|
||||
start = Nk;
|
||||
|
||||
std::cout << GridLogDebug << "b after Arnoldi " << b << std::endl;
|
||||
|
||||
// --- Restart: Schur-decompose Rayleigh, reorder by ritzFilter, rotate
|
||||
// the basis into the Schur vectors, and truncate to the leading Nk.
|
||||
ComplexSchurDecomposition schur (Rayleigh, false, ritzFilter);
|
||||
std::cout << GridLogDebug << "Schur decomp holds? " << schur.checkDecomposition() << std::endl;
|
||||
|
||||
// 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;
|
||||
|
||||
Eigen::MatrixXcd Q = schur.getMatrixQ();
|
||||
Qt = Q.adjoint(); // TODO should Q be real?
|
||||
|
||||
std::cout << GridLogMessage << "*** ROTATING TO SCHUR BASIS *** " << std::endl;
|
||||
|
||||
// Rotate Krylov basis, b vector, redefine Rayleigh quotient and evecs, and truncate.
|
||||
Rayleigh = schur.getMatrixS();
|
||||
b = Q * b; // b^\dag = b^\dag * Q^\dag <==> b = Q*b
|
||||
|
||||
std::vector<Field> basis2;
|
||||
constructUR(basis2, basis, Qt, Nm,Nk);
|
||||
|
||||
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;
|
||||
|
||||
basis = std::vector<Field> (basis2.begin(), basis2.begin() + Nk);
|
||||
|
||||
b = b.head(Nk).eval();
|
||||
|
||||
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 (checkConvergedAndReport(i)) return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the harmonic (shifted) Krylov-Schur loop: extracts Ritz values of a
|
||||
* shift-augmented Rayleigh quotient so that eigenvalues near `*_shift` are
|
||||
* reordered to the top instead of the extremal ones.
|
||||
*/
|
||||
void operator()(const Field& v0, int _maxIter, int _Nm, int _Nk, int _Nstop, RealD *_shift, bool doubleOrthog = true) {
|
||||
|
||||
assert(_shift && "harmonic KrylovSchur: shift must be non-null");
|
||||
RealD shiftVal = *_shift;
|
||||
|
||||
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;
|
||||
|
||||
b = Eigen::VectorXcd::Zero(Nm); // start as e_{k+1}
|
||||
b(Nm-1) = 1.0;
|
||||
|
||||
int start = 0;
|
||||
Field startVec = v0;
|
||||
littleEvecs = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
for (int i = 0; i < MaxIter; i++) {
|
||||
std::cout << GridLogMessage << "Restart Iteration " << i << std::endl;
|
||||
|
||||
// Perform Arnoldi steps to compute Krylov basis and Rayleigh quotient (Hess)
|
||||
arnoldiIteration(startVec, Nm, start, doubleOrthog);
|
||||
startVec = u;
|
||||
start = Nk;
|
||||
|
||||
std::cout << GridLogDebug << "b after Arnoldi " << b << std::endl;
|
||||
|
||||
// --- Harmonic restart: Schur-decompose the shift-augmented Rayleigh
|
||||
// quotient so Ritz values close to shiftVal are reordered to the
|
||||
// top, then rotate and truncate as in the non-harmonic case.
|
||||
Eigen::MatrixXcd temp = Rayleigh;
|
||||
for (int m=0;m<Nm;m++) temp(m,m) -= shiftVal;
|
||||
Eigen::MatrixXcd RayleighS = temp.inverse(); // (B-tI)^-1
|
||||
Eigen::MatrixXcd temp2 = RayleighS.adjoint(); //(B-tI)^-1*
|
||||
Eigen::VectorXcd g = temp2*b; //g = (B-tI)^-1* * b
|
||||
Eigen::MatrixXcd Btilde = Rayleigh + g*(b.adjoint());
|
||||
|
||||
Field utilde(Grid);
|
||||
utilde = u;
|
||||
for (int j = 0; j<Nm; j++){
|
||||
utilde -= basis[j]*g(j);
|
||||
}
|
||||
|
||||
ComplexSchurDecomposition schurS (Btilde, false, ritzFilter);
|
||||
std::cout << GridLogMessage << "Shifted Schur eigenvalues shift = "<<shiftVal << std::endl;
|
||||
schurS.schurReorder(Nk);
|
||||
|
||||
Eigen::MatrixXcd Q_s = schurS.getMatrixQ();
|
||||
Eigen::MatrixXcd Qt_s = Q_s.adjoint(); // TODO should Q be real?
|
||||
|
||||
Btilde=schurS.getMatrixS();
|
||||
Eigen::VectorXcd b_s = Q_s*b; // Q is Qt in SlepC, b_s=bhat
|
||||
|
||||
std::vector<Field> basis2_s;
|
||||
constructUR(basis2_s, basis, Qt_s, Nm,Nk);
|
||||
|
||||
Eigen::MatrixXcd RayTmp_s = Btilde(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk));
|
||||
Btilde = RayTmp_s;
|
||||
|
||||
basis2_s = std::vector<Field> (basis2_s.begin(), basis2_s.begin() + Nk);
|
||||
|
||||
b_s = b_s.head(Nk).eval();
|
||||
|
||||
Eigen::VectorXcd ghat = -Q_s * g;
|
||||
ghat = ghat.head(Nk).eval();
|
||||
|
||||
Field uhat(Grid);
|
||||
uhat = utilde;
|
||||
for (int j = 0; j<Nk; j++){
|
||||
uhat -= basis2_s[j]*ghat(j);
|
||||
}
|
||||
|
||||
RealD gamma = std::sqrt(norm2(uhat));
|
||||
uhat *= 1.0/gamma;
|
||||
std::cout << GridLogMessage << " gamma "<<gamma << std::endl;
|
||||
|
||||
Btilde += ghat*(b_s.adjoint());
|
||||
b_s *=gamma;
|
||||
|
||||
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;
|
||||
|
||||
if (checkConvergedAndReport(i)) return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Shared post-restart convergence check. On convergence (or the final
|
||||
* iteration) reports eigenvalues and optionally the explicit residual
|
||||
* check. Returns true if operator() should return.
|
||||
*/
|
||||
bool checkConvergedAndReport(int i) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructs the Arnoldi basis for the Krylov space K_n(D, src). (TODO make private)
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* v0 : Field&
|
||||
* Source to generate Krylov basis.
|
||||
* Nm : int
|
||||
* Final size of the basis desired. If the basis becomes complete before a basis of size Nm is constructed
|
||||
* (determined by relative tolerance Tolerance), stops iteration there.
|
||||
* doubleOrthog : bool (default = false)
|
||||
* Whether to double orthogonalize the basis (for numerical cancellations) or not.
|
||||
* start : int (default = 0)
|
||||
* If non-zero, assumes part of the Arnoldi basis has already been constructed.
|
||||
*/
|
||||
void arnoldiIteration(const Field& v0, int Nm, int start = 0, bool doubleOrthog = true)
|
||||
{
|
||||
|
||||
ComplexD coeff;
|
||||
Field w (Grid); // A acting on last Krylov vector.
|
||||
|
||||
// basis.reserve(Nm);
|
||||
// for (int i = start; i < Nm; i++) {
|
||||
// basis.emplace_back(Grid);
|
||||
// }
|
||||
// basis.assign(Nm, Field(Grid));
|
||||
// basis.resize(Nm);
|
||||
// for (int i = start; i < Nm; i++) {
|
||||
// basis[i] = Field(Grid);
|
||||
// }
|
||||
|
||||
if (start == 0) { // initialize everything that we need.
|
||||
RealD v0Norm = 1 / std::sqrt(ssq);
|
||||
basis.push_back(v0Norm * v0); // normalized source
|
||||
// basis[0] = v0Norm * v0; // normalized source
|
||||
|
||||
Rayleigh = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
u = Zero();
|
||||
} else {
|
||||
// assert( start == basis.size() ); // should be starting at the end of basis (start = Nk)
|
||||
std::cout << GridLogMessage << "Resetting Rayleigh and b" << std::endl;
|
||||
Eigen::MatrixXcd RayleighCp = Rayleigh;
|
||||
Rayleigh = Eigen::MatrixXcd::Zero(Nm, Nm);
|
||||
Rayleigh(Eigen::seqN(0, Nk), Eigen::seqN(0, Nk)) = RayleighCp;
|
||||
|
||||
// append b^\dag to Rayleigh, add u to basis
|
||||
Rayleigh(Nk, Eigen::seqN(0, Nk)) = b.adjoint();
|
||||
basis.push_back(u);
|
||||
// basis[start] = u; // TODO make sure this is correct
|
||||
b = Eigen::VectorXcd::Zero(Nm);
|
||||
}
|
||||
|
||||
// Construct next Arnoldi vector by normalizing w_i = Dv_i - \sum_j v_j h_{ji}
|
||||
for (int i = start; i < Nm; i++) {
|
||||
Linop.Op(basis.back(), w);
|
||||
// Linop.Op(basis[i], w);
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
coeff = innerProduct(basis[j], w); // coeff = h_{ij}. Note that since {vi} is ONB it's OK to subtract it off after.
|
||||
Rayleigh(j, i) = toStdCmplx(coeff);
|
||||
w -= coeff * basis[j];
|
||||
}
|
||||
|
||||
if (doubleOrthog) {
|
||||
std::cout << GridLogDebug << "Double orthogonalizing." << std::endl;
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
coeff = innerProduct(basis[j], w); // see if there is any residual component in basis[j] direction
|
||||
Rayleigh(j, i) += toStdCmplx(coeff); // if coeff is non-zero, adjust Rayleigh
|
||||
w -= coeff * basis[j];
|
||||
}
|
||||
}
|
||||
|
||||
// add w_i to the pile
|
||||
if (i < Nm - 1) {
|
||||
coeff = std::sqrt(norm2(w));
|
||||
Rayleigh(i+1, i) = toStdCmplx(coeff);
|
||||
basis.push_back(
|
||||
(1.0/coeff) * w
|
||||
);
|
||||
// basis[i+1] = (1.0/coeff) * w;
|
||||
}
|
||||
|
||||
// after iterations, update u and beta_k = ||u|| before norm
|
||||
u = w; // make sure u is normalized
|
||||
beta_k = std::sqrt(norm2(u)); // beta_k = ||f_k|| determines convergence.
|
||||
u = (1/beta_k) * u;
|
||||
}
|
||||
|
||||
b(Nm - 1) = beta_k;
|
||||
|
||||
// std::cout << GridLogMessage << "|f|^2 after Arnoldi step = " << norm2(f) << std::endl;
|
||||
std::cout << GridLogMessage << "beta_k = |u| (before norm) after Arnoldi step = " << beta_k << std::endl;
|
||||
std::cout << GridLogDebug << "Computed Rayleigh quotient = " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the eigensystem of the linear operator by computing the eigensystem of
|
||||
* the Rayleigh quotient. Assumes that the Rayleigh quotient has already been constructed (by
|
||||
* calling the operator() function).
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Eigen::MatrixXcd& S
|
||||
* Schur matrix (upper triangular) similar to original Rayleigh quotient.
|
||||
*/
|
||||
void computeEigensystem(Eigen::MatrixXcd& S)
|
||||
{
|
||||
std::cout << GridLogMessage << "Computing eigenvalues." << std::endl;
|
||||
|
||||
evecs.clear();
|
||||
|
||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
||||
es.compute(S);
|
||||
|
||||
// Sort eigenvalues/evecs to match the schurReorder ordering.
|
||||
int n = es.eigenvalues().size();
|
||||
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)));
|
||||
});
|
||||
|
||||
evals.resize(n);
|
||||
littleEvecs.resize(n, n);
|
||||
for (int k = 0; k < n; k++) {
|
||||
evals(k) = es.eigenvalues()(idx[k]);
|
||||
littleEvecs.col(k) = es.eigenvectors().col(idx[k]);
|
||||
}
|
||||
|
||||
// Convert evecs to lattice fields
|
||||
for (int k = 0; k < n; k++) {
|
||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
||||
Field tmp (basis[0].Grid());
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < (int)basis.size(); j++) {
|
||||
tmp = tmp + vec[j] * basis[j];
|
||||
}
|
||||
evecs.push_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates the maximum eigenvalue of Linop.Op to normalize the residual and test for convergence.
|
||||
*
|
||||
* TODO implement in parent class eventually
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* Field& v0
|
||||
* Source field to start with. Must have non-zero norm.
|
||||
* int MAX_ITER (default = 50)
|
||||
* Maximum number of iterations for power approximation.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* RealD lamApprox
|
||||
* Approximation of largest eigenvalue.
|
||||
*/
|
||||
RealD approxMaxEval(const Field& v0, int MAX_ITER = 50) {
|
||||
assert (norm2(v0) > 1e-8); // must have relatively large source norm to start
|
||||
RealD lamApprox = 0.0;
|
||||
RealD denom = 1.0; RealD num = 1.0;
|
||||
Field v0cp (Grid); Field tmp (Grid);
|
||||
v0cp = v0;
|
||||
denom = std::sqrt(norm2(v0cp));
|
||||
for (int i = 0; i < MAX_ITER; i++) {
|
||||
Linop.Op(v0cp, tmp); // CAREFUL: do not do Op(tmp, tmp)
|
||||
v0cp = tmp;
|
||||
num = std::sqrt(norm2(v0cp)); // num = |A^{n+1} v0|
|
||||
lamApprox = num / denom; // lam = |A^{n+1} v0| / |A^n v0|
|
||||
std::cout << GridLogDebug << "Approx for max eval: " << lamApprox << std::endl;
|
||||
denom = num; // denom = |A^{n} v0|
|
||||
}
|
||||
return lamApprox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of Krylov-Schur eigenvectors that have converged. An eigenvector s is considered converged
|
||||
* for a tolerance epsilon if
|
||||
* r(s) := |\beta e_m^T s| < epsilon
|
||||
* where beta is the norm of f_{m+1}.
|
||||
*
|
||||
* TODO implement in parent class eventually
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* int : Number of converged eigenvectors.
|
||||
*/
|
||||
int converged() {
|
||||
int Nconv = 0;
|
||||
int _Nm = evecs.size();
|
||||
std::cout << GridLogDebug << "b: " << b << std::endl;
|
||||
Field tmp (Grid); Field fullEvec (Grid);
|
||||
ritzEstimates.clear();
|
||||
// ritzEstimates.resize(_Nm);
|
||||
for (int k = 0; k < _Nm; k++) {
|
||||
Eigen::VectorXcd evec_k = littleEvecs.col(k);
|
||||
RealD ritzEstimate = std::abs(b.dot(evec_k)); // b^\dagger s
|
||||
ritzEstimates.push_back(ritzEstimate);
|
||||
std::cout << GridLogMessage << "Ritz estimate for evec " << k << " = " << ritzEstimate << std::endl;
|
||||
if (ritzEstimate < rtol) {
|
||||
Nconv++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check that Ritz estimate is explicitly || D (Uy) - lambda (Uy) ||
|
||||
// checkRitzEstimate();
|
||||
return Nconv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Krylov-Schur decomposition DU = UR + f b^\dag with the last-computed
|
||||
* U, R, f, and b.
|
||||
*/
|
||||
bool checkKSDecomposition(RealD tol = 1e-8) {
|
||||
|
||||
std::cout << GridLogMessage << "*** CHECKING KRYLOV-SCHUR DECOMPOSITION *** " << std::endl;
|
||||
|
||||
int k = basis.size(); // number of basis vectors, also the size of Rayleigh.
|
||||
|
||||
// rotate basis by Rayleigh to construct UR
|
||||
// std::vector<Field> rotated;
|
||||
|
||||
// std::cout << GridLogDebug << "Rayleigh in KSDecomposition: " << std::endl << Rayleigh << std::endl;
|
||||
|
||||
std::vector<Field> rotated = basis;
|
||||
constructUR(rotated, basis, Rayleigh, k,k); // manually rotate
|
||||
// Eigen::MatrixXcd Rt = Rayleigh.adjoint();
|
||||
// basisRotate(rotated, Rt, 0, k, 0, k, k); // UR
|
||||
|
||||
// TODO: make a new function that I'm positive does what this is doing
|
||||
// just take the basis U = (u1 u2 ... uNm) and form the linear combination UR from R
|
||||
|
||||
// For each i, form D u(i) and subtract off (US - u b^\dag)(i)
|
||||
RealD delta = 0.0; RealD deltaSum = 0;
|
||||
Field tmp (Grid); tmp = Zero();
|
||||
for (int i = 0; i < k; i++) {
|
||||
Linop.Op(basis[i], tmp); // tmp = D u(i)
|
||||
delta = norm2(tmp - rotated[i] - u * std::conj(b(i)));
|
||||
delta = delta / norm2(tmp); // relative tolerance
|
||||
deltaSum += delta;
|
||||
|
||||
// std::cout << GridLogDebug << "Iteration " << i << std::endl;
|
||||
// std::cout << GridLogDebug << "Du = " << norm2(tmp) << std::endl;
|
||||
// std::cout << GridLogDebug << "rotated = " << norm2(rotated[i]) << std::endl;
|
||||
// std::cout << GridLogDebug << "b[i] = " << b(i) << std::endl;
|
||||
std::cout << GridLogMessage << "Deviation in decomp, column " << i << ": " << delta << std::endl;
|
||||
}
|
||||
std::cout << GridLogMessage << "Squared sum of relative deviations in decomposition: " << deltaSum << std::endl;
|
||||
|
||||
// std::cout << "[DEBUG] testing basis rotate" << std::endl;
|
||||
// std::vector<Field> rotated2;
|
||||
// constructUR(rotated2, basis, Rayleigh, k);
|
||||
// for (int i = 0; i < k; i++) {
|
||||
// std::cout << "rotated[i] - UR[i] = " << norm2(rotated[i] - rotated2[i]) << std::endl;
|
||||
// }
|
||||
|
||||
return deltaSum < tol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Ritz vector s was rotated correctly by explicitly recomputing the
|
||||
* eigenvectors of the rotated Rayleigh quotient.
|
||||
*/
|
||||
bool checkRitzRotation(RealD tol = 1e-8) {
|
||||
std::cout << GridLogMessage << "*** CHECKING RITZ VECTOR ROTATION *** " << std::endl;
|
||||
|
||||
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> es;
|
||||
es.compute(Rayleigh);
|
||||
Eigen::MatrixXcd littleEvecs2 = es.eigenvectors();
|
||||
RealD dLittle = (littleEvecs2 - littleEvecs).norm() / littleEvecs.norm();
|
||||
std::cout << GridLogMessage << "|littleEvecs2 - littleEvecs| / |littleEvecs| = " << dLittle << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Forming full eigenvectors" << std::endl;
|
||||
RealD delta = 0.0; RealD deltaSum = 0;
|
||||
for (int k = 0; k < evals.size(); k++) {
|
||||
Eigen::VectorXcd vec = littleEvecs.col(k);
|
||||
Field tmpEvec (Grid);
|
||||
tmpEvec = Zero();
|
||||
for (int j = 0; j < basis.size(); j++) {
|
||||
tmpEvec = tmpEvec + vec[j] * basis[j];
|
||||
}
|
||||
delta = norm2(tmpEvec - evecs[k]) / norm2(evecs[k]);
|
||||
std::cout << GridLogMessage << "Deviation in evec " << k << ": " << delta << std::endl;
|
||||
deltaSum += delta;
|
||||
}
|
||||
return deltaSum < tol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Ritz estimate R(s) is indeed the deviation of a Ritz eigenvector from being a true eigenvector.
|
||||
*/
|
||||
void checkRitzEstimate(RealD tol = 1e-8) {
|
||||
std::cout << GridLogMessage << "*** CHECKING RITZ ESTIMATE *** " << std::endl;
|
||||
|
||||
// The issue was that the Eigen::eigensolver occasionally returned the complex conjugate pairs in the wrong
|
||||
// order compared to the diagonal, which is how I was reading them out. When this happened, the Ritz estimate would
|
||||
// be wrong. So, just need to be more careful and actually read out the eigenvalues.
|
||||
|
||||
Field tmp (Grid);
|
||||
// std::cout << "n evecs: " << evecs.size() << std::endl;
|
||||
for (int k = 0; k < evecs.size(); k++) {
|
||||
tmp = Zero();
|
||||
Linop.Op(evecs[k], tmp); // D evec
|
||||
RealD ritz = std::sqrt(norm2(tmp - evals[k] * evecs[k]));
|
||||
std::cout << "Ritz estimate " << k << " = " << ritz << std::endl;
|
||||
|
||||
// Checking little Ritz estimate
|
||||
// Eigen::VectorXcd littleEvec = littleEvecs.col(k);
|
||||
// Eigen::VectorXcd dev = Rayleigh * littleEvec - evals[k] * littleEvec;
|
||||
// std::cout << GridLogMessage << "Little Ritz estimate = " << dev.norm() << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a vector of fields U (equivalently, a LxN matrix, where L is the number of degrees of
|
||||
* freedom on the lattice field) and an NxN matrix R, forms the product UR.
|
||||
* Only the first N2 output columns are computed; columns [N2, N) are Zero.
|
||||
*
|
||||
* Implemented with the accelerator basisRotate kernel, which computes
|
||||
* basis[j] <- sum_k Qt(j,k) basis[k] in place, so we copy U into UR and
|
||||
* pass R^T. The coefficients are staged as ComplexD (thrust::complex on
|
||||
* GPU builds) since std::complex has no device arithmetic with Grid tensors.
|
||||
*/
|
||||
void constructUR(std::vector<Field>& UR, std::vector<Field> &U, Eigen::MatrixXcd& R, int N, int N2) {
|
||||
KSCoeffMat Rt(N);
|
||||
for (int i = 0; i < N; i++)
|
||||
for (int j = 0; j < N; j++)
|
||||
Rt(i, j) = ComplexD(R(j, i).real(), R(j, i).imag());
|
||||
|
||||
UR = U;
|
||||
basisRotate(UR, Rt, 0, N2, 0, N, N);
|
||||
for (int i = N2; i < N; i++) UR[i] = Zero();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as constructUR but for the product order RU.
|
||||
*/
|
||||
void constructRU(std::vector<Field>& RU, std::vector<Field> &U, Eigen::MatrixXcd& R, int N) {
|
||||
Field tmp (Grid);
|
||||
RU.clear();
|
||||
// RU.resize(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
tmp = Zero();
|
||||
for (int j = 0; j < N; j++) {
|
||||
tmp = tmp + R(i, j) * U[j];
|
||||
}
|
||||
RU.push_back(tmp);
|
||||
// RU[i] = tmp;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// void writeEvec(Field& in, std::string const fname){
|
||||
// #ifdef HAVE_LIME
|
||||
// // Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
// std::cout << GridLogMessage << "Writing evec to: " << fname << std::endl;
|
||||
// Grid::emptyUserRecord record;
|
||||
// Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
// WR.open(fname);
|
||||
// WR.writeScidacFieldRecord(in,record,0); // Lexico
|
||||
// WR.close();
|
||||
// #endif
|
||||
// // What is the appropriate way to throw error?
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Writes the eigensystem of a Krylov Schur object to a directory.
|
||||
// *
|
||||
// * Parameters
|
||||
// * ----------
|
||||
// * std::string path
|
||||
// * Directory to write to.
|
||||
// */
|
||||
// void writeEigensystem(std::string outDir) {
|
||||
// std::cout << GridLogMessage << "Writing output to directory: " << outDir << std::endl;
|
||||
// // TODO write a scidac density file so that we can easily integrate with visualization toolkit
|
||||
// std::string evalPath = outDir + "/evals.txt";
|
||||
// std::ofstream fEval;
|
||||
// fEval.open(evalPath);
|
||||
// for (int i = 0; i < Nk; i++) {
|
||||
// // write Eigenvalues
|
||||
// fEval << i << " " << evals(i);
|
||||
// if (i < Nk - 1) { fEval << "\n"; }
|
||||
// }
|
||||
// fEval.close();
|
||||
|
||||
// for (int i = 0; i < Nk; i++) {
|
||||
// std::string fName = outDir + "/evec" + std::to_string(i);
|
||||
// // writeFile(evecs[i], fName); // using method from Grid/HMC/ComputeWilsonFlow.cc
|
||||
// // writeEvec(evecs[i], fName);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -0,0 +1,276 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./Grid/algorithms/iterative/LanczosBidiagonalization.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_LANCZOS_BIDIAGONALIZATION_H
|
||||
#define GRID_LANCZOS_BIDIAGONALIZATION_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Lanczos Bidiagonalization (Golub-Kahan)
|
||||
*
|
||||
* For a linear operator A with adjoint A^dag, constructs the bidiagonal
|
||||
* decomposition:
|
||||
*
|
||||
* A V_m = U_m B_m
|
||||
* A^dag U_m = V_m B_m^T + beta_{m+1} v_{m+1} e_m^T
|
||||
*
|
||||
* where:
|
||||
* V_m = [v_1, ..., v_m] right Lanczos vectors (orthonormal)
|
||||
* U_m = [u_1, ..., u_m] left Lanczos vectors (orthonormal)
|
||||
* B_m is upper bidiagonal with diag(alpha_1,...,alpha_m) and
|
||||
* superdiag(beta_2,...,beta_m)
|
||||
*
|
||||
* The singular values of A are approximated by those of B_m.
|
||||
* The singular values of B_m are the square roots of the eigenvalues of
|
||||
* the symmetric tridiagonal matrix B_m^T B_m.
|
||||
*
|
||||
* Usage:
|
||||
* LanczosBidiagonalization<Field> lb(Linop, grid);
|
||||
* lb.run(src, Nm, tol);
|
||||
* // Access results via getters.
|
||||
*/
|
||||
template <class Field>
|
||||
class LanczosBidiagonalization {
|
||||
|
||||
public:
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
int Nm; // number of Lanczos steps taken
|
||||
RealD Tolerance; // convergence threshold on beta_{k+1} / alpha_k
|
||||
|
||||
std::vector<Field> V; // right Lanczos vectors v_1 ... v_m
|
||||
std::vector<Field> U; // left Lanczos vectors u_1 ... u_m
|
||||
std::vector<RealD> alpha; // diagonal of bidiagonal matrix
|
||||
std::vector<RealD> beta; // super-diagonal (beta[k] couples u_k and v_{k+1})
|
||||
|
||||
// SVD of the bidiagonal matrix (filled after computeSVD())
|
||||
Eigen::VectorXd singularValues;
|
||||
Eigen::MatrixXd leftSVecs; // columns are left singular vectors of B
|
||||
Eigen::MatrixXd rightSVecs; // columns are right singular vectors of B
|
||||
|
||||
public:
|
||||
|
||||
LanczosBidiagonalization(LinearOperatorBase<Field> &_Linop, GridBase *_Grid,
|
||||
RealD _tol = 1.0e-8)
|
||||
: Linop(_Linop), Grid(_Grid), Tolerance(_tol), Nm(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Run the Golub-Kahan Lanczos bidiagonalization.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* src : starting vector (need not be normalised)
|
||||
* Nmax : maximum number of Lanczos steps
|
||||
* reorth : if true, full reorthogonalisation of both V and U bases
|
||||
*/
|
||||
void run(const Field &src, int Nmax, bool reorth = true)
|
||||
{
|
||||
assert(norm2(src) > 0.0);
|
||||
|
||||
V.clear(); U.clear();
|
||||
alpha.clear(); beta.clear();
|
||||
Nm = 0;
|
||||
|
||||
Field p(Grid), r(Grid);
|
||||
|
||||
// --- initialise: v_1 = src / ||src|| ---
|
||||
Field v(Grid);
|
||||
v = src;
|
||||
RealD nrm = std::sqrt(norm2(v));
|
||||
v = (1.0 / nrm) * v;
|
||||
V.push_back(v);
|
||||
|
||||
for (int k = 0; k < Nmax; ++k) {
|
||||
|
||||
// p = A v_k
|
||||
Linop.Op(V[k], p);
|
||||
|
||||
// p = p - beta_k * u_{k-1} (remove previous left vector)
|
||||
if (k > 0) {
|
||||
p = p - beta[k-1] * U[k-1];
|
||||
}
|
||||
|
||||
// alpha_k = ||p||
|
||||
RealD ak = std::sqrt(norm2(p));
|
||||
if (ak < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization: lucky breakdown at step "
|
||||
<< k << " (alpha = " << ak << ")" << std::endl;
|
||||
break;
|
||||
}
|
||||
alpha.push_back(ak);
|
||||
|
||||
// u_k = p / alpha_k
|
||||
Field u(Grid);
|
||||
u = (1.0 / ak) * p;
|
||||
|
||||
// full reorthogonalisation of u against previous U
|
||||
if (reorth) {
|
||||
for (int j = 0; j < (int)U.size(); ++j) {
|
||||
ComplexD ip = innerProduct(U[j], u);
|
||||
u = u - ip * U[j];
|
||||
}
|
||||
RealD unrm = std::sqrt(norm2(u));
|
||||
if (unrm > 1.0e-14) u = (1.0 / unrm) * u;
|
||||
}
|
||||
U.push_back(u);
|
||||
|
||||
// r = A^dag u_k - alpha_k * v_k
|
||||
Linop.AdjOp(U[k], r);
|
||||
r = r - ak * V[k];
|
||||
|
||||
// full reorthogonalisation of r against previous V
|
||||
if (reorth) {
|
||||
for (int j = 0; j < (int)V.size(); ++j) {
|
||||
ComplexD ip = innerProduct(V[j], r);
|
||||
r = r - ip * V[j];
|
||||
}
|
||||
}
|
||||
|
||||
// beta_{k+1} = ||r||
|
||||
RealD bk = std::sqrt(norm2(r));
|
||||
beta.push_back(bk);
|
||||
|
||||
Nm = k + 1;
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization step " << k
|
||||
<< " alpha = " << ak
|
||||
<< " beta = " << bk << std::endl;
|
||||
|
||||
// convergence: residual beta / alpha small enough
|
||||
if (bk / ak < Tolerance) {
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization converged at step " << k
|
||||
<< " (beta/alpha = " << bk / ak << ")" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (k == Nmax - 1) break; // no v_{k+2} needed after last step
|
||||
|
||||
// v_{k+1} = r / beta_{k+1}
|
||||
Field vnext(Grid);
|
||||
vnext = (1.0 / bk) * r;
|
||||
V.push_back(vnext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the SVD of the bidiagonal matrix B using Eigen.
|
||||
* Singular values are stored in descending order.
|
||||
*/
|
||||
void computeSVD()
|
||||
{
|
||||
int m = Nm;
|
||||
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(m, m);
|
||||
|
||||
for (int k = 0; k < m; ++k) {
|
||||
B(k, k) = alpha[k];
|
||||
if (k + 1 < m && k < (int)beta.size())
|
||||
B(k, k+1) = beta[k];
|
||||
}
|
||||
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
|
||||
singularValues = svd.singularValues(); // already sorted descending
|
||||
leftSVecs = svd.matrixU();
|
||||
rightSVecs = svd.matrixV();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization: singular values of B_" << m
|
||||
<< std::endl;
|
||||
for (int k = 0; k < m; ++k)
|
||||
std::cout << GridLogMessage << " sigma[" << k << "] = "
|
||||
<< singularValues(k) << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the k-th approximate left singular vector of A in the full
|
||||
* lattice space. computeSVD() must have been called first.
|
||||
*/
|
||||
Field leftSingularVector(int k)
|
||||
{
|
||||
assert(k < (int)leftSVecs.cols());
|
||||
Field svec(Grid);
|
||||
svec = Zero();
|
||||
for (int j = 0; j < Nm; ++j)
|
||||
svec = svec + leftSVecs(j, k) * U[j];
|
||||
return svec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the k-th approximate right singular vector of A in the full
|
||||
* lattice space. computeSVD() must have been called first.
|
||||
*/
|
||||
Field rightSingularVector(int k)
|
||||
{
|
||||
assert(k < (int)rightSVecs.cols());
|
||||
Field svec(Grid);
|
||||
svec = Zero();
|
||||
for (int j = 0; j < Nm; ++j)
|
||||
svec = svec + rightSVecs(j, k) * V[j];
|
||||
return svec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the bidiagonalization: returns max residual
|
||||
* max_k || A v_k - alpha_k u_k - beta_k u_{k-1} ||
|
||||
*/
|
||||
RealD verify()
|
||||
{
|
||||
Field tmp(Grid);
|
||||
RealD maxres = 0.0;
|
||||
for (int k = 0; k < Nm; ++k) {
|
||||
Linop.Op(V[k], tmp);
|
||||
tmp = tmp - alpha[k] * U[k];
|
||||
if (k > 0 && k-1 < (int)beta.size())
|
||||
tmp = tmp - beta[k-1] * U[k-1];
|
||||
RealD res = std::sqrt(norm2(tmp));
|
||||
if (res > maxres) maxres = res;
|
||||
std::cout << GridLogMessage
|
||||
<< "LanczosBidiagonalization verify step " << k
|
||||
<< " ||A v_k - alpha_k u_k - beta_{k-1} u_{k-1}|| = "
|
||||
<< res << std::endl;
|
||||
}
|
||||
return maxres;
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
int getNm() const { return Nm; }
|
||||
const std::vector<Field>& getV() const { return V; }
|
||||
const std::vector<Field>& getU() const { return U; }
|
||||
const std::vector<RealD>& getAlpha() const { return alpha; }
|
||||
const std::vector<RealD>& getBeta() const { return beta; }
|
||||
Eigen::VectorXd getSingularValues() const { return singularValues; }
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -60,18 +60,29 @@ public:
|
||||
|
||||
void Level(int lv) { level=lv; };
|
||||
|
||||
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
|
||||
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax, int _nstep) :
|
||||
Tolerance(tol),
|
||||
MaxIterations(maxit),
|
||||
Linop(_Linop),
|
||||
Preconditioner(Prec),
|
||||
mmax(_mmax),
|
||||
nstep(_nstep)
|
||||
nstep(_nstep) // what is nstep vs mmax? one is the number of inner iterations
|
||||
{
|
||||
level=1;
|
||||
verbose=1;
|
||||
};
|
||||
|
||||
// virtual method stubs for updating GCR polynomial
|
||||
virtual void LogBegin(void){
|
||||
std::cout << "GCR::LogBegin() "<<std::endl;
|
||||
};
|
||||
virtual void LogIteration(int k, ComplexD a, std::vector<ComplexD> betas){
|
||||
std::cout << "GCR::LogIteration() "<<std::endl;
|
||||
};
|
||||
virtual void LogComplete(std::vector<ComplexD>& alphas, std::vector<std::vector<ComplexD>>& betas) {
|
||||
std::cout << "GCR::LogComplete() "<<std::endl;
|
||||
};
|
||||
|
||||
void operator() (const Field &src, Field &psi){
|
||||
|
||||
// psi=Zero();
|
||||
@@ -96,19 +107,18 @@ public:
|
||||
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<" target "<<rsq <<std::endl;
|
||||
|
||||
if(cp<rsq) {
|
||||
SolverTimer.Stop();
|
||||
|
||||
SolverTimer.Stop();
|
||||
Linop.Op(psi,r);
|
||||
axpy(r,-1.0,src,r);
|
||||
RealD tr = norm2(r);
|
||||
GCRLogLevel<<"PGCR: Converged on iteration " <<steps
|
||||
<< " computed residual "<<sqrt(cp/ssq)
|
||||
<< " true residual " <<sqrt(tr/ssq)
|
||||
<< " target " <<Tolerance <<std::endl;
|
||||
|
||||
Linop.Op(psi,r);
|
||||
axpy(r,-1.0,src,r);
|
||||
RealD tr = norm2(r);
|
||||
GCRLogLevel<<"PGCR: Converged on iteration " <<steps
|
||||
<< " computed residual "<<sqrt(cp/ssq)
|
||||
<< " true residual " <<sqrt(tr/ssq)
|
||||
<< " target " <<Tolerance <<std::endl;
|
||||
|
||||
GCRLogLevel<<"PGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
|
||||
return;
|
||||
GCRLogLevel<<"PGCR Time elapsed: Total "<< SolverTimer.Elapsed() <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -135,9 +145,9 @@ public:
|
||||
////////////////////////////////
|
||||
// history for flexible orthog
|
||||
////////////////////////////////
|
||||
std::vector<Field> q(mmax,grid);
|
||||
std::vector<Field> p(mmax,grid);
|
||||
std::vector<RealD> qq(mmax);
|
||||
std::vector<Field> q(mmax,grid); // q = Ap
|
||||
std::vector<Field> p(mmax,grid); // store mmax conjugate momenta
|
||||
std::vector<RealD> qq(mmax); // qq = (Ap)^2 = <p|A^\dagger A |p> (denom of \alpha)
|
||||
|
||||
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
|
||||
|
||||
@@ -155,7 +165,9 @@ public:
|
||||
LinalgTimer.Start();
|
||||
r=src-Az;
|
||||
LinalgTimer.Stop();
|
||||
GCRLogLevel<< "PGCR true residual r = src - A psi "<<norm2(r) <<std::endl;
|
||||
GCRLogLevel<< "PGCR true residual r = src - A psi "<< norm2(r) <<std::endl;
|
||||
|
||||
this->LogBegin(); // initialize polynomial GCR if needed (TODO think about placement of this)
|
||||
|
||||
/////////////////////
|
||||
// p = Prec(r)
|
||||
@@ -178,32 +190,45 @@ public:
|
||||
p[0]= z;
|
||||
q[0]= Az;
|
||||
qq[0]= zAAz;
|
||||
|
||||
std::cout << "||init p - src||: " << norm2(p[0] - src) << std::endl; // for debugging
|
||||
|
||||
cp =norm2(r);
|
||||
LinalgTimer.Stop();
|
||||
|
||||
std::vector<ComplexD> all_alphas;
|
||||
std::vector<std::vector<ComplexD>> all_betas;
|
||||
|
||||
for(int k=0;k<nstep;k++){
|
||||
|
||||
steps++;
|
||||
|
||||
int kp = k+1;
|
||||
int peri_k = k %mmax;
|
||||
int peri_k = k %mmax; // only store mmax vectors; just roll around if needed
|
||||
int peri_kp= kp%mmax;
|
||||
|
||||
// std::cout << "peri_kp = " << peri_kp << std::endl;
|
||||
|
||||
LinalgTimer.Start();
|
||||
rq= innerProduct(q[peri_k],r); // what if rAr not real?
|
||||
a = rq/qq[peri_k];
|
||||
a = rq/qq[peri_k]; // compute alpha_j
|
||||
|
||||
axpy(psi,a,p[peri_k],psi);
|
||||
all_alphas.push_back(a);
|
||||
|
||||
cp = axpy_norm(r,-a,q[peri_k],r);
|
||||
axpy(psi,a,p[peri_k],psi); // update psi --> psi + \alpha p
|
||||
|
||||
cp = axpy_norm(r,-a,q[peri_k],r); // update r --> r - \alpha D p. Note q = Dp
|
||||
LinalgTimer.Stop();
|
||||
|
||||
GCRLogLevel<< "PGCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
|
||||
// LogIterationA(k + 1, a);
|
||||
|
||||
if((k==nstep-1)||(cp<rsq)){
|
||||
return cp;
|
||||
}
|
||||
GCRLogLevel<< "GCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
|
||||
|
||||
// moving this to end of loop so that it doesn't exit beforehand
|
||||
// TODO if I want to uncomment this, I have to split the LogIteration again and put LogIterationA() beforehand
|
||||
// if((k==nstep-1)||(cp<rsq)){
|
||||
// return cp;
|
||||
// }
|
||||
|
||||
|
||||
PrecTimer.Start();
|
||||
@@ -221,22 +246,205 @@ public:
|
||||
q[peri_kp]=Az;
|
||||
p[peri_kp]=z;
|
||||
|
||||
// Field Dsrc (grid);
|
||||
// Linop.Op(src, Dsrc);
|
||||
// std::cout << "||q[peri_kp] - D(src)||: " << norm2(q[peri_kp] - Dsrc) << std::endl; // for debugging
|
||||
|
||||
// // delete after testing
|
||||
// std::cout << "Testing Dsq on one for GCR: " << std::endl;
|
||||
// Field myField (grid);
|
||||
// myField = 1.0;
|
||||
// Field out1 (grid); Field out2 (grid);
|
||||
// Linop.HermOp(myField, out1);
|
||||
// Linop.Op(myField, out2);
|
||||
// std::cout << "Dsq.Hermop(ones) has norm " << norm2(out1) << std::endl;
|
||||
// std::cout << "Dsq.Op(ones) has norm " << norm2(out2) << std::endl;
|
||||
|
||||
// basically northog = k+1 if mmax is large
|
||||
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
|
||||
// std::cout << "northog: " << northog << std::endl;
|
||||
std::vector<ComplexD> betas (northog);
|
||||
// std::cout << "peri_kp: " << peri_kp << std::endl;
|
||||
// we iterate backwards counting down from the current k+1 index (peri_kp) because we
|
||||
for(int back=0;back<northog;back++){
|
||||
|
||||
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
|
||||
|
||||
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
||||
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
||||
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
||||
// b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
|
||||
b=-(innerProduct(q[peri_back],Az))/qq[peri_back]; // TODO try complex beta
|
||||
p[peri_kp]=p[peri_kp]+b*p[peri_back];
|
||||
q[peri_kp]=q[peri_kp]+b*q[peri_back];
|
||||
|
||||
// LogIterationB(peri_back, b);
|
||||
// betas[back] = b; // may need to change the indexing if I ever do it with restarts
|
||||
// std::cout << "[DEBUG] pushing beta for back = " << back << ", peri_back = " << peri_back << std::endl;
|
||||
|
||||
betas[peri_back] = b; // may need to change the indexing if I ever do it with restarts
|
||||
|
||||
}
|
||||
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
|
||||
LinalgTimer.Stop();
|
||||
|
||||
// log iteration and update GCR polynomial if necessary.
|
||||
all_betas.push_back(betas);
|
||||
LogIteration(k + 1, a, betas);
|
||||
|
||||
// finish if necessary
|
||||
if((k==nstep-1)||(cp<rsq)){
|
||||
std::cout << "All alphas: " << std::endl << all_alphas << std::endl;
|
||||
std::cout << "All betas: " << std::endl << all_betas << std::endl;
|
||||
LogComplete(all_alphas, all_betas);
|
||||
std::cout << "Exiting GCR." << std::endl;
|
||||
return cp;
|
||||
}
|
||||
|
||||
}
|
||||
GRID_ASSERT(0); // never reached
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
|
||||
class PolynomialFile: Serializable {
|
||||
public:
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(PolynomialFile,
|
||||
std::vector<std::vector<std::complex<double>>>, data,
|
||||
std::vector<std::vector<std::complex<double>>>, betas,
|
||||
std::vector<std::complex<double>>, alphas
|
||||
);
|
||||
};
|
||||
|
||||
// Optionally record the GCR polynomial. [PO]: TODO
|
||||
template <class Field>
|
||||
class PGCRPolynomial : public PrecGeneralisedConjugateResidualNonHermitian<Field> {
|
||||
public:
|
||||
std::vector<ComplexD> ak;
|
||||
std::vector<std::vector<ComplexD>> bk;
|
||||
// std::vector<ComplexD> poly_p;
|
||||
std::vector<std::vector<ComplexD>> poly_p;
|
||||
std::vector<ComplexD> poly_Ap; // polynomial in Ap_j (only store it for last p)
|
||||
std::vector<ComplexD> poly_r;
|
||||
std::vector<ComplexD> polynomial;
|
||||
|
||||
PolynomialFile& PF;
|
||||
|
||||
public:
|
||||
PGCRPolynomial(RealD tol, Integer maxit,LinearOperatorBase<Field> &_Linop, LinearFunction<Field> &Prec, int _mmax, int _nstep, PolynomialFile& _PF)
|
||||
: PrecGeneralisedConjugateResidualNonHermitian<Field>(tol, maxit, _Linop, Prec, _mmax, _nstep), PF(_PF)
|
||||
{};
|
||||
|
||||
// think this applies the polynomial in A = Linop to a field src. The coeffs are
|
||||
// stored in the vector `polynomial`.
|
||||
void PolyOp(const Field &src, Field &psi)
|
||||
{
|
||||
Field tmp(src.Grid());
|
||||
Field AtoN(src.Grid());
|
||||
AtoN = src;
|
||||
psi=AtoN*polynomial[0];
|
||||
for(int n=1;n<polynomial.size();n++){
|
||||
tmp = AtoN;
|
||||
this->Linop.Op(tmp,AtoN); // iterate A^n
|
||||
psi = psi + polynomial[n]*AtoN; // psi += poly_n A^n src
|
||||
}
|
||||
}
|
||||
|
||||
// [PO TODO] debug this
|
||||
void PGCRsequence(const Field &src, Field &x)
|
||||
{
|
||||
Field Ap(src.Grid());
|
||||
Field r(src.Grid());
|
||||
// Field p(src.Grid());
|
||||
// p=src;
|
||||
std::vector<Field> p;
|
||||
p.push_back(src);
|
||||
r=src;
|
||||
x=Zero();
|
||||
x.Checkerboard()=src.Checkerboard();
|
||||
for(int k=0;k<ak.size();k++){
|
||||
x = x + ak[k]*p[k];
|
||||
this->Linop.Op(p[k], Ap);
|
||||
r = r - ak[k] * Ap;
|
||||
// p[k] = r;
|
||||
p.push_back(r);
|
||||
for (int i = 0; i < k; i++) { // [PO TODO] check indices
|
||||
p[k+1] += bk[i, k+1] * p[i];
|
||||
}
|
||||
// p = r + bk[k] * p;
|
||||
}
|
||||
}
|
||||
|
||||
void Solve(const Field &src, Field &psi)
|
||||
{
|
||||
psi=Zero();
|
||||
this->operator()(src, psi);
|
||||
}
|
||||
|
||||
virtual void LogBegin(void)
|
||||
{
|
||||
std::cout << "PGCR::LogBegin() "<<std::endl;
|
||||
ak.resize(0);
|
||||
bk.resize(0);
|
||||
polynomial.resize(0);
|
||||
poly_Ap.push_back(0.0); // start with (0.0); during first iteration should change to (0.0, 1.0)
|
||||
std::vector<ComplexD> p0_tmp;
|
||||
p0_tmp.push_back(1.0);
|
||||
poly_p.push_back(p0_tmp);
|
||||
poly_r.push_back(1.0);
|
||||
};
|
||||
|
||||
// Updates vector psi and r and initializes vector p[k+1]
|
||||
virtual void LogIteration(int k, ComplexD a, std::vector<ComplexD> betas){
|
||||
std::cout << "PGCR::LogIteration(k = " << k << ")" << std::endl;
|
||||
ak.push_back(a);
|
||||
bk.push_back(betas);
|
||||
|
||||
// update Ap by pushing p[k] to the right
|
||||
poly_Ap.push_back(0.0); // need to pad the end with an element
|
||||
poly_Ap[0] = 0.0; // technically this should be unnecessary, as the first component is never set
|
||||
for(int i = 0; i < k; i++){
|
||||
poly_Ap[i+1]=poly_p[k-1][i]; // A\vec{p} = (0, \vec{p}) bc A shifts components of p to the right
|
||||
}
|
||||
|
||||
// update psi_{k+1} --> psi_k + a_k p_k
|
||||
polynomial.push_back(0.0);
|
||||
for(int i = 0; i < k; i++) {
|
||||
polynomial[i] += a * poly_p[k-1][i];
|
||||
}
|
||||
{
|
||||
std::vector<std::complex<double>> poly_stdcmplx(polynomial.begin(), polynomial.end());
|
||||
PF.data.push_back(poly_stdcmplx);
|
||||
}
|
||||
|
||||
// r_{k+1} --> r_k - a_k A p_k
|
||||
// p_{k+1} --> r_k + \sum_{i=0}^k \beta_{ik} p_i, input betas = (\beta_{ik})_i
|
||||
poly_r.push_back(0.0); // should be of size k+1 if we start with k = 1
|
||||
std::vector<ComplexD> p_next (k + 1, ComplexD(0.0)); // p_{k+1} = same size as r_{k+1}
|
||||
for(int i = 0; i < k + 1; i++){
|
||||
poly_r[i] = poly_r[i] - a * poly_Ap[i]; // update r_{k+1} --> r_k - \alpha_k A p_k
|
||||
p_next[i] = poly_r[i]; // init new vector as r_{k+1}
|
||||
}
|
||||
|
||||
// p_{k+1} --> p_{k+1} + \sum_i \beta_{ij} p_i
|
||||
int nbeta = betas.size();
|
||||
std::cout << "Betas: " << betas << std::endl;
|
||||
for (int j = 0; j < nbeta; j++) {
|
||||
for (int i = 0; i < j+1; i++) {
|
||||
p_next[i] += betas[j] * poly_p[j][i];
|
||||
}
|
||||
}
|
||||
poly_p.push_back(p_next); // add p_{k+1} to the list of p's
|
||||
}
|
||||
|
||||
virtual void LogComplete(std::vector<ComplexD>& alphas, std::vector<std::vector<ComplexD>>& betas) {
|
||||
/** Logs all alphas and betas to complete the iterations. */
|
||||
std::cout << "PGCR::LogComplete() "<<std::endl;
|
||||
for (int i = 0; i < alphas.size(); i++) {
|
||||
PF.alphas.push_back(std::complex<double>(alphas[i].real(), alphas[i].imag()));
|
||||
std::vector<std::complex<double>> beta_stdcmplx(betas[i].begin(), betas[i].end());
|
||||
PF.betas.push_back(beta_stdcmplx);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,753 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./Grid/algorithms/iterative/RestartedLanczosBidiagonalization.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_RESTARTED_LANCZOS_BIDIAGONALIZATION_H
|
||||
#define GRID_RESTARTED_LANCZOS_BIDIAGONALIZATION_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Implicitly Restarted Lanczos Bidiagonalization (IRLBA)
|
||||
*
|
||||
* Computes the p largest (or p smallest) singular triplets of a linear
|
||||
* operator A using the Golub-Kahan-Lanczos bidiagonalization with implicit
|
||||
* restart via thick-restart / QR shifts.
|
||||
*
|
||||
* Algorithm (Baglama & Reichel, SIAM J. Sci. Comput. 27(1):19-42, 2005):
|
||||
*
|
||||
* Outer loop:
|
||||
* 1. Extend the p-step (or seed) bidiagonalization to k steps:
|
||||
* A V_k = U_k B_k
|
||||
* A^dag U_k = V_k B_k^T + beta_{k+1} v_{k+1} e_k^T
|
||||
* 2. Compute SVD: B_k = X Sigma Y^T
|
||||
* 3. Check convergence of the p desired singular values via
|
||||
* |beta_{k+1} * y_{k,i}| < tol * sigma_i
|
||||
* where y_{k,i} is the last component of the i-th right singular vector.
|
||||
* 4. Apply k-p implicit QR shifts to implicitly compress the basis
|
||||
* to p steps (Sorensen-Lehoucq thick restart):
|
||||
* B_p^+ = X_p^T B_k Y_p (upper bidiagonal, p x p)
|
||||
* and update the lattice vectors:
|
||||
* V_p^+ = V_k Y_p
|
||||
* U_p^+ = U_k X_p
|
||||
* The new residual coupling is
|
||||
* beta_p^+ v_{p+1}^+ = beta_{k+1} v_{k+1} * (e_k^T Y_p)_p
|
||||
* + B_k(p,p+1) * (orthogonal tail from QR)
|
||||
* 5. Go to step 1.
|
||||
*
|
||||
* Template parameter
|
||||
* ------------------
|
||||
* Field : lattice field type (must support Grid algebra operations)
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
* RestartedLanczosBidiagonalization<Field> irlba(Linop, grid, p, k, tol, maxIter);
|
||||
* irlba.run(src);
|
||||
* // Results available via getters.
|
||||
*/
|
||||
template <class Field>
|
||||
class RestartedLanczosBidiagonalization {
|
||||
|
||||
public:
|
||||
LinearOperatorBase<Field> &Linop;
|
||||
GridBase *Grid;
|
||||
|
||||
int Nk; // number of desired singular triplets
|
||||
int Nm; // Lanczos basis size (Nm > Nk)
|
||||
RealD Tolerance;
|
||||
int MaxIter;
|
||||
bool largest; // if true, target largest singular values; otherwise smallest
|
||||
|
||||
// Converged singular triplets (filled after run())
|
||||
std::vector<RealD> singularValues; // sigma_0 >= sigma_1 >= ...
|
||||
std::vector<Field> leftVectors; // approximate left singular vectors
|
||||
std::vector<Field> rightVectors; // approximate right singular vectors
|
||||
|
||||
private:
|
||||
// Working bases (size up to Nm+1)
|
||||
std::vector<Field> V; // right Lanczos vectors
|
||||
std::vector<Field> U; // left Lanczos vectors
|
||||
std::vector<RealD> alpha;
|
||||
std::vector<RealD> beta;
|
||||
|
||||
// After a thick restart, the column at index restart_col of U^dag A V
|
||||
// has extra non-zero entries (rows 0..restart_col-2) beyond what the
|
||||
// upper bidiagonal captures. fvec[j] = <U[j] | A V[restart_col]> for
|
||||
// j = 0..restart_col-1. (fvec[restart_col-1] == beta[restart_col-1].)
|
||||
// reset_col == -1 means no restart has occurred yet (pure bidiagonal).
|
||||
std::vector<RealD> fvec;
|
||||
int restart_col;
|
||||
|
||||
public:
|
||||
|
||||
RestartedLanczosBidiagonalization(LinearOperatorBase<Field> &_Linop,
|
||||
GridBase *_Grid,
|
||||
int _Nk, int _Nm,
|
||||
RealD _tol = 1.0e-8,
|
||||
int _maxIt = 300,
|
||||
bool _largest = true)
|
||||
: Linop(_Linop), Grid(_Grid),
|
||||
Nk(_Nk), Nm(_Nm),
|
||||
Tolerance(_tol), MaxIter(_maxIt),
|
||||
largest(_largest)
|
||||
{
|
||||
assert(Nm > Nk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run IRLBA starting from src.
|
||||
* On exit, singularValues, leftVectors, rightVectors are filled with
|
||||
* the Nk converged singular triplets.
|
||||
*/
|
||||
void run(const Field &src)
|
||||
{
|
||||
assert(norm2(src) > 0.0);
|
||||
|
||||
singularValues.clear();
|
||||
leftVectors.clear();
|
||||
rightVectors.clear();
|
||||
|
||||
// Allocate working bases
|
||||
V.clear(); U.clear();
|
||||
alpha.clear(); beta.clear();
|
||||
fvec.clear(); restart_col = -1;
|
||||
V.reserve(Nm + 1);
|
||||
U.reserve(Nm);
|
||||
|
||||
// Seed: v_0 = src / ||src||
|
||||
Field vtmp(Grid);
|
||||
vtmp = src;
|
||||
RealD nrm = std::sqrt(norm2(vtmp));
|
||||
vtmp = (1.0 / nrm) * vtmp;
|
||||
V.push_back(vtmp);
|
||||
|
||||
int pStart = 0; // current basis size at start of extension
|
||||
RealD betaRestart = 0.0; // coupling from previous restart
|
||||
|
||||
for (int iter = 0; iter < MaxIter; ++iter) {
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 1: extend from pStart steps to Nm steps
|
||||
// ----------------------------------------------------------------
|
||||
extendBasis(pStart, Nm, betaRestart);
|
||||
// verify();
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 2: SVD of the Nm x Nm B matrix.
|
||||
// iter=0 (pStart==0): B is exactly bidiagonal — use buildBidiagonal.
|
||||
// iter>0 (pStart==Nk): after a thick restart, column restart_col of
|
||||
// U^dag A V has extra off-diagonal entries captured by fvec; use
|
||||
// buildFullB so the Ritz values and restart vectors are computed from
|
||||
// the exact projected matrix A V = U B_full.
|
||||
// ----------------------------------------------------------------
|
||||
Eigen::MatrixXd B = (pStart == 0) ? buildBidiagonal(Nm) : buildFullB(Nm);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
|
||||
Eigen::VectorXd sigma = svd.singularValues(); // descending
|
||||
Eigen::MatrixXd X = svd.matrixU(); // Nm x Nm left SVecs of B
|
||||
Eigen::MatrixXd Y = svd.matrixV(); // Nm x Nm right SVecs of B
|
||||
|
||||
// If targeting smallest, reorder so desired ones come first
|
||||
Eigen::VectorXi order = sortOrder(sigma);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 3: check convergence of the Nk desired singular values
|
||||
// ----------------------------------------------------------------
|
||||
RealD betaK = beta.back(); // beta_{k+1}
|
||||
// In our convention A V = U B (exact), the residual is in the A^dag
|
||||
// direction: A^dag u_j - sigma_j v_j = betaK * X[Nm-1,j] * V[Nm].
|
||||
// Convergence criterion: |betaK * X[Nm-1, idx]| < tol * sigma_idx.
|
||||
int nconv = 0;
|
||||
for (int i = 0; i < Nk; ++i) {
|
||||
int idx = order(i);
|
||||
RealD res = std::abs(betaK * X(Nm - 1, idx));
|
||||
RealD thr = Tolerance * std::max(sigma(idx), 1.0e-14);
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA iter " << iter
|
||||
<< " sigma[" << i << "] = " << sigma(idx)
|
||||
<< " res = " << res
|
||||
<< " thr = " << thr << std::endl;
|
||||
if (res < thr) ++nconv;
|
||||
else break; // residuals not strictly ordered but break is conservative
|
||||
}
|
||||
|
||||
if (nconv >= Nk) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA converged: " << nconv << " singular values after "
|
||||
<< iter + 1 << " iterations." << std::endl;
|
||||
// Collect converged triplets
|
||||
extractTriplets(Nm, sigma, X, Y, order, Nk);
|
||||
return;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Step 4: implicit restart — compress to Nk steps
|
||||
// ----------------------------------------------------------------
|
||||
implicitRestart(Nm, Nk, sigma, X, Y, order, betaK, betaRestart);
|
||||
// verify();
|
||||
|
||||
// Lucky breakdown: exact invariant subspace found; convergence is exact.
|
||||
// B_p^+ = diag(alpha[0..Nk-1]); extract directly from restart basis.
|
||||
if (betaRestart < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA: lucky breakdown after restart (betaRestart = 0)."
|
||||
<< " Extracting " << Nk << " exact Ritz triplets." << std::endl;
|
||||
// Re-run SVD on the p-step diagonal B^+ to get sorted Ritz triplets.
|
||||
Eigen::MatrixXd Bp = buildBidiagonal(Nk);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svdp(Bp,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
Eigen::VectorXi ordp = sortOrder(svdp.singularValues());
|
||||
extractTriplets(Nk, svdp.singularValues(), svdp.matrixU(),
|
||||
svdp.matrixV(), ordp, Nk);
|
||||
return;
|
||||
}
|
||||
|
||||
pStart = Nk;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA: did not converge in " << MaxIter
|
||||
<< " iterations. Returning best approximations." << std::endl;
|
||||
|
||||
// Return best available approximations
|
||||
Eigen::MatrixXd B = buildFullB((int)alpha.size());
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(B,
|
||||
Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
Eigen::VectorXd sigma = svd.singularValues();
|
||||
Eigen::MatrixXd X = svd.matrixU();
|
||||
Eigen::MatrixXd Y = svd.matrixV();
|
||||
Eigen::VectorXi order = sortOrder(sigma);
|
||||
int nout = std::min(Nk, (int)alpha.size());
|
||||
extractTriplets((int)alpha.size(), sigma, X, Y, order, nout);
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
int getNk() const { return (int)singularValues.size(); }
|
||||
const std::vector<RealD>& getSingularValues() const { return singularValues; }
|
||||
const std::vector<Field>& getLeftVectors() const { return leftVectors; }
|
||||
const std::vector<Field>& getRightVectors() const { return rightVectors; }
|
||||
|
||||
/**
|
||||
* Print B_k and U^dag A V to verify the bidiagonalization relation
|
||||
* A V_m = U_m B_m (exact in our GK convention)
|
||||
* On the first call (pStart=0), max|B - U^dag A V| should be ~machine epsilon.
|
||||
* After a restart and extension, the column p of U^dag A V deviates from B
|
||||
* by O(betaK): this is expected because the thick restart breaks the Krylov
|
||||
* structure at column p, introducing off-diagonal terms proportional to betaK.
|
||||
* These terms vanish as betaK -> 0 (convergence), so the algorithm is correct.
|
||||
*/
|
||||
void verify()
|
||||
{
|
||||
int m = (int)alpha.size();
|
||||
int nU = (int)U.size();
|
||||
int nV = (int)V.size();
|
||||
if (m == 0) { std::cout << GridLogMessage << "IRLBA verify: empty basis" << std::endl; return; }
|
||||
|
||||
// Build reference matrix Bref (nU x nV):
|
||||
// Columns 0..m-1 : buildFullB(m) (bidiagonal + fvec column at restart_col)
|
||||
// Column m : residual column, two cases:
|
||||
// (a) restart_col == m (right after implicitRestart, before extendBasis):
|
||||
// V[m] = sgn*V_old[Nm], so <U[i]|A|V[m]> = fvec[i] for all i
|
||||
// (b) otherwise (pure GK or after extendBasis):
|
||||
// only entry (m-1, m) = beta[m-1] (GK recurrence residual)
|
||||
Eigen::MatrixXd Bref = Eigen::MatrixXd::Zero(nU, nV);
|
||||
{
|
||||
Eigen::MatrixXd Bfull = buildFullB(m);
|
||||
int cols = std::min(m, nV);
|
||||
Bref.block(0, 0, m, cols) = Bfull.block(0, 0, m, cols);
|
||||
}
|
||||
if (nV > m && m > 0) {
|
||||
if (restart_col == m && (int)fvec.size() == m) {
|
||||
// Case (a): right after implicitRestart
|
||||
for (int i = 0; i < m; ++i) Bref(i, m) = fvec[i];
|
||||
} else if ((int)beta.size() >= m) {
|
||||
// Case (b): standard GK residual column
|
||||
Bref(m - 1, m) = beta[m - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute M[i,j] = <U[i] | A | V[j]>
|
||||
Eigen::MatrixXd M = Eigen::MatrixXd::Zero(nU, nV);
|
||||
Field Avj(Grid);
|
||||
for (int j = 0; j < nV; ++j) {
|
||||
Linop.Op(V[j], Avj);
|
||||
for (int i = 0; i < nU; ++i) {
|
||||
ComplexD ip = innerProduct(U[i], Avj);
|
||||
M(i, j) = ip.real();
|
||||
}
|
||||
}
|
||||
|
||||
// Print Bref
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA verify: Bref (" << nU << "x" << nV << "):" << std::endl;
|
||||
for (int i = 0; i < nU; ++i) {
|
||||
std::cout << GridLogMessage << " row " << i << ": ";
|
||||
for (int j = 0; j < nV; ++j) std::cout << Bref(i,j) << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Print U^dag A V
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA verify: U^dag A V (" << nU << "x" << nV << "):" << std::endl;
|
||||
for (int i = 0; i < nU; ++i) {
|
||||
std::cout << GridLogMessage << " row " << i << ": ";
|
||||
for (int j = 0; j < nV; ++j) std::cout << M(i,j) << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Max deviation over the full nU x nV matrix
|
||||
RealD maxdev = (Bref - M).cwiseAbs().maxCoeff();
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA verify: max|Bref - U^dag A V| = " << maxdev << std::endl;
|
||||
|
||||
// Beta
|
||||
std::cout << GridLogMessage << "IRLBA verify: beta[0.." << (int)beta.size()-1 << "] = ";
|
||||
for (auto b : beta) std::cout << b << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Build the m x m upper-bidiagonal matrix from alpha[0..m-1], beta[0..m-2]
|
||||
// ------------------------------------------------------------------
|
||||
Eigen::MatrixXd buildBidiagonal(int m) const
|
||||
{
|
||||
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(m, m);
|
||||
for (int k = 0; k < m; ++k) {
|
||||
B(k, k) = alpha[k];
|
||||
if (k + 1 < m && k < (int)beta.size())
|
||||
B(k, k + 1) = beta[k];
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Build the full m x m B matrix, including the non-bidiagonal column
|
||||
// at restart_col that arises after a thick restart.
|
||||
//
|
||||
// After restart, A V[restart_col] has projections onto all U[0..restart_col-1]
|
||||
// (not just U[restart_col-1]). These are stored in fvec[0..restart_col-1]
|
||||
// and make column restart_col of U^dag A V non-bidiagonal.
|
||||
// ------------------------------------------------------------------
|
||||
Eigen::MatrixXd buildFullB(int m) const
|
||||
{
|
||||
Eigen::MatrixXd B = buildBidiagonal(m);
|
||||
if (restart_col >= 0 && restart_col < m && (int)fvec.size() > 0) {
|
||||
for (int j = 0; j < restart_col && j < (int)fvec.size(); ++j){
|
||||
B(j, restart_col) = fvec[j];
|
||||
std::cout << GridLogDebug << "buildFullB: B " <<j<<" "<<restart_col<<B(j, restart_col)<<std::endl;
|
||||
}
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Return a permutation vector that puts the desired Nk singular values
|
||||
// first (largest first if largest==true, smallest first otherwise).
|
||||
// Eigen's JacobiSVD already returns sigma in descending order, so for
|
||||
// largest we just return 0,1,...,m-1; for smallest we reverse.
|
||||
// ------------------------------------------------------------------
|
||||
Eigen::VectorXi sortOrder(const Eigen::VectorXd &sigma) const
|
||||
{
|
||||
int m = (int)sigma.size();
|
||||
Eigen::VectorXi ord(m);
|
||||
if (largest) {
|
||||
for (int i = 0; i < m; ++i) ord(i) = i;
|
||||
} else {
|
||||
for (int i = 0; i < m; ++i) ord(i) = m - 1 - i;
|
||||
}
|
||||
return ord;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Extend the Lanczos bidiagonalization from pStart to kEnd steps.
|
||||
// On first call pStart==0 (V[0] already set).
|
||||
// On restart calls V[0..pStart], U[0..pStart-1], alpha[0..pStart-1],
|
||||
// beta[0..pStart-1] are already set; betaRestart is the coupling
|
||||
// beta_{pStart} that drives the first new U step.
|
||||
// ------------------------------------------------------------------
|
||||
void extendBasis(int pStart, int kEnd, RealD betaRestart)
|
||||
{
|
||||
// Truncate containers to pStart (Lattice has no default constructor)
|
||||
if ((int)V.size() > pStart + 1) V.erase(V.begin() + pStart + 1, V.end());
|
||||
if ((int)U.size() > pStart) U.erase(U.begin() + pStart, U.end());
|
||||
alpha.resize(pStart);
|
||||
beta.resize(pStart);
|
||||
|
||||
Field p(Grid), r(Grid);
|
||||
|
||||
for (int k = pStart; k < kEnd; ++k) {
|
||||
|
||||
// p = A v_k
|
||||
Linop.Op(V[k], p);
|
||||
|
||||
// Remove previous left vector coupling
|
||||
if (k > 0) {
|
||||
p = p - beta[k - 1] * U[k - 1];
|
||||
}
|
||||
// On the first step after a restart, beta[pStart-1] was already set;
|
||||
// but V[pStart] was already constructed including the beta correction,
|
||||
// so no extra subtraction needed here beyond the standard recurrence.
|
||||
|
||||
// Reorthogonalize p against U, then alpha_k = ||p||, u_k = p/alpha_k
|
||||
reorthogonalize(p, U);
|
||||
RealD ak = std::sqrt(norm2(p));
|
||||
if (ak < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA extendBasis: lucky breakdown at step " << k
|
||||
<< " (alpha = " << ak << ")" << std::endl;
|
||||
alpha.push_back(ak);
|
||||
Field zero(Grid); zero = Zero();
|
||||
U.push_back(zero);
|
||||
beta.push_back(0.0);
|
||||
V.push_back(zero);
|
||||
break;
|
||||
}
|
||||
alpha.push_back(ak);
|
||||
|
||||
Field u(Grid);
|
||||
u = (1.0 / ak) * p;
|
||||
U.push_back(u);
|
||||
|
||||
// r = A^dag u_k - alpha_k v_k, reorthogonalize, then beta_{k+1} = ||r||
|
||||
Linop.AdjOp(U[k], r);
|
||||
r = r - ak * V[k];
|
||||
reorthogonalize(r, V);
|
||||
|
||||
RealD bk = std::sqrt(norm2(r));
|
||||
beta.push_back(bk);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA extend step " << k
|
||||
<< " alpha = " << ak
|
||||
<< " beta = " << bk << std::endl;
|
||||
|
||||
// Always push v_{k+1} (needed as residual direction for restart)
|
||||
if (bk < 1.0e-14) {
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA extendBasis: lucky breakdown (beta = 0) at step "
|
||||
<< k << std::endl;
|
||||
Field zero(Grid); zero = Zero();
|
||||
V.push_back(zero);
|
||||
break;
|
||||
}
|
||||
Field vnext(Grid);
|
||||
vnext = (1.0 / bk) * r;
|
||||
V.push_back(vnext);
|
||||
|
||||
if (k == kEnd - 1) break; // v_{k+1} pushed above; stop here
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------
|
||||
// Block reorthogonalization helpers.
|
||||
// Declared public because CUDA extended lambdas cannot live inside
|
||||
// private/protected member functions.
|
||||
//
|
||||
// batchInnerProducts: computes c[j] = <basis[j], vec> for all j
|
||||
// in a single GPU pass (one accelerator_barrier instead of n).
|
||||
// Queues n pairs of (per-site kernel, reduceKernel) to computeStream
|
||||
// without intermediate CPU syncs, then syncs once at the end.
|
||||
//
|
||||
// batchUpdate: computes vec -= sum_j c[j]*basis[j] in one GPU kernel.
|
||||
//
|
||||
// reorthogonalize: two-pass Classical Gram-Schmidt (CGS2) using the
|
||||
// two helpers above. Each pass costs 2 GPU syncs (1 IP + 1 update)
|
||||
// instead of 2n syncs per pass in the old sequential MGS.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
void batchInnerProducts(const Field &vec,
|
||||
const std::vector<Field> &basis,
|
||||
std::vector<ComplexD> &c)
|
||||
{
|
||||
int n = (int)basis.size();
|
||||
c.resize(n);
|
||||
if (n == 0) return;
|
||||
|
||||
typedef typename Field::vector_object vobj;
|
||||
typedef decltype(innerProduct(vobj(), vobj())) inner_t;
|
||||
typedef decltype(basis[0].View(AcceleratorRead)) View;
|
||||
|
||||
GridBase *grid = vec.Grid();
|
||||
uint64_t oSites = grid->oSites();
|
||||
uint64_t nsimd = grid->Nsimd();
|
||||
|
||||
// all_ip[j * oSites + ss] = per-site inner product of basis[j] and vec at site ss.
|
||||
// Layout: n contiguous blocks of oSites each.
|
||||
deviceVector<inner_t> all_ip((uint64_t)n * oSites);
|
||||
inner_t *all_ip_p = &all_ip[0];
|
||||
|
||||
hostVector<View> h_basis_v(n);
|
||||
deviceVector<View> d_basis_v(n);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
h_basis_v[j] = basis[j].View(AcceleratorRead);
|
||||
acceleratorPut(d_basis_v[j], h_basis_v[j]);
|
||||
}
|
||||
View *basis_vp = &d_basis_v[0];
|
||||
|
||||
// Queue n per-site kernels to the accelerator stream — no intermediate barriers.
|
||||
autoView(vec_v, vec, AcceleratorRead);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
int jj = j;
|
||||
uint64_t oSites_ = oSites;
|
||||
accelerator_for(ss, oSites, nsimd, {
|
||||
auto x = coalescedRead(basis_vp[jj][ss]);
|
||||
auto y = coalescedRead(vec_v[ss]);
|
||||
coalescedWrite(all_ip_p[jj * oSites_ + ss], innerProduct(x, y));
|
||||
});
|
||||
}
|
||||
|
||||
// ONE sync after all n kernels
|
||||
accelerator_barrier();
|
||||
|
||||
// Copy all per-site results to host
|
||||
hostVector<inner_t> all_ip_h((uint64_t)n * oSites);
|
||||
acceleratorCopyFromDevice(all_ip_p, &all_ip_h[0], (uint64_t)n * oSites * sizeof(inner_t));
|
||||
|
||||
// Reduce on host: sum over oSites, then collapse SIMD lanes via Reduce(TensorRemove(...))
|
||||
// TensorRemove strips the iSinglet tensor wrapper to expose the SIMD scalar type.
|
||||
// Reduce sums all nsimd lanes and returns a plain scalar (RealD or ComplexD).
|
||||
std::vector<ComplexD> raw(n);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
inner_t sum = Zero();
|
||||
for (uint64_t ss = 0; ss < oSites; ++ss)
|
||||
sum += all_ip_h[(uint64_t)j * oSites + ss];
|
||||
raw[j] = ComplexD(Reduce(TensorRemove(sum)));
|
||||
}
|
||||
grid->GlobalSumVector(&raw[0], n);
|
||||
for (int j = 0; j < n; ++j) c[j] = raw[j];
|
||||
|
||||
for (int j = 0; j < n; ++j) h_basis_v[j].ViewClose();
|
||||
}
|
||||
|
||||
void batchUpdate(Field &vec,
|
||||
const std::vector<Field> &basis,
|
||||
const std::vector<ComplexD> &c)
|
||||
{
|
||||
int n = (int)basis.size();
|
||||
if (n == 0) return;
|
||||
|
||||
typedef typename Field::vector_object vobj;
|
||||
typedef decltype(basis[0].View(AcceleratorRead)) View;
|
||||
|
||||
GridBase *grid = vec.Grid();
|
||||
uint64_t oSites = grid->oSites();
|
||||
uint64_t nsimd = grid->Nsimd();
|
||||
|
||||
// Split complex coefficients into real/imag double arrays on device.
|
||||
// Using doubles avoids potential ComplexD-device-code compatibility issues.
|
||||
hostVector<double> h_re(n), h_im(n);
|
||||
deviceVector<double> d_re(n), d_im(n);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
h_re[k] = c[k].real();
|
||||
h_im[k] = c[k].imag();
|
||||
}
|
||||
acceleratorCopyToDevice(&h_re[0], &d_re[0], n * sizeof(double));
|
||||
acceleratorCopyToDevice(&h_im[0], &d_im[0], n * sizeof(double));
|
||||
double *re_p = &d_re[0];
|
||||
double *im_p = &d_im[0];
|
||||
|
||||
// Basis views
|
||||
hostVector<View> h_basis_v(n);
|
||||
deviceVector<View> d_basis_v(n);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
h_basis_v[k] = basis[k].View(AcceleratorRead);
|
||||
acceleratorPut(d_basis_v[k], h_basis_v[k]);
|
||||
}
|
||||
View *basis_vp = &d_basis_v[0];
|
||||
|
||||
// Single kernel: vec[ss] -= sum_k (re[k] + i*im[k]) * basis[k][ss]
|
||||
autoView(vec_v, vec, AcceleratorWrite);
|
||||
accelerator_for(ss, oSites, nsimd, {
|
||||
auto v = coalescedRead(vec_v[ss]);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
auto b = coalescedRead(basis_vp[k][ss]);
|
||||
v = v - re_p[k] * b - timesI(im_p[k] * b);
|
||||
}
|
||||
coalescedWrite(vec_v[ss], v);
|
||||
});
|
||||
|
||||
for (int k = 0; k < n; ++k) h_basis_v[k].ViewClose();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Full reorthogonalization using two-pass Classical Gram-Schmidt (CGS2).
|
||||
// Each pass calls batchInnerProducts (1 GPU sync) + batchUpdate (1 sync),
|
||||
// replacing the old 2n GPU syncs per pass from sequential MGS.
|
||||
// ------------------------------------------------------------------
|
||||
void reorthogonalize(Field &vec, const std::vector<Field> &basis)
|
||||
{
|
||||
if (basis.empty()) return;
|
||||
std::vector<ComplexD> c;
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
batchInnerProducts(vec, basis, c);
|
||||
batchUpdate(vec, basis, c);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Implicit restart: given the Nm-step bidiagonalization and its SVD,
|
||||
// compress to Nk steps via implicit QR shifts applied to B_k.
|
||||
//
|
||||
// The "shifts" are the Nm - Nk singular values we want to deflate
|
||||
// (those NOT in the desired set). We apply them as implicit QR steps
|
||||
// to the bidiagonal matrix, then update the lattice bases accordingly.
|
||||
//
|
||||
// After this call:
|
||||
// V[0..Nk], U[0..Nk-1], alpha[0..Nk-1], beta[0..Nk-1] are updated.
|
||||
// betaRestart ← new beta_Nk coupling for the next extension.
|
||||
// ------------------------------------------------------------------
|
||||
void implicitRestart(int k, int p,
|
||||
const Eigen::VectorXd &sigma,
|
||||
const Eigen::MatrixXd &X,
|
||||
const Eigen::MatrixXd &Y,
|
||||
const Eigen::VectorXi &order,
|
||||
RealD betaK,
|
||||
RealD &betaRestart)
|
||||
{
|
||||
// Thick restart (Baglama & Reichel, Sec. 2.2):
|
||||
//
|
||||
// Given B_k = X Sigma Y^T, define the new p-step basis by:
|
||||
// V^+_i = V_k * y_{order(i)} (right sing. vec. of B_k)
|
||||
// U^+_i = U_k * x_{order(i)} (left sing. vec. of B_k)
|
||||
//
|
||||
// Then A V^+_i = A V_k y_{order(i)} = U_k B_k y_{order(i)}
|
||||
// = sigma_{order(i)} U_k x_{order(i)} = sigma_{order(i)} U^+_i
|
||||
//
|
||||
// So B_p^+ = diag(sigma_{order(0)}, ..., sigma_{order(p-1)}) — DIAGONAL,
|
||||
// all internal betas are zero.
|
||||
//
|
||||
// The residual coupling comes from A^dag U_k = V_k B_k^T + betaK V[k] e_{k-1}^T:
|
||||
// A^dag U^+_{p-1} - sigma_{order(p-1)} V^+_{p-1}
|
||||
// = V_k (B_k^T x_{order(p-1)} - sigma_{order(p-1)} y_{order(p-1)})
|
||||
// + betaK * X(k-1, order(p-1)) * V[k]
|
||||
// = betaK * X(k-1, order(p-1)) * V[k] (since B_k^T x_j = sigma_j y_j)
|
||||
//
|
||||
// Therefore: betaRestart = |betaK * X(k-1, order(p-1))|
|
||||
// V[p] = sign(X(k-1, order(p-1))) * V[k]
|
||||
|
||||
// ---- Build new lattice vectors ----
|
||||
std::vector<Field> Vnew, Unew;
|
||||
Vnew.reserve(p + 1);
|
||||
Unew.reserve(p);
|
||||
|
||||
for (int i = 0; i < p; ++i) {
|
||||
int idx = order(i);
|
||||
Field vi(Grid); vi = Zero();
|
||||
for (int j = 0; j < k; ++j)
|
||||
vi = vi + Y(j, idx) * V[j];
|
||||
Vnew.push_back(vi);
|
||||
}
|
||||
|
||||
for (int i = 0; i < p; ++i) {
|
||||
int idx = order(i);
|
||||
Field ui(Grid); ui = Zero();
|
||||
for (int j = 0; j < k; ++j)
|
||||
ui = ui + X(j, idx) * U[j];
|
||||
Unew.push_back(ui);
|
||||
}
|
||||
|
||||
// New v_{p} (0-indexed: V[p]) = sign * V[k]
|
||||
// From A^dag U_k = V_k B_k^T + betaK V[k] e_{k-1}^T:
|
||||
// A^dag U^+_j - sigma_j V^+_j = betaK * X(k-1, order(j)) * V[k]
|
||||
// The last Ritz pair (j=p-1) defines betaRestart and the sign of V[p].
|
||||
// All p couplings (j=0..p-1) are stored in fvec so that buildFullB can
|
||||
// reconstruct the exact column p of U^dag A V after the next extension.
|
||||
RealD coeff = betaK * X(k - 1, order(p - 1));
|
||||
betaRestart = std::abs(coeff);
|
||||
RealD sgn = (coeff >= 0.0) ? 1.0 : -1.0;
|
||||
|
||||
fvec.resize(p);
|
||||
for (int j = 0; j < p; ++j)
|
||||
fvec[j] = betaK * X(k - 1, order(j)) * sgn;
|
||||
// fvec[p-1] == betaRestart by construction
|
||||
restart_col = p;
|
||||
|
||||
Field vp(Grid);
|
||||
if (betaRestart > 1.0e-14) {
|
||||
vp = sgn * V[k];
|
||||
} else {
|
||||
betaRestart = 0.0;
|
||||
vp = Zero();
|
||||
}
|
||||
Vnew.push_back(vp); // V[p]
|
||||
|
||||
// ---- New alpha, beta ----
|
||||
// B_p^+ is diagonal: alpha^+_i = sigma_{order(i)}, all internal beta = 0
|
||||
std::vector<RealD> alpha_new(p), beta_new(p);
|
||||
for (int i = 0; i < p; ++i) alpha_new[i] = sigma(order(i));
|
||||
for (int i = 0; i < p - 1; ++i) beta_new[i] = 0.0;
|
||||
beta_new[p - 1] = betaRestart;
|
||||
|
||||
// ---- Commit new state ----
|
||||
V = Vnew;
|
||||
U = Unew;
|
||||
alpha = alpha_new;
|
||||
beta = beta_new;
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "IRLBA restart: compressed to " << p << " steps,"
|
||||
<< " new beta_p = " << betaRestart << std::endl;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Extract the desired singular triplets into the public output vectors.
|
||||
// ------------------------------------------------------------------
|
||||
void extractTriplets(int m,
|
||||
const Eigen::VectorXd &sigma,
|
||||
const Eigen::MatrixXd &X,
|
||||
const Eigen::MatrixXd &Y,
|
||||
const Eigen::VectorXi &order,
|
||||
int nout)
|
||||
{
|
||||
singularValues.resize(nout);
|
||||
leftVectors.clear(); leftVectors.reserve(nout);
|
||||
rightVectors.clear(); rightVectors.reserve(nout);
|
||||
|
||||
for (int i = 0; i < nout; ++i) {
|
||||
int idx = order(i);
|
||||
singularValues[i] = sigma(idx);
|
||||
|
||||
// Left singular vector of A: svec_L = U_m * x_i
|
||||
Field svL(Grid); svL = Zero();
|
||||
for (int j = 0; j < m && j < (int)U.size(); ++j)
|
||||
svL = svL + X(j, idx) * U[j];
|
||||
leftVectors.push_back(svL);
|
||||
|
||||
// Right singular vector of A: svec_R = V_m * y_i
|
||||
Field svR(Grid); svR = Zero();
|
||||
for (int j = 0; j < m && j < (int)V.size(); ++j)
|
||||
svR = svR + Y(j, idx) * V[j];
|
||||
rightVectors.push_back(svR);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
#endif
|
||||
@@ -0,0 +1,931 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/ImplicitlyRestartedLanczos.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_LANC_H
|
||||
#define GRID_LANC_H
|
||||
|
||||
#include <string.h> //memset
|
||||
|
||||
#ifdef USE_LAPACK
|
||||
#ifdef USE_MKL
|
||||
#include<mkl_lapack.h>
|
||||
#else
|
||||
void LAPACK_dstegr (char *jobz, char *range, int *n, double *d, double *e,
|
||||
double *vl, double *vu, int *il, int *iu, double *abstol,
|
||||
int *m, double *w, double *z, int *ldz, int *isuppz,
|
||||
double *work, int *lwork, int *iwork, int *liwork,
|
||||
int *info);
|
||||
//#include <lapacke/lapacke.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//#include <Grid/algorithms/densematrix/DenseMatrix.h>
|
||||
|
||||
// eliminate temorary vector in calc()
|
||||
#define MEM_SAVE
|
||||
|
||||
namespace Grid
|
||||
{
|
||||
|
||||
struct Bisection
|
||||
{
|
||||
|
||||
#if 0
|
||||
static void get_eig2 (int row_num, std::vector < RealD > &ALPHA,
|
||||
std::vector < RealD > &BETA,
|
||||
std::vector < RealD > &eig)
|
||||
{
|
||||
int i, j;
|
||||
std::vector < RealD > evec1 (row_num + 3);
|
||||
std::vector < RealD > evec2 (row_num + 3);
|
||||
RealD eps2;
|
||||
ALPHA[1] = 0.;
|
||||
BETHA[1] = 0.;
|
||||
for (i = 0; i < row_num - 1; i++)
|
||||
{
|
||||
ALPHA[i + 1] = A[i * (row_num + 1)].real ();
|
||||
BETHA[i + 2] = A[i * (row_num + 1) + 1].real ();
|
||||
}
|
||||
ALPHA[row_num] = A[(row_num - 1) * (row_num + 1)].real ();
|
||||
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-10, 1e-10, evec1, eps2);
|
||||
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-16, 1e-16, evec2, eps2);
|
||||
|
||||
// Do we really need to sort here?
|
||||
int begin = 1;
|
||||
int end = row_num;
|
||||
int swapped = 1;
|
||||
while (swapped)
|
||||
{
|
||||
swapped = 0;
|
||||
for (i = begin; i < end; i++)
|
||||
{
|
||||
if (mag (evec2[i]) > mag (evec2[i + 1]))
|
||||
{
|
||||
swap (evec2 + i, evec2 + i + 1);
|
||||
swapped = 1;
|
||||
}
|
||||
}
|
||||
end--;
|
||||
for (i = end - 1; i >= begin; i--)
|
||||
{
|
||||
if (mag (evec2[i]) > mag (evec2[i + 1]))
|
||||
{
|
||||
swap (evec2 + i, evec2 + i + 1);
|
||||
swapped = 1;
|
||||
}
|
||||
}
|
||||
begin++;
|
||||
}
|
||||
|
||||
for (i = 0; i < row_num; i++)
|
||||
{
|
||||
for (j = 0; j < row_num; j++)
|
||||
{
|
||||
if (i == j)
|
||||
H[i * row_num + j] = evec2[i + 1];
|
||||
else
|
||||
H[i * row_num + j] = 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bisec (std::vector < RealD > &c,
|
||||
std::vector < RealD > &b,
|
||||
int n,
|
||||
int m1,
|
||||
int m2,
|
||||
RealD eps1,
|
||||
RealD relfeh, std::vector < RealD > &x, RealD & eps2)
|
||||
{
|
||||
std::vector < RealD > wu (n + 2);
|
||||
|
||||
RealD h, q, x1, xu, x0, xmin, xmax;
|
||||
int i, a, k;
|
||||
|
||||
b[1] = 0.0;
|
||||
xmin = c[n] - fabs (b[n]);
|
||||
xmax = c[n] + fabs (b[n]);
|
||||
for (i = 1; i < n; i++)
|
||||
{
|
||||
h = fabs (b[i]) + fabs (b[i + 1]);
|
||||
if (c[i] + h > xmax)
|
||||
xmax = c[i] + h;
|
||||
if (c[i] - h < xmin)
|
||||
xmin = c[i] - h;
|
||||
}
|
||||
xmax *= 2.;
|
||||
|
||||
eps2 = relfeh * ((xmin + xmax) > 0.0 ? xmax : -xmin);
|
||||
if (eps1 <= 0.0)
|
||||
eps1 = eps2;
|
||||
eps2 = 0.5 * eps1 + 7.0 * (eps2);
|
||||
x0 = xmax;
|
||||
for (i = m1; i <= m2; i++)
|
||||
{
|
||||
x[i] = xmax;
|
||||
wu[i] = xmin;
|
||||
}
|
||||
|
||||
for (k = m2; k >= m1; k--)
|
||||
{
|
||||
xu = xmin;
|
||||
i = k;
|
||||
do
|
||||
{
|
||||
if (xu < wu[i])
|
||||
{
|
||||
xu = wu[i];
|
||||
i = m1 - 1;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
while (i >= m1);
|
||||
if (x0 > x[k])
|
||||
x0 = x[k];
|
||||
while ((x0 - xu) > 2 * relfeh * (fabs (xu) + fabs (x0)) + eps1)
|
||||
{
|
||||
x1 = (xu + x0) / 2;
|
||||
|
||||
a = 0;
|
||||
q = 1.0;
|
||||
for (i = 1; i <= n; i++)
|
||||
{
|
||||
q =
|
||||
c[i] - x1 -
|
||||
((q != 0.0) ? b[i] * b[i] / q : fabs (b[i]) / relfeh);
|
||||
if (q < 0)
|
||||
a++;
|
||||
}
|
||||
// printf("x1=%0.14e a=%d\n",x1,a);
|
||||
if (a < k)
|
||||
{
|
||||
if (a < m1)
|
||||
{
|
||||
xu = x1;
|
||||
wu[m1] = x1;
|
||||
}
|
||||
else
|
||||
{
|
||||
xu = x1;
|
||||
wu[a + 1] = x1;
|
||||
if (x[a] > x1)
|
||||
x[a] = x1;
|
||||
}
|
||||
}
|
||||
else
|
||||
x0 = x1;
|
||||
}
|
||||
printf ("x0=%0.14e xu=%0.14e k=%d\n", x0, xu, k);
|
||||
x[k] = (x0 + xu) / 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Implicitly restarted lanczos
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template < class Field > class SimpleLanczos
|
||||
{
|
||||
|
||||
const RealD small = 1.0e-16;
|
||||
public:
|
||||
int lock;
|
||||
int get;
|
||||
int Niter;
|
||||
int converged;
|
||||
|
||||
int Nstop; // Number of evecs checked for convergence
|
||||
int Nk; // Number of converged sought
|
||||
int Np; // Np -- Number of spare vecs in kryloc space
|
||||
int Nm; // Nm -- total number of vectors
|
||||
|
||||
|
||||
RealD OrthoTime;
|
||||
|
||||
RealD eresid;
|
||||
|
||||
// SortEigen < Field > _sort;
|
||||
|
||||
LinearFunction < Field > &_Linop;
|
||||
|
||||
// OperatorFunction < Field > &_poly;
|
||||
|
||||
/////////////////////////
|
||||
// Constructor
|
||||
/////////////////////////
|
||||
void init (void)
|
||||
{
|
||||
};
|
||||
// void Abort (int ff, std::vector < RealD > &evals, DenseVector < Denstd::vector < RealD > >&evecs);
|
||||
|
||||
SimpleLanczos (LinearFunction < Field > &Linop, // op
|
||||
// OperatorFunction < Field > &poly, // polynmial
|
||||
int _Nstop, // sought vecs
|
||||
int _Nk, // sought vecs
|
||||
int _Nm, // spare vecs
|
||||
RealD _eresid, // resid in lmdue deficit
|
||||
int _Niter): // Max iterations
|
||||
|
||||
_Linop (Linop),
|
||||
// _poly (poly),
|
||||
Nstop (_Nstop), Nk (_Nk), Nm (_Nm), eresid (_eresid), Niter (_Niter)
|
||||
{
|
||||
Np = Nm - Nk;
|
||||
assert (Np > 0);
|
||||
};
|
||||
|
||||
/////////////////////////
|
||||
// Sanity checked this routine (step) against Saad.
|
||||
/////////////////////////
|
||||
void RitzMatrix (std::vector < Field > &evec, int k)
|
||||
{
|
||||
|
||||
if (1)
|
||||
return;
|
||||
|
||||
GridBase *grid = evec[0].Grid();
|
||||
Field w (grid);
|
||||
std::cout << GridLogMessage << "RitzMatrix " << std::endl;
|
||||
for (int i = 0; i < k; i++)
|
||||
{
|
||||
_Linop(evec[i], w);
|
||||
// _poly(_Linop,evec[i],w);
|
||||
std::cout << GridLogMessage << "[" << i << "] ";
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
ComplexD in = innerProduct (evec[j], w);
|
||||
if (fabs ((double) i - j) > 1)
|
||||
{
|
||||
if (abs (in) > 1.0e-9)
|
||||
{
|
||||
std::cout << GridLogMessage << "oops" << std::endl;
|
||||
abort ();
|
||||
}
|
||||
else
|
||||
std::cout << GridLogMessage << " 0 ";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << GridLogMessage << " " << in << " ";
|
||||
}
|
||||
}
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void step (std::vector < RealD > &lmd,
|
||||
std::vector < RealD > &lme,
|
||||
Field & last, Field & current, Field & next, uint64_t k)
|
||||
{
|
||||
if (lmd.size () <= k)
|
||||
lmd.resize (k + Nm);
|
||||
if (lme.size () <= k)
|
||||
lme.resize (k + Nm);
|
||||
|
||||
|
||||
// _poly(_Linop,current,next ); // 3. wk:=Avk−βkv_{k−1}
|
||||
_Linop(current, next); // 3. wk:=Avk−βkv_{k−1}
|
||||
if (k > 0)
|
||||
{
|
||||
next -= lme[k - 1] * last;
|
||||
}
|
||||
// std::cout<<GridLogMessage << "<last|next>" << innerProduct(last,next) <<std::endl;
|
||||
|
||||
ComplexD zalph = innerProduct (current, next); // 4. αk:=(wk,vk)
|
||||
RealD alph = real (zalph);
|
||||
|
||||
next = next - alph * current; // 5. wk:=wk−αkvk
|
||||
// std::cout<<GridLogMessage << "<current|next>" << innerProduct(current,next) <<std::endl;
|
||||
|
||||
RealD beta = normalise (next); // 6. βk+1 := ∥wk∥2. If βk+1 = 0 then Stop
|
||||
// 7. vk+1 := wk/βk+1
|
||||
// norm=beta;
|
||||
|
||||
int interval = Nm / 100 + 1;
|
||||
if ((k % interval) == 0)
|
||||
std::
|
||||
cout << GridLogMessage << k << " : alpha = " << zalph << " beta " <<
|
||||
beta << std::endl;
|
||||
const RealD tiny = 1.0e-20;
|
||||
if (beta < tiny)
|
||||
{
|
||||
std::cout << GridLogMessage << " beta is tiny " << beta << std::
|
||||
endl;
|
||||
}
|
||||
lmd[k] = alph;
|
||||
lme[k] = beta;
|
||||
|
||||
}
|
||||
|
||||
void qr_decomp (std::vector < RealD > &lmd,
|
||||
std::vector < RealD > &lme,
|
||||
int Nk,
|
||||
int Nm,
|
||||
std::vector < RealD > &Qt, RealD Dsh, int kmin, int kmax)
|
||||
{
|
||||
int k = kmin - 1;
|
||||
RealD x;
|
||||
|
||||
RealD Fden = 1.0 / hypot (lmd[k] - Dsh, lme[k]);
|
||||
RealD c = (lmd[k] - Dsh) * Fden;
|
||||
RealD s = -lme[k] * Fden;
|
||||
|
||||
RealD tmpa1 = lmd[k];
|
||||
RealD tmpa2 = lmd[k + 1];
|
||||
RealD tmpb = lme[k];
|
||||
|
||||
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
|
||||
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
|
||||
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
|
||||
x = -s * lme[k + 1];
|
||||
lme[k + 1] = c * lme[k + 1];
|
||||
|
||||
for (int i = 0; i < Nk; ++i)
|
||||
{
|
||||
RealD Qtmp1 = Qt[i + Nm * k];
|
||||
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
|
||||
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
|
||||
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
|
||||
}
|
||||
|
||||
// Givens transformations
|
||||
for (int k = kmin; k < kmax - 1; ++k)
|
||||
{
|
||||
|
||||
RealD Fden = 1.0 / hypot (x, lme[k - 1]);
|
||||
RealD c = lme[k - 1] * Fden;
|
||||
RealD s = -x * Fden;
|
||||
|
||||
RealD tmpa1 = lmd[k];
|
||||
RealD tmpa2 = lmd[k + 1];
|
||||
RealD tmpb = lme[k];
|
||||
|
||||
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
|
||||
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
|
||||
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
|
||||
lme[k - 1] = c * lme[k - 1] - s * x;
|
||||
|
||||
if (k != kmax - 2)
|
||||
{
|
||||
x = -s * lme[k + 1];
|
||||
lme[k + 1] = c * lme[k + 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < Nk; ++i)
|
||||
{
|
||||
RealD Qtmp1 = Qt[i + Nm * k];
|
||||
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
|
||||
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
|
||||
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifdef USE_LAPACK
|
||||
#ifdef USE_MKL
|
||||
#define LAPACK_INT MKL_INT
|
||||
#else
|
||||
#define LAPACK_INT long long
|
||||
#endif
|
||||
void diagonalize_lapack (std::vector < RealD > &lmd, std::vector < RealD > &lme, int N1, // all
|
||||
int N2, // get
|
||||
GridBase * grid)
|
||||
{
|
||||
const int size = Nm;
|
||||
LAPACK_INT NN = N1;
|
||||
double evals_tmp[NN];
|
||||
double DD[NN];
|
||||
double EE[NN];
|
||||
for (int i = 0; i < NN; i++)
|
||||
for (int j = i - 1; j <= i + 1; j++)
|
||||
if (j < NN && j >= 0)
|
||||
{
|
||||
if (i == j)
|
||||
DD[i] = lmd[i];
|
||||
if (i == j)
|
||||
evals_tmp[i] = lmd[i];
|
||||
if (j == (i - 1))
|
||||
EE[j] = lme[j];
|
||||
}
|
||||
LAPACK_INT evals_found;
|
||||
LAPACK_INT lwork =
|
||||
((18 * NN) >
|
||||
(1 + 4 * NN + NN * NN) ? (18 * NN) : (1 + 4 * NN + NN * NN));
|
||||
LAPACK_INT liwork = 3 + NN * 10;
|
||||
LAPACK_INT iwork[liwork];
|
||||
double work[lwork];
|
||||
LAPACK_INT isuppz[2 * NN];
|
||||
char jobz = 'N'; // calculate evals only
|
||||
char range = 'I'; // calculate il-th to iu-th evals
|
||||
// char range = 'A'; // calculate all evals
|
||||
char uplo = 'U'; // refer to upper half of original matrix
|
||||
char compz = 'I'; // Compute eigenvectors of tridiagonal matrix
|
||||
int ifail[NN];
|
||||
LAPACK_INT info;
|
||||
// int total = QMP_get_number_of_nodes();
|
||||
// int node = QMP_get_node_number();
|
||||
// GridBase *grid = evec[0]._grid;
|
||||
int total = grid->_Nprocessors;
|
||||
int node = grid->_processor;
|
||||
int interval = (NN / total) + 1;
|
||||
double vl = 0.0, vu = 0.0;
|
||||
LAPACK_INT il = interval * node + 1, iu = interval * (node + 1);
|
||||
if (iu > NN)
|
||||
iu = NN;
|
||||
double tol = 0.0;
|
||||
if (1)
|
||||
{
|
||||
memset (evals_tmp, 0, sizeof (double) * NN);
|
||||
if (il <= NN)
|
||||
{
|
||||
printf ("total=%d node=%d il=%d iu=%d\n", total, node, il, iu);
|
||||
#ifdef USE_MKL
|
||||
dstegr (&jobz, &range, &NN,
|
||||
#else
|
||||
LAPACK_dstegr (&jobz, &range, &NN,
|
||||
#endif
|
||||
(double *) DD, (double *) EE, &vl, &vu, &il, &iu, // these four are ignored if second parameteris 'A'
|
||||
&tol, // tolerance
|
||||
&evals_found, evals_tmp, (double *) NULL, &NN,
|
||||
isuppz, work, &lwork, iwork, &liwork, &info);
|
||||
for (int i = iu - 1; i >= il - 1; i--)
|
||||
{
|
||||
printf ("node=%d evals_found=%d evals_tmp[%d] = %g\n", node,
|
||||
evals_found, i - (il - 1), evals_tmp[i - (il - 1)]);
|
||||
evals_tmp[i] = evals_tmp[i - (il - 1)];
|
||||
if (il > 1)
|
||||
evals_tmp[i - (il - 1)] = 0.;
|
||||
}
|
||||
}
|
||||
{
|
||||
grid->GlobalSumVector (evals_tmp, NN);
|
||||
}
|
||||
}
|
||||
// cheating a bit. It is better to sort instead of just reversing it, but the document of the routine says evals are sorted in increasing order. qr gives evals in decreasing order.
|
||||
}
|
||||
#undef LAPACK_INT
|
||||
#endif
|
||||
|
||||
|
||||
void diagonalize (std::vector < RealD > &lmd,
|
||||
std::vector < RealD > &lme,
|
||||
int N2, int N1, GridBase * grid)
|
||||
{
|
||||
|
||||
#ifdef USE_LAPACK
|
||||
const int check_lapack = 0; // just use lapack if 0, check against lapack if 1
|
||||
|
||||
if (!check_lapack)
|
||||
return diagonalize_lapack (lmd, lme, N2, N1, grid);
|
||||
|
||||
// diagonalize_lapack(lmd2,lme2,Nm2,Nm,Qt,grid);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static RealD normalise (Field & v)
|
||||
{
|
||||
RealD nn = norm2 (v);
|
||||
nn = sqrt (nn);
|
||||
v = v * (1.0 / nn);
|
||||
return nn;
|
||||
}
|
||||
|
||||
void orthogonalize (Field & w, std::vector < Field > &evec, int k)
|
||||
{
|
||||
double t0 = -usecond () / 1e6;
|
||||
typedef typename Field::scalar_type MyComplex;
|
||||
MyComplex ip;
|
||||
|
||||
if (0)
|
||||
{
|
||||
for (int j = 0; j < k; ++j)
|
||||
{
|
||||
normalise (evec[j]);
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
ip = innerProduct (evec[i], evec[j]); // are the evecs normalised? ; this assumes so.
|
||||
evec[j] = evec[j] - ip * evec[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < k; ++j)
|
||||
{
|
||||
ip = innerProduct (evec[j], w); // are the evecs normalised? ; this assumes so.
|
||||
w = w - ip * evec[j];
|
||||
}
|
||||
normalise (w);
|
||||
t0 += usecond () / 1e6;
|
||||
OrthoTime += t0;
|
||||
}
|
||||
|
||||
void setUnit_Qt (int Nm, std::vector < RealD > &Qt)
|
||||
{
|
||||
for (int i = 0; i < Qt.size (); ++i)
|
||||
Qt[i] = 0.0;
|
||||
for (int k = 0; k < Nm; ++k)
|
||||
Qt[k + k * Nm] = 1.0;
|
||||
}
|
||||
|
||||
|
||||
void calc (std::vector < RealD > &eval, const Field & src, int &Nconv)
|
||||
{
|
||||
|
||||
GridBase *grid = src.Grid();
|
||||
// assert(grid == src._grid);
|
||||
|
||||
std::
|
||||
cout << GridLogMessage << " -- Nk = " << Nk << " Np = " << Np << std::
|
||||
endl;
|
||||
std::cout << GridLogMessage << " -- Nm = " << Nm << std::endl;
|
||||
std::cout << GridLogMessage << " -- size of eval = " << eval.
|
||||
size () << std::endl;
|
||||
|
||||
// assert(c.size() && Nm == eval.size());
|
||||
|
||||
std::vector < RealD > lme (Nm);
|
||||
std::vector < RealD > lmd (Nm);
|
||||
|
||||
|
||||
Field current (grid);
|
||||
Field last (grid);
|
||||
Field next (grid);
|
||||
|
||||
Nconv = 0;
|
||||
|
||||
RealD beta_k;
|
||||
|
||||
// Set initial vector
|
||||
// (uniform vector) Why not src??
|
||||
// evec[0] = 1.0;
|
||||
current = src;
|
||||
std::cout << GridLogMessage << "norm2(src)= " << norm2 (src) << std::
|
||||
endl;
|
||||
normalise (current);
|
||||
std::
|
||||
cout << GridLogMessage << "norm2(evec[0])= " << norm2 (current) <<
|
||||
std::endl;
|
||||
|
||||
// Initial Nk steps
|
||||
OrthoTime = 0.;
|
||||
double t0 = usecond () / 1e6;
|
||||
RealD norm; // sqrt norm of last vector
|
||||
|
||||
uint64_t iter = 0;
|
||||
|
||||
bool initted = false;
|
||||
std::vector < RealD > low (Nstop * 10);
|
||||
std::vector < RealD > high (Nstop * 10);
|
||||
RealD cont = 0.;
|
||||
while (1) {
|
||||
cont = 0.;
|
||||
std::vector < RealD > lme2 (Nm);
|
||||
std::vector < RealD > lmd2 (Nm);
|
||||
for (uint64_t k = 0; k < Nm; ++k, iter++) {
|
||||
step (lmd, lme, last, current, next, iter);
|
||||
last = current;
|
||||
current = next;
|
||||
}
|
||||
double t1 = usecond () / 1e6;
|
||||
std::cout << GridLogMessage << "IRL::Initial steps: " << t1 -
|
||||
t0 << "seconds" << std::endl;
|
||||
t0 = t1;
|
||||
std::
|
||||
cout << GridLogMessage << "IRL::Initial steps:OrthoTime " <<
|
||||
OrthoTime << "seconds" << std::endl;
|
||||
|
||||
// getting eigenvalues
|
||||
lmd2.resize (iter + 2);
|
||||
lme2.resize (iter + 2);
|
||||
for (uint64_t k = 0; k < iter; ++k) {
|
||||
lmd2[k + 1] = lmd[k];
|
||||
lme2[k + 2] = lme[k];
|
||||
}
|
||||
t1 = usecond () / 1e6;
|
||||
std::cout << GridLogMessage << "IRL:: copy: " << t1 -
|
||||
t0 << "seconds" << std::endl;
|
||||
t0 = t1;
|
||||
{
|
||||
int total = grid->_Nprocessors;
|
||||
int node = grid->_processor;
|
||||
int interval = (Nstop / total) + 1;
|
||||
int iu = (iter + 1) - (interval * node + 1);
|
||||
int il = (iter + 1) - (interval * (node + 1));
|
||||
std::vector < RealD > eval2 (iter + 3);
|
||||
RealD eps2;
|
||||
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
|
||||
eps2);
|
||||
// diagonalize(eval2,lme2,iter,Nk,grid);
|
||||
RealD diff = 0.;
|
||||
for (int i = il; i <= iu; i++) {
|
||||
if (initted)
|
||||
diff =
|
||||
fabs (eval2[i] - high[iu-i]) / (fabs (eval2[i]) +
|
||||
fabs (high[iu-i]));
|
||||
if (initted && (diff > eresid))
|
||||
cont = 1.;
|
||||
if (initted)
|
||||
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
|
||||
high[iu-i], diff);
|
||||
high[iu-i] = eval2[i];
|
||||
}
|
||||
il = (interval * node + 1);
|
||||
iu = (interval * (node + 1));
|
||||
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
|
||||
eps2);
|
||||
for (int i = il; i <= iu; i++) {
|
||||
if (initted)
|
||||
diff =
|
||||
fabs (eval2[i] - low[i]) / (fabs (eval2[i]) +
|
||||
fabs (low[i]));
|
||||
if (initted && (diff > eresid))
|
||||
cont = 1.;
|
||||
if (initted)
|
||||
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
|
||||
low[i], diff);
|
||||
low[i] = eval2[i];
|
||||
}
|
||||
t1 = usecond () / 1e6;
|
||||
std::cout << GridLogMessage << "IRL:: diagonalize: " << t1 -
|
||||
t0 << "seconds" << std::endl;
|
||||
t0 = t1;
|
||||
}
|
||||
|
||||
for (uint64_t k = 0; k < Nk; ++k) {
|
||||
// eval[k] = eval2[k];
|
||||
}
|
||||
if (initted)
|
||||
{
|
||||
grid->GlobalSumVector (&cont, 1);
|
||||
if (cont < 1.) return;
|
||||
}
|
||||
initted = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
There is some matrix Q such that for any vector y
|
||||
Q.e_1 = y and Q is unitary.
|
||||
**/
|
||||
template < class T >
|
||||
static T orthQ (DenseMatrix < T > &Q, std::vector < T > y)
|
||||
{
|
||||
int N = y.size (); //Matrix Size
|
||||
Fill (Q, 0.0);
|
||||
T tau;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
Q[i][0] = y[i];
|
||||
}
|
||||
T sig = conj (y[0]) * y[0];
|
||||
T tau0 = fabs (sqrt (sig));
|
||||
|
||||
for (int j = 1; j < N; j++)
|
||||
{
|
||||
sig += conj (y[j]) * y[j];
|
||||
tau = abs (sqrt (sig));
|
||||
|
||||
if (abs (tau0) > 0.0)
|
||||
{
|
||||
|
||||
T gam = conj ((y[j] / tau) / tau0);
|
||||
for (int k = 0; k <= j - 1; k++)
|
||||
{
|
||||
Q[k][j] = -gam * y[k];
|
||||
}
|
||||
Q[j][j] = tau0 / tau;
|
||||
}
|
||||
else
|
||||
{
|
||||
Q[j - 1][j] = 1.0;
|
||||
}
|
||||
tau0 = tau;
|
||||
}
|
||||
return tau;
|
||||
}
|
||||
|
||||
/**
|
||||
There is some matrix Q such that for any vector y
|
||||
Q.e_k = y and Q is unitary.
|
||||
**/
|
||||
template < class T >
|
||||
static T orthU (DenseMatrix < T > &Q, std::vector < T > y)
|
||||
{
|
||||
T tau = orthQ (Q, y);
|
||||
SL (Q);
|
||||
return tau;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Wind up with a matrix with the first con rows untouched
|
||||
|
||||
say con = 2
|
||||
Q is such that Qdag H Q has {x, x, val, 0, 0, 0, 0, ...} as 1st colum
|
||||
and the matrix is upper hessenberg
|
||||
and with f and Q appropriately modidied with Q is the arnoldi factorization
|
||||
|
||||
**/
|
||||
|
||||
template < class T > static void Lock (DenseMatrix < T > &H, ///Hess mtx
|
||||
DenseMatrix < T > &Q, ///Lock Transform
|
||||
T val, ///value to be locked
|
||||
int con, ///number already locked
|
||||
RealD small, int dfg, bool herm)
|
||||
{
|
||||
//ForceTridiagonal(H);
|
||||
|
||||
int M = H.dim;
|
||||
DenseVector < T > vec;
|
||||
Resize (vec, M - con);
|
||||
|
||||
DenseMatrix < T > AH;
|
||||
Resize (AH, M - con, M - con);
|
||||
AH = GetSubMtx (H, con, M, con, M);
|
||||
|
||||
DenseMatrix < T > QQ;
|
||||
Resize (QQ, M - con, M - con);
|
||||
|
||||
Unity (Q);
|
||||
Unity (QQ);
|
||||
|
||||
DenseVector < T > evals;
|
||||
Resize (evals, M - con);
|
||||
DenseMatrix < T > evecs;
|
||||
Resize (evecs, M - con, M - con);
|
||||
|
||||
Wilkinson < T > (AH, evals, evecs, small);
|
||||
|
||||
int k = 0;
|
||||
RealD cold = abs (val - evals[k]);
|
||||
for (int i = 1; i < M - con; i++)
|
||||
{
|
||||
RealD cnew = abs (val - evals[i]);
|
||||
if (cnew < cold)
|
||||
{
|
||||
k = i;
|
||||
cold = cnew;
|
||||
}
|
||||
}
|
||||
vec = evecs[k];
|
||||
|
||||
ComplexD tau;
|
||||
orthQ (QQ, vec);
|
||||
//orthQM(QQ,AH,vec);
|
||||
|
||||
AH = Hermitian (QQ) * AH;
|
||||
AH = AH * QQ;
|
||||
|
||||
for (int i = con; i < M; i++)
|
||||
{
|
||||
for (int j = con; j < M; j++)
|
||||
{
|
||||
Q[i][j] = QQ[i - con][j - con];
|
||||
H[i][j] = AH[i - con][j - con];
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = M - 1; j > con + 2; j--)
|
||||
{
|
||||
|
||||
DenseMatrix < T > U;
|
||||
Resize (U, j - 1 - con, j - 1 - con);
|
||||
DenseVector < T > z;
|
||||
Resize (z, j - 1 - con);
|
||||
T nm = norm (z);
|
||||
for (int k = con + 0; k < j - 1; k++)
|
||||
{
|
||||
z[k - con] = conj (H (j, k + 1));
|
||||
}
|
||||
normalise (z);
|
||||
|
||||
RealD tmp = 0;
|
||||
for (int i = 0; i < z.size () - 1; i++)
|
||||
{
|
||||
tmp = tmp + abs (z[i]);
|
||||
}
|
||||
|
||||
if (tmp < small / ((RealD) z.size () - 1.0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tau = orthU (U, z);
|
||||
|
||||
DenseMatrix < T > Hb;
|
||||
Resize (Hb, j - 1 - con, M);
|
||||
|
||||
for (int a = 0; a < M; a++)
|
||||
{
|
||||
for (int b = 0; b < j - 1 - con; b++)
|
||||
{
|
||||
T sum = 0;
|
||||
for (int c = 0; c < j - 1 - con; c++)
|
||||
{
|
||||
sum += H[a][con + 1 + c] * U[c][b];
|
||||
} //sum += H(a,con+1+c)*U(c,b);}
|
||||
Hb[b][a] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = con + 1; k < j; k++)
|
||||
{
|
||||
for (int l = 0; l < M; l++)
|
||||
{
|
||||
H[l][k] = Hb[k - 1 - con][l];
|
||||
}
|
||||
} //H(Hb[k-1-con][l] , l,k);}}
|
||||
|
||||
DenseMatrix < T > Qb;
|
||||
Resize (Qb, M, M);
|
||||
|
||||
for (int a = 0; a < M; a++)
|
||||
{
|
||||
for (int b = 0; b < j - 1 - con; b++)
|
||||
{
|
||||
T sum = 0;
|
||||
for (int c = 0; c < j - 1 - con; c++)
|
||||
{
|
||||
sum += Q[a][con + 1 + c] * U[c][b];
|
||||
} //sum += Q(a,con+1+c)*U(c,b);}
|
||||
Qb[b][a] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = con + 1; k < j; k++)
|
||||
{
|
||||
for (int l = 0; l < M; l++)
|
||||
{
|
||||
Q[l][k] = Qb[k - 1 - con][l];
|
||||
}
|
||||
} //Q(Qb[k-1-con][l] , l,k);}}
|
||||
|
||||
DenseMatrix < T > Hc;
|
||||
Resize (Hc, M, M);
|
||||
|
||||
for (int a = 0; a < j - 1 - con; a++)
|
||||
{
|
||||
for (int b = 0; b < M; b++)
|
||||
{
|
||||
T sum = 0;
|
||||
for (int c = 0; c < j - 1 - con; c++)
|
||||
{
|
||||
sum += conj (U[c][a]) * H[con + 1 + c][b];
|
||||
} //sum += conj( U(c,a) )*H(con+1+c,b);}
|
||||
Hc[b][a] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < M; k++)
|
||||
{
|
||||
for (int l = con + 1; l < j; l++)
|
||||
{
|
||||
H[l][k] = Hc[k][l - 1 - con];
|
||||
}
|
||||
} //H(Hc[k][l-1-con] , l,k);}}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./lib/algorithms/iterative/SplitGridBlockKrylovSchur.h
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
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_SPLIT_BLOCK_KRYLOV_SCHUR_H
|
||||
#define GRID_SPLIT_BLOCK_KRYLOV_SCHUR_H
|
||||
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
/**
|
||||
* Block Krylov-Schur eigensolver with Split-Grid batched operator application.
|
||||
*
|
||||
* Derives from BlockKrylovSchur<Field> and overrides only the operator
|
||||
* application: instead of calling Linop.Op() once per vector, mrhs vectors
|
||||
* are packed onto a smaller split grid with Grid_split, the polynomial-
|
||||
* filtered operator poly(SLinop) is applied once to the combined field, and
|
||||
* the results are unpacked with Grid_unsplit. All other algorithmic logic
|
||||
* (Arnoldi orthogonalisation, Schur restart, convergence test, etc.) is
|
||||
* inherited unchanged from the base class.
|
||||
*
|
||||
* Constructor extras (beyond BlockKrylovSchur)
|
||||
* -------------------------------------------
|
||||
* SLinop : split-grid linear operator used inside poly
|
||||
* poly : polynomial filter OperatorFunction applied on the split grid
|
||||
* SGrid : split-grid GridBase (mrhs fields packed here)
|
||||
* mrhs : RHS batched per split-grid call; Nblock must be divisible by mrhs
|
||||
*
|
||||
* Notes
|
||||
* -----
|
||||
* - The Rayleigh quotient H is built from poly(A), so its Ritz values are
|
||||
* eigenvalues of poly(A). Convergence is assessed against the full-grid
|
||||
* Linop (inherited from base), giving true A-eigenvalues.
|
||||
* - Grid_ (inherited) is the full grid; SGrid is the split grid.
|
||||
*/
|
||||
template<class Field>
|
||||
class SplitGridBlockKrylovSchur : public BlockKrylovSchur<Field> {
|
||||
|
||||
using Base = BlockKrylovSchur<Field>;
|
||||
|
||||
// Bring protected base members into scope
|
||||
using Base::Nblock;
|
||||
using Base::Grid_;
|
||||
using Base::basis;
|
||||
|
||||
// Split-grid extras
|
||||
LinearOperatorBase<Field>& SLinop;
|
||||
OperatorFunction<Field>& poly;
|
||||
GridBase* SGrid;
|
||||
int mrhs;
|
||||
|
||||
public:
|
||||
|
||||
SplitGridBlockKrylovSchur(LinearOperatorBase<Field>& _Linop,
|
||||
LinearOperatorBase<Field>& _SLinop,
|
||||
OperatorFunction<Field>& _poly,
|
||||
GridBase* _FGrid,
|
||||
GridBase* _SGrid,
|
||||
int _mrhs,
|
||||
RealD _Tolerance,
|
||||
RitzFilter _rf = EvalReSmall)
|
||||
: Base(_Linop, _FGrid, _Tolerance, _rf),
|
||||
SLinop(_SLinop), poly(_poly), SGrid(_SGrid), mrhs(_mrhs)
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
// Validate mrhs divisibility before the Arnoldi loop starts
|
||||
void preRun() override
|
||||
{
|
||||
assert(Nblock % mrhs == 0 && "Nblock must be divisible by mrhs");
|
||||
}
|
||||
|
||||
// Apply poly(A) to basis[kBase .. kBase+Nblock-1] via Grid_split / Grid_unsplit,
|
||||
// batching mrhs vectors per split-grid call.
|
||||
void applyBlock(std::vector<Field>& W, int kBase) override
|
||||
{
|
||||
std::vector<Field> in(mrhs, Field(Grid_));
|
||||
Field s_in(SGrid);
|
||||
Field s_out(SGrid);
|
||||
|
||||
int k_start = 0;
|
||||
while (k_start < Nblock) {
|
||||
for (int u = 0; u < mrhs; u++)
|
||||
in[u] = basis[kBase + k_start + u];
|
||||
|
||||
Grid_split(in, s_in);
|
||||
poly(SLinop, s_in, s_out);
|
||||
Grid_unsplit(in, s_out);
|
||||
|
||||
for (int u = 0; u < mrhs; u++)
|
||||
W[k_start + u] = in[u];
|
||||
|
||||
k_start += mrhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(Grid);
|
||||
|
||||
#endif // GRID_SPLIT_BLOCK_KRYLOV_SCHUR_H
|
||||
@@ -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
|
||||
@@ -260,7 +260,8 @@ class GridLimeReader : public BinaryIO {
|
||||
<< " / field= " << n2ck << " / rdiff= " << GRID_FIELD_NORM_CALC(FieldNormMetaData_,n2ck) << std::endl;
|
||||
GRID_FIELD_NORM_CHECK(FieldNormMetaData_,n2ck);
|
||||
}
|
||||
assert(scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb)==1);
|
||||
// assert(scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb)==1);
|
||||
scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb);
|
||||
|
||||
// find out if next field is a GridFieldNorm
|
||||
return;
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
field.checksum = std::stoul(header["CHECKSUM"],0,16);
|
||||
field.ensemble_id = header["ENSEMBLE_ID"];
|
||||
field.ensemble_label = header["ENSEMBLE_LABEL"];
|
||||
field.sequence_number = std::stol(header["SEQUENCE_NUMBER"]);
|
||||
// field.sequence_number = std::stol(header["SEQUENCE_NUMBER"]);
|
||||
field.creator = header["CREATOR"];
|
||||
field.creator_hardware = header["CREATOR_HARDWARE"];
|
||||
field.creation_date = header["CREATION_DATE"];
|
||||
|
||||
@@ -28,6 +28,11 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
|
||||
/* END LEGAL */
|
||||
#pragma once
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x,y) ((x)>(y)?(y):(x))
|
||||
#endif
|
||||
|
||||
|
||||
// Introduce a class to gain deterministic bit reproducible reduction.
|
||||
// make static; perhaps just a namespace is required.
|
||||
NAMESPACE_BEGIN(Grid);
|
||||
|
||||
@@ -877,7 +877,7 @@ int main (int argc, char ** argv)
|
||||
int do_su4=0;
|
||||
int do_memory=1;
|
||||
int do_comms =1;
|
||||
int do_blas =1;
|
||||
int do_blas =0;
|
||||
int do_dslash=1;
|
||||
|
||||
int sel=4;
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ esac
|
||||
############### fermions
|
||||
AC_ARG_ENABLE([fermion-instantiations],
|
||||
[AS_HELP_STRING([--enable-fermion-instantiations=yes|no],[enable fermion instantiations])],
|
||||
[ac_FERMION_REPS=${enable_fermion_instantiations}], [ac_FERMION_INSTANTIATIONS=yes])
|
||||
[ac_FERMION_INSTANTIATIONS=${enable_fermion_instantiations}], [ac_FERMION_INSTANTIATIONS=yes])
|
||||
|
||||
AM_CONDITIONAL(BUILD_FERMION_INSTANTIATIONS, [ test "${ac_FERMION_INSTANTIATIONS}X" == "yesX" ])
|
||||
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./tests/Test_padded_cell.cc
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
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 */
|
||||
|
||||
// copied here from Test_general_coarse_pvdagm.cc
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
// Hermitize a DWF operator by squaring it
|
||||
template<class Matrix,class Field>
|
||||
class SquaredLinearOperator : public LinearOperatorBase<Field> {
|
||||
|
||||
public:
|
||||
Matrix &_Mat;
|
||||
|
||||
public:
|
||||
SquaredLinearOperator(Matrix &Mat): _Mat(Mat) {};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
// std::cout << "Op is overloaded as HermOp" << std::endl;
|
||||
HermOp(in, out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
HermOp(in, out);
|
||||
}
|
||||
void _Op (const Field &in, Field &out){
|
||||
// std::cout << "Op: M "<<std::endl;
|
||||
_Mat.M(in, out);
|
||||
}
|
||||
void _AdjOp (const Field &in, Field &out){
|
||||
// std::cout << "AdjOp: Mdag "<<std::endl;
|
||||
_Mat.Mdag(in, out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
// std::cout << "HermOp: Mdag M Mdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Op(in,tmp);
|
||||
_AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class PVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
public:
|
||||
PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(in,tmp);
|
||||
_Mat.Mdag(tmp,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
// _Mat.M(in,tmp);
|
||||
// _PV.Mdag(tmp,out);
|
||||
// _PV.M(out,tmp);
|
||||
// _Mat.Mdag(tmp,out);
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
// std::cout << "HermOp done "<<norm2(out)<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class ShiftedPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
RealD shift;
|
||||
public:
|
||||
ShiftedPVdagMLinearOperator(RealD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix, class Field>
|
||||
class ShiftedComplexPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
ComplexD shift;
|
||||
public:
|
||||
ShiftedComplexPVdagMLinearOperator(ComplexD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
|
||||
void resetShift(ComplexD newShift) {
|
||||
shift = newShift;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class MGPreconditioner : public LinearFunction< Lattice<Fobj> > {
|
||||
public:
|
||||
using LinearFunction<Lattice<Fobj> >::operator();
|
||||
|
||||
typedef Aggregation<Fobj,CComplex,nbasis> Aggregates;
|
||||
typedef typename Aggregation<Fobj,CComplex,nbasis>::FineField FineField;
|
||||
typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseVector CoarseVector;
|
||||
typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseMatrix CoarseMatrix;
|
||||
typedef LinearOperatorBase<FineField> FineOperator;
|
||||
typedef LinearFunction <FineField> FineSmoother;
|
||||
typedef LinearOperatorBase<CoarseVector> CoarseOperator;
|
||||
typedef LinearFunction <CoarseVector> CoarseSolver;
|
||||
Aggregates & _Aggregates;
|
||||
FineOperator & _FineOperator;
|
||||
FineSmoother & _PreSmoother;
|
||||
FineSmoother & _PostSmoother;
|
||||
CoarseOperator & _CoarseOperator;
|
||||
CoarseSolver & _CoarseSolve;
|
||||
|
||||
int level; void Level(int lv) {level = lv; };
|
||||
|
||||
MGPreconditioner(Aggregates &Agg,
|
||||
FineOperator &Fine,
|
||||
FineSmoother &PreSmoother,
|
||||
FineSmoother &PostSmoother,
|
||||
CoarseOperator &CoarseOperator_,
|
||||
CoarseSolver &CoarseSolve_)
|
||||
: _Aggregates(Agg),
|
||||
_FineOperator(Fine),
|
||||
_PreSmoother(PreSmoother),
|
||||
_PostSmoother(PostSmoother),
|
||||
_CoarseOperator(CoarseOperator_),
|
||||
_CoarseSolve(CoarseSolve_),
|
||||
level(1) { }
|
||||
|
||||
virtual void operator()(const FineField &in, FineField & out)
|
||||
{
|
||||
GridBase *CoarseGrid = _Aggregates.CoarseGrid;
|
||||
// auto CoarseGrid = _CoarseOperator.Grid();
|
||||
CoarseVector Csrc(CoarseGrid);
|
||||
CoarseVector Csol(CoarseGrid);
|
||||
FineField vec1(in.Grid());
|
||||
FineField vec2(in.Grid());
|
||||
|
||||
std::cout<<GridLogMessage << "Calling PreSmoother " <<std::endl;
|
||||
|
||||
// std::cout<<GridLogMessage << "Calling PreSmoother input residual "<<norm2(in) <<std::endl;
|
||||
double t;
|
||||
// Fine Smoother
|
||||
// out = in;
|
||||
out = Zero();
|
||||
t=-usecond();
|
||||
_PreSmoother(in,out);
|
||||
t+=usecond();
|
||||
|
||||
std::cout<<GridLogMessage << "PreSmoother took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Update the residual
|
||||
_FineOperator.Op(out,vec1); sub(vec1, in ,vec1);
|
||||
// std::cout<<GridLogMessage <<"Residual-1 now " <<norm2(vec1)<<std::endl;
|
||||
|
||||
// Fine to Coarse
|
||||
t=-usecond();
|
||||
_Aggregates.ProjectToSubspace (Csrc,vec1);
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "Project to coarse took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Coarse correction
|
||||
t=-usecond();
|
||||
Csol = Zero();
|
||||
_CoarseSolve(Csrc,Csol);
|
||||
//Csol=Zero();
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "Coarse solve took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Coarse to Fine
|
||||
t=-usecond();
|
||||
// _CoarseOperator.PromoteFromSubspace(_Aggregates,Csol,vec1);
|
||||
_Aggregates.PromoteFromSubspace(Csol,vec1);
|
||||
add(out,out,vec1);
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "Promote to this level took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Residual
|
||||
_FineOperator.Op(out,vec1); sub(vec1 ,in , vec1);
|
||||
// std::cout<<GridLogMessage <<"Residual-2 now " <<norm2(vec1)<<std::endl;
|
||||
|
||||
// Fine Smoother
|
||||
t=-usecond();
|
||||
// vec2=vec1;
|
||||
vec2=Zero();
|
||||
_PostSmoother(vec1,vec2);
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "PostSmoother took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
add( out,out,vec2);
|
||||
std::cout<<GridLogMessage << "Done " <<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
const int Ls=16;
|
||||
|
||||
// GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
|
||||
std::vector<int> lat_size {16, 16, 16, 32};
|
||||
std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size,
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
|
||||
// Construct a coarsened grid
|
||||
// poare TODO: replace this with the following line?
|
||||
Coordinate clatt = lat_size;
|
||||
// Coordinate clatt = GridDefaultLatt(); // [PO] initial line before I edited it
|
||||
for(int d=0;d<clatt.size();d++){
|
||||
clatt[d] = clatt[d]/2;
|
||||
// clatt[d] = clatt[d]/4;
|
||||
}
|
||||
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());;
|
||||
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
|
||||
|
||||
std::vector<int> seeds4({1,2,3,4});
|
||||
std::vector<int> seeds5({5,6,7,8});
|
||||
std::vector<int> cseeds({5,6,7,8});
|
||||
GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5);
|
||||
GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4);
|
||||
GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds);
|
||||
|
||||
LatticeFermion src(FGrid); random(RNG5,src);
|
||||
LatticeFermion result(FGrid); result=Zero();
|
||||
LatticeFermion ref(FGrid); ref=Zero();
|
||||
LatticeFermion tmp(FGrid);
|
||||
LatticeFermion err(FGrid);
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
|
||||
FieldMetaData header;
|
||||
// std::string file("ckpoint_lat.4000");
|
||||
std::string file("/Users/patrickoare/libraries/PETSc-Grid/ckpoint_lat.4000");
|
||||
NerscIO::readConfiguration(Umu,header,file);
|
||||
|
||||
RealD mass=0.01;
|
||||
RealD M5=1.8;
|
||||
|
||||
DomainWallFermionD Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5);
|
||||
DomainWallFermionD Dpv(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,1.0,M5);
|
||||
|
||||
// const int nbasis = 20; // size of approximate basis for low-mode space
|
||||
const int nbasis = 3; // size of approximate basis for low-mode space
|
||||
const int cb = 0 ;
|
||||
LatticeFermion prom(FGrid);
|
||||
|
||||
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
|
||||
typedef LittleDiracOperator::CoarseVector CoarseVector;
|
||||
|
||||
NextToNearestStencilGeometry5D geom(Coarse5d);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
|
||||
typedef PVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> PVdagM_t;
|
||||
typedef ShiftedPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedPVdagM_t;
|
||||
typedef ShiftedComplexPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedComplexPVdagM_t;
|
||||
PVdagM_t PVdagM(Ddwf, Dpv);
|
||||
ShiftedPVdagM_t ShiftedPVdagM(0.1,Ddwf,Dpv);
|
||||
SquaredLinearOperator<DomainWallFermionD, LatticeFermionD> Dsq (Ddwf);
|
||||
NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
// PowerMethod<LatticeFermion> PM; PM(PVdagM, src);
|
||||
int Nm = 10;
|
||||
int Nk = 6;
|
||||
// int Nm = 6; // Nm = 6 case is acting really strangely... with Nm = 6 and Nm = 3 it zeros out the Hessenberg and also makes it imaginary?
|
||||
// int Nk = 2;
|
||||
// int Nk = Nm+1; // if just running once
|
||||
// int maxIter = 5;
|
||||
// int maxIter = 1;
|
||||
int maxIter = 3;
|
||||
// int maxIter = 100;
|
||||
int Nstop = 6;
|
||||
|
||||
Coordinate origin ({0,0,0,0});
|
||||
auto tmpSrc = peekSite(src, origin);
|
||||
std::cout << "[DEBUG] Source at origin = " << tmpSrc << std::endl;
|
||||
LatticeFermion src2 = src;
|
||||
|
||||
// Run Lanczos and Arnoldi on a Hermitian matrix
|
||||
// Arnoldi Arn (Dsq, FGrid, 1e-8, false);
|
||||
// Arn(src, 1, Nm, -1);
|
||||
Arnoldi Arn (Dsq, FGrid, 1e-8, EvalNormLarge); // for comparison to Lanczos
|
||||
// Arn(src, maxIter, Nm, Nk, Nstop);
|
||||
|
||||
// auto tmpSrcDup = peekSite(src, origin);
|
||||
// std::cout << "[DEBUG] Source at origin = " << tmpSrcDup << std::endl;
|
||||
// auto tmpSrc2Dup = peekSite(src2, origin);
|
||||
// std::cout << "[DEBUG] Source2 at origin = " << tmpSrc2Dup << std::endl;
|
||||
|
||||
Arn(src, maxIter, Nm, Nk, Nstop);
|
||||
std::cout << "Hessenberg mat for symmetric N = " << Nm << std::endl;
|
||||
std::cout << Arn.getHessenbergMat() << std::endl;
|
||||
|
||||
// ImplicitlyRestartedLanczosHermOpTester<LatticeFermionD> SimpleTester (Dsq);
|
||||
// ImplicitlyRestartedLanczos<LatticeFermionD> Lanc (Dsq, Dsq, SimpleTester, Nm, Nm, Nm, 1e-8, Nm);
|
||||
int Nconv;
|
||||
PlainHermOp DsqHermOp (Dsq);
|
||||
// std::vector<RealD> levals (Nm+1); std::vector<LatticeFermionD> levecs (Nm+1, src);
|
||||
// ImplicitlyRestartedLanczos<LatticeFermionD> Lanc (DsqHermOp, DsqHermOp, Nm, Nm, Nm + 1, 1e-8, Nm);
|
||||
std::vector<RealD> levals (Nm+1); std::vector<LatticeFermionD> levecs (Nm, src);
|
||||
ImplicitlyRestartedLanczos<LatticeFermionD> Lanc (DsqHermOp, DsqHermOp, Nstop, Nk, Nm, 1e-8, maxIter);
|
||||
std::cout << GridLogMessage << "Calculating with Lanczos" << std::endl;
|
||||
|
||||
// auto tmpSrc1 = peekSite(src, origin);
|
||||
// std::cout << "[DEBUG] Source at origin = " << tmpSrc1 << std::endl;
|
||||
// auto tmpSrc2 = peekSite(src2, origin);
|
||||
// std::cout << "[DEBUG] Source2 at origin = " << tmpSrc2 << std::endl;
|
||||
// std::cout << "[DEBUG] Source norm2: " << norm2(src) << std::endl;
|
||||
|
||||
std::cout << "running Lanczos now" << std::endl;
|
||||
Lanc.calc(levals, levecs, src2, Nconv);
|
||||
|
||||
std::cout<<GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout<<GridLogMessage << "***************** RESULTS *****************" << std::endl;
|
||||
std::cout<<GridLogMessage << "*******************************************" << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Arnoldi eigenvalues: " << std::endl << Arn.getEvals() << std::endl;
|
||||
std::cout << GridLogMessage << "Lanczos eigenvalues: " << std::endl << levals << std::endl;
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./examples/Example_gamma5_block_lanczos.cc
|
||||
|
||||
Copyright (C) 2026
|
||||
|
||||
Author: Chulwoo Jung <chulwoo@bnl.gov>
|
||||
|
||||
γ5-Block Lanczos example for the Wilson Dirac operator.
|
||||
|
||||
Reads a gauge configuration from "config" (NERSC format) and Lanczos
|
||||
parameters from "LanParams.xml". Runs Gamma5BlockLanczos to
|
||||
compute eigenvalues of D_W directly (not H_W = γ5 D_W).
|
||||
|
||||
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 */
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
namespace Grid {
|
||||
|
||||
struct LanczosParameters : Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
Integer, maxIter,
|
||||
Integer, reorthog,
|
||||
Integer, verify,
|
||||
Integer, ReadEvec,
|
||||
RealD, resid)
|
||||
|
||||
LanczosParameters()
|
||||
: mass(-0.5), Nstop(10), Nk(20), maxIter(100),
|
||||
reorthog(1), verify(0), ReadEvec(0), resid(1e-8)
|
||||
{}
|
||||
|
||||
template<class ReaderClass>
|
||||
LanczosParameters(Reader<ReaderClass>& r) { initialize(r); }
|
||||
|
||||
template<class ReaderClass>
|
||||
void initialize(Reader<ReaderClass>& r) {
|
||||
read(r, "LanczosParameters", *this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Grid
|
||||
|
||||
template<class T>
|
||||
void writeField(T& in, std::string const& fname) {
|
||||
#ifdef HAVE_LIME
|
||||
std::cout << GridLogMessage << "Writing to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in, record, 0);
|
||||
WR.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef WilsonFermionD WilsonOp;
|
||||
typedef typename WilsonFermionD::FermionField FermionField;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv);
|
||||
|
||||
GridCartesian* UGrid = SpaceTimeGrid::makeFourDimGrid(
|
||||
GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd, vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian* UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
std::vector<int> seeds4({1, 2, 3, 4});
|
||||
GridParallelRNG RNG4(UGrid);
|
||||
RNG4.SeedFixedIntegers(seeds4);
|
||||
|
||||
// Read gauge configuration
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
FieldMetaData header;
|
||||
std::string configFile("config");
|
||||
NerscIO::readConfiguration(Umu, header, configFile);
|
||||
std::cout << GridLogMessage << "Loaded gauge configuration: " << configFile << std::endl;
|
||||
|
||||
// Read Lanczos parameters
|
||||
LanczosParameters Params;
|
||||
{
|
||||
XmlReader rd("LanParams.xml");
|
||||
read(rd, "LanczosParameters", Params);
|
||||
}
|
||||
std::cout << GridLogMessage << Params << std::endl;
|
||||
|
||||
// Build Wilson Dirac operator and wrap in a non-Hermitian linear operator
|
||||
std::vector<Complex> boundary = {1, 1, 1, -1};
|
||||
WilsonOp::ImplParams WilsonParams(boundary);
|
||||
WilsonOp Dwilson(Umu, *UGrid, *UrbGrid, Params.mass, WilsonParams);
|
||||
NonHermitianLinearOperator<WilsonOp, FermionField> DLinOp(Dwilson);
|
||||
|
||||
// γ5 functor: for 4D Wilson fermions γ5 is Gamma(Gamma5)
|
||||
Gamma G5(Gamma::Algebra::Gamma5);
|
||||
auto gamma5 = [&G5](const FermionField& in, FermionField& out) {
|
||||
out = G5 * in;
|
||||
};
|
||||
|
||||
// Starting vectors: two independent random vectors
|
||||
FermionField src(UGrid), src2(UGrid);
|
||||
random(RNG4, src);
|
||||
random(RNG4, src2);
|
||||
std::cout << GridLogMessage << "Using two random starting vectors" << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout << GridLogMessage << " Running γ5-Block Lanczos" << std::endl;
|
||||
std::cout << GridLogMessage << " mass = " << Params.mass << std::endl;
|
||||
std::cout << GridLogMessage << " Nk = " << Params.Nk << std::endl;
|
||||
std::cout << GridLogMessage << " maxIter = " << Params.maxIter << std::endl;
|
||||
std::cout << GridLogMessage << " Nstop = " << Params.Nstop << std::endl;
|
||||
std::cout << GridLogMessage << " resid = " << Params.resid << std::endl;
|
||||
std::cout << GridLogMessage << " reorthog = " << Params.reorthog << std::endl;
|
||||
std::cout << GridLogMessage << " verify = " << Params.verify << std::endl;
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
|
||||
Gamma5BlockLanczos<FermionField> G5BL(DLinOp, UGrid, gamma5, Params.resid);
|
||||
G5BL.doEvalCheck = (Params.verify != 0);
|
||||
G5BL.doVerify = (Params.verify != 0);
|
||||
// G5BL(src, src2, Params.maxIter, Params.Nstop, Params.reorthog != 0);
|
||||
G5BL.restart(src, src2, Params.maxIter, Params.Nk+Params.Np, Params.Nk, Params.Nstop, Params.reorthog != 0, EvalNormSmall);
|
||||
// G5BL.implicitRestart(src, src2, Params.maxIter, Params.Nk+Params.Np, Params.Nk, Params.Nstop, Params.reorthog != 0, EvalNormSmall);
|
||||
if (Params.verify ) G5BL.verify("after restart");
|
||||
|
||||
// Summary of results
|
||||
Eigen::VectorXcd evals = G5BL.getEvals();
|
||||
std::vector<RealD> residuals = G5BL.getResiduals();
|
||||
std::vector<FermionField> evecs = G5BL.getEvecs();
|
||||
|
||||
int Nout = (int)evals.size();
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout << GridLogMessage << " γ5-Block Lanczos: " << Nout << " Ritz pairs" << std::endl;
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl;
|
||||
for (int i = 0; i < Nout; i++) {
|
||||
std::cout << GridLogMessage
|
||||
<< " [" << std::setw(3) << i << "]"
|
||||
<< " lambda = " << evals(i)
|
||||
<< " |res| = " << residuals[i] << std::endl;
|
||||
}
|
||||
|
||||
// Write the first Nstop eigenvectors (in SCIDAC format when LIME is available)
|
||||
int Nwrite = std::min((int)Params.Nstop, Nout);
|
||||
for (int i = 0; i < Nwrite; i++) {
|
||||
std::string fname = "./g5bl_evec_m" + std::to_string(Params.mass)
|
||||
+ "_" + std::to_string(i);
|
||||
writeField(evecs[i], fname);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << std::endl;
|
||||
std::cout << GridLogMessage << "Done" << std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./tests/Test_padded_cell.cc
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
|
||||
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 */
|
||||
|
||||
// Tests code written to read off the Krylov coefficients
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
#include <Grid/algorithms/iterative/ConjugateGradient.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
// Hermitize a DWF operator by squaring it
|
||||
template<class Matrix,class Field>
|
||||
class SquaredLinearOperator : public LinearOperatorBase<Field> {
|
||||
|
||||
public:
|
||||
Matrix &_Mat;
|
||||
|
||||
public:
|
||||
SquaredLinearOperator(Matrix &Mat): _Mat(Mat) {};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
// std::cout << "Op is overloaded as HermOp" << std::endl;
|
||||
HermOp(in, out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
HermOp(in, out);
|
||||
}
|
||||
void _Op (const Field &in, Field &out){
|
||||
// std::cout << "Op: M "<<std::endl;
|
||||
_Mat.M(in, out);
|
||||
}
|
||||
void _AdjOp (const Field &in, Field &out){
|
||||
// std::cout << "AdjOp: Mdag "<<std::endl;
|
||||
_Mat.Mdag(in, out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
// std::cout << "HermOp: Mdag M Mdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Op(in,tmp);
|
||||
_AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the coefficients in the Krylov expansion for 1/D ~ \sum_{i=0}^N c_i D^i.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* std::vector<double> &coeffs
|
||||
* Polynomial coeffients to return, with indexing order (c_0, c_1, c_2, ..., c_n).
|
||||
* LinearOperatorBase<FineField> &DiracOp
|
||||
* Dirac operator D.
|
||||
* FineField src
|
||||
* Source field b.
|
||||
* FineField psiStar
|
||||
* Output approximation for D^{-1} b coming from a Krylov method.
|
||||
* int N
|
||||
* Dimension of the polynomial approximation (Krylov space K_{N-1} = {b, Db, D^2 b, ..., D^{N-1} b}).
|
||||
*/
|
||||
void poly_coeffs(std::vector<ComplexD> &coeffs, LinearOperatorBase<LatticeFermion> &DiracOp, LatticeFermion src,
|
||||
LatticeFermion psiStar, GridCartesian* FGrid, int N, bool use_herm = false)
|
||||
{
|
||||
// stdBasis = {b, Db, D^2 b, ..., D^N b}, kryBasis = {k0, k1, ..., kN}
|
||||
std::vector<LatticeFermion> kryBasis;
|
||||
Eigen::VectorXcd psiStarCoeffs (N);
|
||||
|
||||
// Normalize by 1 / ||src||; does not change the polynomial coefficients
|
||||
double srcNorm = 1 / std::sqrt(norm2(src));
|
||||
kryBasis.push_back(srcNorm * src); // normalized source
|
||||
psiStar = srcNorm * psiStar;
|
||||
psiStarCoeffs(0) = innerProduct(kryBasis[0], psiStar);
|
||||
|
||||
// orthonormalize canonical Krylov basis {b, Db, D^2 b, ..., D^{N-1} b} <--> {k_i} and compute components <k_i | psi*>
|
||||
LatticeFermion tmp (FGrid);
|
||||
for (int i = 0; i < N - 1; i++) { // construct ONB for {b, Db, ..., D^{i+1} b}
|
||||
if (use_herm) {
|
||||
DiracOp.HermOp(kryBasis.back(), tmp); // tmp \in span{(D^\dag D)^{i+1} b} \oplus span{(D^\dag D)^i b, ..., D^\dag D b, b}
|
||||
} else {
|
||||
DiracOp.Op(kryBasis.back(), tmp); // tmp \in span{D^{i+1} b} \oplus span{D^i b, ..., Db, b}
|
||||
}
|
||||
|
||||
for (int j = 0; j < i+1; j++) { // orthogonalize tmp with previous basis vectors
|
||||
ComplexD coeff = innerProduct(kryBasis[j], tmp); // <k_j | tmp>
|
||||
tmp -= coeff * kryBasis[j]; // subtract off |k_j><k_j | tmp>; now tmp is perp to |k_j>
|
||||
}
|
||||
double tmpNorm = 1 / std::sqrt(norm2(tmp));
|
||||
kryBasis.push_back(
|
||||
tmpNorm * tmp
|
||||
); // normalize |k_i> and add to kryBasis
|
||||
psiStarCoeffs(i+1) = innerProduct(kryBasis[i+1], psiStar); // compute < k_i | psi* >
|
||||
}
|
||||
|
||||
// To verify the basis is ONB
|
||||
// for (int i = 0; i < N; i++) {
|
||||
// for (int j = 0; j < N; j++) {
|
||||
// std::cout << "<ki|kj> for (i,j) = (" << i << ", " << j << ") = " << innerProduct(kryBasis[i], kryBasis[j]) << std::endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Compute change of basis matrix
|
||||
LatticeFermion tmp2 (FGrid);
|
||||
Eigen::MatrixXcd M = Eigen::MatrixXcd::Zero(N, N);
|
||||
tmp = kryBasis[0]; // current Krylov vector; starts with tmp = src (normalized)
|
||||
for (int i = 0; i < N; i++) {
|
||||
for (int j = 0; j < i + 1; j++) { // fill column with components of kryVec. Only need j <= i to get orthonormal components
|
||||
M(j, i) = innerProduct(kryBasis[j], tmp);
|
||||
}
|
||||
if (use_herm) { // tmp --> D^\dag D(tmp)
|
||||
DiracOp.HermOp(tmp, tmp2);
|
||||
tmp = tmp2;
|
||||
} else { // tmp --> D(tmp). Note that DiracOp.Op(tmp, tmp) will cause a bug
|
||||
DiracOp.Op(tmp, tmp2);
|
||||
tmp = tmp2;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute M^{-1} @ psiStarCoeffs and copy to coeffs
|
||||
Eigen::VectorXcd res (N);
|
||||
res = M.inverse() * psiStarCoeffs;
|
||||
for (int i = 0; i < N; i++) {
|
||||
coeffs[i] = res(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// out file for poly coefficients (should it be complex?)
|
||||
// class PolynomialFile: Serializable {
|
||||
// public:
|
||||
// GRID_SERIALIZABLE_CLASS_MEMBERS(OutputFile, std::vector< Real >, data);
|
||||
// };
|
||||
|
||||
std::complex<double> poly_approx(std::complex<double> x, std::vector<std::complex<double>> coeffs) {
|
||||
std::complex<double> px;
|
||||
for (int i = 0; i < coeffs.size(); i++) {
|
||||
px += coeffs[i] * std::pow(x, i);
|
||||
}
|
||||
return px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the approximation psi = \sum_i c_i D^i b resulting from a Krylov solver.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* LatticeFermion &psi
|
||||
* Approximation field, returned psi = \sum_i c_i D^i b.
|
||||
* LatticeFermion src
|
||||
* Source b used to generate the Krylov space K_n(D, b).
|
||||
* LinearOperatorBase<LatticeFermion> &Linop
|
||||
* Dirac operator used to generate the Krylov space K_n(D, b).
|
||||
* std::vector<std::complex<double>> coeffs
|
||||
* Polynomial coefficients returned from the solver.
|
||||
*/
|
||||
void krylovApprox(LatticeFermion &psi, LatticeFermion src, LinearOperatorBase<LatticeFermion> &Linop, std::vector<ComplexD> coeffs) {
|
||||
psi = Zero();
|
||||
LatticeFermion tmp (psi.Grid());
|
||||
tmp = src;
|
||||
LatticeFermion tmp2 (psi.Grid());
|
||||
for (int i = 0; i < coeffs.size(); i++) {
|
||||
psi = psi + coeffs[i] * tmp;
|
||||
Linop.Op(tmp, tmp2); // tmp = D*tmp
|
||||
tmp = tmp2;
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv);
|
||||
const int Ls = 8;
|
||||
std::vector<int> lat_size {16, 16, 16, 32};
|
||||
std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size,
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// You can manage seeds however you like.
|
||||
// Recommend SeedUniqueString.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// std::vector<int> seeds4({1, 2, 3, 4});
|
||||
// GridParallelRNG RNG4(UGrid);
|
||||
// RNG4.SeedFixedIntegers(seeds4);
|
||||
|
||||
// std::vector<int> seeds5({1, 2, 3, 4, 5});
|
||||
// GridParallelRNG RNG5(FGrid);
|
||||
// RNG5.SeedFixedIntegers(seeds5);
|
||||
|
||||
// std::string outStrStem = "/Users/patrickoare/Dropbox (MIT)/research/multigrid/grid_out/";
|
||||
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
FieldMetaData header;
|
||||
std::string file("/Users/patrickoare/libraries/PETSc-Grid/ckpoint_lat.4000");
|
||||
NerscIO::readConfiguration(Umu, header, file);
|
||||
|
||||
RealD mass=0.01;
|
||||
RealD M5=1.8;
|
||||
// RealD M5=1.0;
|
||||
RealD b=1.5;// Scale factor b+c=2, b-c=1
|
||||
RealD c=0.5;
|
||||
|
||||
// load in Dirac operators that we'll use; square it to Hermitize
|
||||
// Dsq just needs to be a Hermitian operator so we can use CG on it
|
||||
DomainWallFermionD Ddwf(Umu, *FGrid, *FrbGrid, *UGrid, *UrbGrid, mass, M5);
|
||||
SquaredLinearOperator<DomainWallFermionD, LatticeFermionD> Dsq (Ddwf);
|
||||
NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
LatticeFermion src (FGrid); src = 1.0; // Source to use
|
||||
LatticeFermion psiCG (FGrid); psiCG = Zero(); // Field to solve with for CG
|
||||
LatticeFermion psiGCR (FGrid); psiGCR = Zero(); // Field to solve with for GCR
|
||||
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout << GridLogMessage << "********** TESTING CG POLY COEFFS *********" << std::endl;
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl << std::endl;
|
||||
|
||||
double tol = 1.0e-8;
|
||||
int N = 5; // max iterations (size of Krylov basis)
|
||||
|
||||
// GCR variables
|
||||
int outer_iters = 1; // num restarts for GCR
|
||||
TrivialPrecon<LatticeFermionD> prec; // trivial preconditioner
|
||||
|
||||
ConjugateGradientPolynomial<LatticeFermion> CGP(tol, N, false);
|
||||
CGP(Dsq, src, psiCG);
|
||||
|
||||
// Compute Krylov coeffs directly and compare
|
||||
std::vector<ComplexD> cg_coeffs (N);
|
||||
poly_coeffs(cg_coeffs, Dsq, src, psiCG, FGrid, N, true);
|
||||
|
||||
PolynomialFile PF;
|
||||
|
||||
// Use GCR solver, also get poly coeffs
|
||||
std::vector<ComplexD> gcr_sym_coeffs (N); // Can try N --> N + 3 to test to see if the last 3 comps are 0
|
||||
PGCRPolynomial<LatticeFermionD> GCRPolySym(tol, outer_iters, Dsq, prec, N+1, N, PF); // mmax sets the memory, note the last beta doesn't really matter for updating the polynomial
|
||||
GCRPolySym(src, psiGCR);
|
||||
// poly_coeffs(gcr_sym_coeffs, Dsq, src, psi, FGrid, N, true);
|
||||
poly_coeffs(gcr_sym_coeffs, Dsq, src, psiGCR, FGrid, N, true);
|
||||
|
||||
std::cout << GridLogMessage << std::endl << "******** CG POLYNOMIAL COEFFICIENTS *******" << std::endl;
|
||||
std::cout << GridLogMessage << CGP.polynomial << std::endl << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "****** DIRECT POLYNOMIAL COEFFICIENTS *****" << std::endl;
|
||||
std::cout << GridLogMessage << cg_coeffs << std::endl << std::endl;
|
||||
|
||||
// TODO: try GCR with a Hermitian operator (Dsq)
|
||||
std::cout << GridLogMessage << "****** GCR COEFFICIENTS *****" << std::endl;
|
||||
std::cout << GridLogMessage << GCRPolySym.polynomial << std::endl << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "****** DIRECT GCR COEFFICIENTS *****" << std::endl;
|
||||
std::cout << GridLogMessage << gcr_sym_coeffs << std::endl << std::endl;
|
||||
|
||||
// test how good the decomposition is
|
||||
std::cout << "Testing fidelity of decomposition by computing ||psi* - sum_i c_i D^i b||^2!" << std::endl;
|
||||
LatticeFermion psiPrime (FGrid);
|
||||
|
||||
// for CG
|
||||
krylovApprox(psiPrime, src, Dsq, cg_coeffs);
|
||||
std::cout << "CG with Dsq, ||psi - psiPrime||^2 = " << norm2(psiCG - psiPrime) << std::endl;
|
||||
|
||||
// for GCR with alpha / beta computation
|
||||
krylovApprox(psiPrime, src, Dsq, GCRPolySym.polynomial);
|
||||
std::cout << "GCR with Dsq, ||psi - psiPrime||^2 = " << norm2(psiGCR - psiPrime) << std::endl;
|
||||
|
||||
// for GCR with alpha / beta computation
|
||||
krylovApprox(psiPrime, src, Dsq, gcr_sym_coeffs);
|
||||
std::cout << "GCR direct with Dsq, ||psi - psiPrime||^2 = " << norm2(psiGCR - psiPrime) << std::endl;
|
||||
|
||||
|
||||
// std::vector<double> real_cg_diff (N);
|
||||
// for (int i = 0; i < N; i++) { real_cg_diff[i] = std::abs(cg_coeffs[i].real() - CGP.polynomial[i]); }
|
||||
// std::cout << GridLogMessage << "************* COEFF DIFFERENCE ************" << std::endl;
|
||||
// std::cout << GridLogMessage << real_cg_diff << std::endl << std::endl;
|
||||
|
||||
// GCR polynomial reconstruction with Ddwf!
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout << GridLogMessage << "********* TESTING GCR POLY COEFFS *********" << std::endl;
|
||||
std::cout << GridLogMessage << "*******************************************" << std::endl << std::endl;
|
||||
|
||||
// re-init variables
|
||||
src = 1.0;
|
||||
src = (1 / std::sqrt(norm2(src))) * src;
|
||||
psiGCR = Zero(); psiPrime = Zero();
|
||||
|
||||
// test GCR poly
|
||||
PGCRPolynomial<LatticeFermionD> GCRPoly(tol, outer_iters, DLinOp, prec, N+1, N, PF); // mmax sets the memory, note the last beta doesn't really matter for updating the polynomial
|
||||
GCRPoly(src, psiGCR);
|
||||
|
||||
// Compute Krylov coeffs directly and compare
|
||||
// N = 1; // compare the N > 1 decomposition with the psi* resulting from N = 1
|
||||
std::vector<ComplexD> gcr_coeffs (N); // note N --> N + k should just give k coeffs that are 0; this works as intended
|
||||
poly_coeffs(gcr_coeffs, DLinOp, src, psiGCR, FGrid, N, false);
|
||||
|
||||
std::cout << GridLogMessage << "******* GCR POLYNOMIAL COEFFICIENTS *******" << std::endl;
|
||||
std::cout << GridLogMessage << GCRPoly.polynomial << std::endl << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "****** DIRECT POLYNOMIAL COEFFICIENTS *****" << std::endl;
|
||||
std::cout << GridLogMessage << gcr_coeffs << std::endl << std::endl;
|
||||
|
||||
// test how good the decomposition is
|
||||
std::cout << "Testing fidelity of decomposition by computing ||psi* - sum_i c_i D^i b||^2!" << std::endl;
|
||||
|
||||
// for GCR with alpha / beta computation
|
||||
krylovApprox(psiPrime, src, DLinOp, GCRPoly.polynomial);
|
||||
std::cout << "GCR with Dsq, ||psi - psiPrime||^2 = " << norm2(psiGCR - psiPrime) << std::endl;
|
||||
|
||||
// for GCR with alpha / beta computation
|
||||
krylovApprox(psiPrime, src, DLinOp, gcr_coeffs);
|
||||
std::cout << "GCR direct with Dsq, ||psi - psiPrime||^2 = " << norm2(psiGCR - psiPrime) << std::endl;
|
||||
|
||||
// TESTS TO DO THE N = 2 CASE DIRECTLY
|
||||
/*
|
||||
std::vector<std::complex<double>> alphas {
|
||||
std::complex(0.244300601, 0.00013007545),
|
||||
std::complex(0.285370971, -0.000160704481)
|
||||
};
|
||||
std::complex<double> beta00 (-0.184661284, -6.52153945e-05);
|
||||
LatticeFermion psi2 (FGrid);
|
||||
LatticeFermion Dsrc (FGrid);
|
||||
DLinOp.Op(src, Dsrc);
|
||||
std::complex<double> c1 = alphas[0] + alphas[1] * (1. + beta00);
|
||||
std::complex<double> c2 = -alphas[0] * alphas[1];
|
||||
psi2 = c1 * src + c2 * Dsrc;
|
||||
|
||||
std::cout << "||b|| = " << norm2(src) << std::endl;
|
||||
std::cout << "||Db|| = " << norm2(Dsrc) << std::endl;
|
||||
// fail; so far this is giving something different than what's being computed in krylovApprox (idk how?)
|
||||
|
||||
std::cout << "c1 and c2 are: " << c1 << " and " << c2 << std::endl;
|
||||
std::cout << "GCRPoly polynomial coeffs are (should equal c1 and c2): " << GCRPoly.polynomial << std::endl;
|
||||
std::cout << "||GCRpsi - psi2||_2^2 = " << norm2(psiGCR - psi2) << std::endl;
|
||||
// pass
|
||||
|
||||
LatticeFermion src2 (FGrid);
|
||||
src2 = 1.0;
|
||||
src2 = (1 / std::sqrt(norm2(src2))) * src2;
|
||||
std::cout << "||ones - src|| (to verify that src is the same throughout, should be 0) = " << norm2(src2 - src) << std::endl;
|
||||
// pass
|
||||
|
||||
krylovApprox(psiPrime, src, DLinOp, GCRPoly.polynomial);
|
||||
std::cout << "GCR with Dsq, ||psi2 - psiPrime||^2 = " << norm2(psi2 - psiPrime) << std::endl;
|
||||
|
||||
std::vector<ComplexD> psi2_coeffs (N); // note N --> N + k should just give k coeffs that are 0; this works as intended
|
||||
poly_coeffs(psi2_coeffs, DLinOp, src, psi2, FGrid, N, false);
|
||||
krylovApprox(psiPrime, src, DLinOp, psi2_coeffs);
|
||||
std::cout << "GCR direct with Dsq, ||psi - psiPrime||^2 = " << norm2(psi2 - psiPrime) << std::endl;
|
||||
*/
|
||||
|
||||
// std::complex z (10.0, 0.0); // z = 10
|
||||
// std::cout << GridLogMessage << "************* GCR POLY(z = 10) *************" << std::endl;
|
||||
// std::cout << GridLogMessage << poly_approx(z, GCRPoly.polynomial) << std::endl;
|
||||
// std::cout << GridLogMessage << "************ DIRECT POLY(z = 10) ***********" << std::endl;
|
||||
// std::cout << GridLogMessage << poly_approx(z, gcr_coeffs) << std::endl;
|
||||
|
||||
// std::vector<std::complex<double>> gcr_diff (N);
|
||||
// for (int i = 0; i < N; i++) { gcr_diff[i] = gcr_coeffs[i] - GCRPoly.polynomial[i]; }
|
||||
// std::cout << GridLogMessage << "*********** GCR COEFF DIFFERENCE **********" << std::endl;
|
||||
// std::cout << GridLogMessage << gcr_diff << std::endl << std::endl;
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./examples/Example_krylov_schur.cc
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <patrickoare@gmail.com>
|
||||
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 */
|
||||
|
||||
// copied here from Test_general_coarse_pvdagm.cc
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
namespace Grid {
|
||||
|
||||
struct LanczosParameters: Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass ,
|
||||
RealD, mstep ,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
Integer, ReadEvec,
|
||||
Integer, maxIter,
|
||||
Integer, Nblock,
|
||||
Integer, verify,
|
||||
RealD, shift ,
|
||||
RealD, resid,
|
||||
RealD, ChebyLow,
|
||||
RealD, ChebyHigh,
|
||||
Integer, ChebyOrder)
|
||||
|
||||
LanczosParameters() {
|
||||
////////////////////////////// Default values
|
||||
mass = 0;
|
||||
/////////////////////////////////
|
||||
}
|
||||
|
||||
template <class ReaderClass >
|
||||
LanczosParameters(Reader<ReaderClass> & TheReader){
|
||||
initialize(TheReader);
|
||||
}
|
||||
|
||||
template < class ReaderClass >
|
||||
void initialize(Reader<ReaderClass> &TheReader){
|
||||
// std::cout << GridLogMessage << "Reading HMC\n";
|
||||
read(TheReader, "HMC", *this);
|
||||
}
|
||||
|
||||
|
||||
void print_parameters() const {
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Trajectories : " << Trajectories << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Start trajectory : " << StartTrajectory << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Metropolis test (on/off): " << std::boolalpha << MetropolisTest << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Thermalization trajs : " << NoMetropolisUntil << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Starting type : " << StartingType << "\n";
|
||||
// MD.print_parameters();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#if 1
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0);
|
||||
WR.close();
|
||||
#endif
|
||||
// What is the appropriate way to throw error?
|
||||
}
|
||||
|
||||
|
||||
typedef WilsonFermionD WilsonOp;
|
||||
typedef typename WilsonFermionD::FermionField FermionField;
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class InvertNonHermitianLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
RealD _stp;
|
||||
public:
|
||||
InvertNonHermitianLinearOperator(Matrix &Mat,RealD stp=1e-8): _Mat(Mat),_stp(stp){};
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
assert(0);
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
assert(0);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
// _Mat.MdirAll(in,out);
|
||||
assert(0);
|
||||
};
|
||||
void Op (const Field &in, Field &out){
|
||||
Field tmp(in.Grid());
|
||||
_Mat.Mdag(in,tmp);
|
||||
MdagMLinearOperator<Matrix,Field> HermOp(_Mat);
|
||||
ConjugateGradient<Field> CG(_stp,10000);
|
||||
CG(HermOp,tmp,out);
|
||||
// out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
_Mat.Mdag(in,out);
|
||||
// out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
assert(0);
|
||||
}
|
||||
void HermOp(const Field &in, Field &out){
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Field>
|
||||
void testSchurFromHess(Arnoldi<Field>& Arn, Field& src, int Nlarge, int Nm, int Nk) {
|
||||
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout << GridLogMessage << "Testing Schur reordering, Nm = " << Nm << ", Nk = " << Nk << std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Running Arnoldi for 1 iteration to get a Hessenberg." << std::endl;
|
||||
Arn(src, 1, Nlarge, Nm, Nlarge);
|
||||
Eigen::MatrixXcd Hess = Arn.getHessenbergMat();
|
||||
std::cout << GridLogMessage << "Hessenberg for use: " << std::endl << Hess << std::endl;
|
||||
|
||||
ComplexSchurDecomposition schur (Hess, true);
|
||||
bool isDecomposed = schur.checkDecomposition();
|
||||
std::cout << "Schur decomp holds? " << isDecomposed << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "S = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << GridLogMessage << "Swapping S(3, 3) with S(4, 4)" << std::endl;
|
||||
schur.swapEvals(3);
|
||||
std::cout << GridLogMessage << "S after swap = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << "Schur decomp still holds? " << schur.checkDecomposition() << std::endl;
|
||||
|
||||
// Now move last diagonal element all the way to the front.
|
||||
std::cout << GridLogMessage << "Moving last eval to front. S at start = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
for (int i = 0; i < Nk - 1; i++) {
|
||||
int swapIdx = Nk - 2 - i;
|
||||
schur.swapEvals(swapIdx);
|
||||
std::cout << GridLogMessage << "S after swap of index " << swapIdx << " = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << "Schur decomp still holds? " << schur.checkDecomposition() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "Testing Schur reorder" << std::endl;
|
||||
schur.schurReorder(Nk);
|
||||
std::cout << GridLogMessage << "S after reorder = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << "Schur decomp still holds? " << schur.checkDecomposition() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
const int Ls=16;
|
||||
|
||||
// GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
|
||||
// std::vector<int> lat_size {32, 32, 32, 32};
|
||||
// std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
// GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
// GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
GridCartesian * FGrid = UGrid;
|
||||
GridRedBlackCartesian * FrbGrid = UrbGrid;
|
||||
|
||||
// Construct a coarsened grid
|
||||
// poare TODO: replace this with the following line?
|
||||
Coordinate clatt = GridDefaultLatt();
|
||||
// Coordinate clatt = GridDefaultLatt(); // [PO] initial line before I edited it
|
||||
for(int d=0;d<clatt.size();d++){
|
||||
std::cout << GridLogMessage<< clatt[d] <<std::endl;
|
||||
clatt[d] = clatt[d]/2;
|
||||
// clatt[d] = clatt[d]/4;
|
||||
}
|
||||
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());;
|
||||
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
|
||||
|
||||
std::vector<int> seeds4({1,2,3,4});
|
||||
std::vector<int> seeds5({5,6,7,8});
|
||||
std::vector<int> cseeds({5,6,7,8});
|
||||
GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5);
|
||||
GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4);
|
||||
GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds);
|
||||
|
||||
LatticeFermion result(FGrid); result=Zero();
|
||||
LatticeFermion ref(FGrid); ref=Zero();
|
||||
LatticeFermion tmp(FGrid);
|
||||
LatticeFermion err(FGrid);
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
|
||||
FieldMetaData header;
|
||||
std::string file("config");
|
||||
// std::string file("Users/patrickoare/libraries/PETSc-Grid/ckpoint_lat.4000");
|
||||
NerscIO::readConfiguration(Umu,header,file);
|
||||
|
||||
LanczosParameters LanParams;
|
||||
{
|
||||
XmlReader HMCrd("LanParams.xml");
|
||||
read(HMCrd,"LanczosParameters",LanParams);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage<< LanParams <<std::endl;
|
||||
{
|
||||
XmlWriter HMCwr("LanParams.xml.out");
|
||||
write(HMCwr,"LanczosParameters",LanParams);
|
||||
}
|
||||
|
||||
|
||||
RealD mass=0.01;
|
||||
RealD M5=1.8;
|
||||
|
||||
// PowerMethod<LatticeFermion> PM; PM(PVdagM, src);
|
||||
int Nm = 50;
|
||||
int Nk = 12;
|
||||
int Np = 38;
|
||||
// int Nk = Nm+1; // if just running once
|
||||
int maxIter = 10000;
|
||||
int Nstop = 10;
|
||||
RealD resid = 1.0e-5;
|
||||
|
||||
std::vector<Complex> boundary = {1,1,1,-1};
|
||||
WilsonOp::ImplParams Params(boundary);
|
||||
|
||||
// DomainWallFermionD Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5);
|
||||
// DomainWallFermionD Dpv(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,1.0,M5);
|
||||
|
||||
mass=LanParams.mass;
|
||||
std::cout << GridLogIRL<< "mass "<<mass<<std::endl;
|
||||
WilsonOp WilsonOperator(Umu,*UGrid,*UrbGrid,mass,Params);
|
||||
|
||||
// const int nbasis = 20; // size of approximate basis for low-mode space
|
||||
const int nbasis = 3; // size of approximate basis for low-mode space
|
||||
const int cb = 0 ;
|
||||
LatticeFermion prom(FGrid);
|
||||
|
||||
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
|
||||
typedef LittleDiracOperator::CoarseVector CoarseVector;
|
||||
|
||||
NextToNearestStencilGeometry5D geom(Coarse5d);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
|
||||
// typedef PVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> PVdagM_t;
|
||||
// typedef ShiftedPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedPVdagM_t;
|
||||
// typedef ShiftedComplexPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedComplexPVdagM_t;
|
||||
// PVdagM_t PVdagM(Ddwf, Dpv);
|
||||
// ShiftedPVdagM_t ShiftedPVdagM(0.1,Ddwf,Dpv);
|
||||
// SquaredLinearOperator<DomainWallFermionD, LatticeFermionD> Dsq (Ddwf);
|
||||
// NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
|
||||
NonHermitianLinearOperator<WilsonOp,FermionField> Dwilson(WilsonOperator); /// <-----
|
||||
InvertNonHermitianLinearOperator<WilsonOp,FermionField> Iwilson(WilsonOperator); /// <-----
|
||||
MdagMLinearOperator<WilsonOp,FermionField> HermOp(WilsonOperator); /// <-----
|
||||
Gamma5HermitianLinearOperator <WilsonOp,LatticeFermion> HermOp2(WilsonOperator); /// <----
|
||||
|
||||
// PowerMethod<LatticeFermion> PM; PM(PVdagM, src);
|
||||
resid=LanParams.resid;
|
||||
Nstop=LanParams.Nstop;
|
||||
Nk=LanParams.Nk;
|
||||
Np=LanParams.Np;
|
||||
maxIter=LanParams.maxIter;
|
||||
Nm = Nk + Np;
|
||||
int Nu=16;
|
||||
std::vector<LatticeFermion> src(Nu,FGrid);
|
||||
for(int i=0;i<Nu;i++){
|
||||
random(RNG5,src[i]);
|
||||
#if 0
|
||||
LatticeFermion src_e(FrbGrid);
|
||||
pickCheckerboard(Even, src_e, src[i]);
|
||||
src_e=Zero();
|
||||
setCheckerboard(src[i],src_e);
|
||||
std::cout << GridLogMessage << "src["<<i<<"] even sites zeroed " << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(LanParams.ReadEvec) {
|
||||
std::string evecs_file="evec_in";
|
||||
std::cout << GridLogIRL<< "Reading evecs from "<<evecs_file<<std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacReader RD;
|
||||
RD.open(evecs_file);
|
||||
RD.readScidacFieldRecord(src[0],record);
|
||||
RD.close();
|
||||
}
|
||||
|
||||
Coordinate origin ({0,0,0,0});
|
||||
auto tmpSrc = peekSite(src[0], origin);
|
||||
std::cout << "[DEBUG] Source at origin = " << tmpSrc << std::endl;
|
||||
LatticeFermion src2 = src[0];
|
||||
|
||||
// Run KrylovSchur / BlockKrylovSchur / TrueHarmonicBlockKrylovSchur on a Hermitian matrix
|
||||
RealD shift=LanParams.shift;
|
||||
|
||||
// --use-blockKS : off -> plain (non-block) KrylovSchur
|
||||
// on -> block family (BlockKrylovSchur / TrueHarmonicBlockKrylovSchur)
|
||||
// --use-harmonic: off -> no shift/harmonic Ritz extraction
|
||||
// on, without --use-blockKS -> plain KrylovSchur with shifted (harmonic) Schur extraction
|
||||
// on, with --use-blockKS -> TrueHarmonicBlockKrylovSchur (true harmonic Ritz)
|
||||
bool useBlockKS = GridCmdOptionExists(argv, argv+argc, std::string("--use-blockKS"));
|
||||
bool useHarmonic = GridCmdOptionExists(argv, argv+argc, std::string("--use-harmonic"));
|
||||
|
||||
std::vector<LatticeFermion> finalEvecs;
|
||||
|
||||
if (!useBlockKS) {
|
||||
KrylovSchur KrySchur (Dwilson, UGrid, resid,EvalImNormSmall);
|
||||
KrySchur.doEvalCheck=true;
|
||||
if (useHarmonic) {
|
||||
std::cout << GridLogMessage << "Running KrylovSchur (shifted/harmonic Ritz)" << std::endl;
|
||||
KrySchur(src[0], maxIter, Nm, Nk, Nstop,&shift);
|
||||
} else {
|
||||
std::cout << GridLogMessage << "Running KrylovSchur" << std::endl;
|
||||
KrySchur(src[0], maxIter, Nm, Nk, Nstop);
|
||||
}
|
||||
std::cout << GridLogMessage << "KrylovSchur evec.size= " << KrySchur.evecs.size()<< std::endl;
|
||||
finalEvecs = KrySchur.evecs;
|
||||
} else {
|
||||
int Nblock=4;
|
||||
Nblock=LanParams.Nblock;
|
||||
bool if_verify=false;
|
||||
if(LanParams.verify) if_verify=true;
|
||||
|
||||
auto gamma5Lambda = [](const FermionField& v, FermionField& out) {
|
||||
Gamma g5(Gamma::Algebra::Gamma5);
|
||||
out = g5 * v;
|
||||
};
|
||||
|
||||
if (useHarmonic) {
|
||||
std::cout << GridLogMessage << "Running TrueHarmonicBlockKrylovSchur" << std::endl;
|
||||
TrueHarmonicBlockKrylovSchur<FermionField> KrySchur (Dwilson, UGrid, resid,shift,EvalImNormSmall);
|
||||
KrySchur.doEvalCheck=true;
|
||||
KrySchur.gamma5Func = gamma5Lambda;
|
||||
KrySchur(src, maxIter, Nm, Nk, Nstop,Nblock,true,if_verify);
|
||||
std::cout << GridLogMessage << "TrueHarmonicBlockKrylovSchur evec.size= " << KrySchur.evecs.size()<< std::endl;
|
||||
finalEvecs = KrySchur.evecs;
|
||||
} else {
|
||||
std::cout << GridLogMessage << "Running BlockKrylovSchur" << std::endl;
|
||||
BlockKrylovSchur<FermionField> KrySchur (Dwilson, UGrid, resid,EvalImNormSmall);
|
||||
KrySchur.doEvalCheck=true;
|
||||
KrySchur.gamma5Func = gamma5Lambda;
|
||||
KrySchur(src, maxIter, Nm, Nk, Nstop,Nblock,true,if_verify);
|
||||
std::cout << GridLogMessage << "BlockKrylovSchur evec.size= " << KrySchur.evecs.size()<< std::endl;
|
||||
finalEvecs = KrySchur.evecs;
|
||||
}
|
||||
}
|
||||
|
||||
src[0]=finalEvecs[0];
|
||||
std::cout << GridLogMessage << "finalEvecs= "<< finalEvecs.size() <<std::endl;
|
||||
for (int i=1;i<Nk;i++) src[0]+=finalEvecs[i];
|
||||
for (int i=0;i<Nstop;i++)
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_"+std::to_string(i));
|
||||
auto evdensity = localInnerProduct(finalEvecs[i],finalEvecs[i] );
|
||||
writeFile(evdensity,evfile);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_sum");
|
||||
// auto evdensity = localInnerProduct(evec[i],evec[i] );
|
||||
writeFile(src[0],evfile);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
std::cout << GridLogMessage << "Running Arnoldi" << std::endl;
|
||||
// Arnoldi Arn (Dsq, FGrid, 1e-8);
|
||||
Arnoldi Arn (DLinOp, FGrid, 1e-8);
|
||||
testSchurFromHess<LatticeFermion>(Arn, src, 10, 6, 4);
|
||||
|
||||
Arnoldi Arn2 (DLinOp, FGrid, 1e-8);
|
||||
testSchurFromHess<LatticeFermion>(Arn2, src, 16, 12, 8);
|
||||
*/
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,498 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Runs the Krylov-Schur algorithm on a (pre-conditioned) domain-wall fermion operator
|
||||
to determine part of its spectrum.
|
||||
|
||||
Usage :
|
||||
$ ./Example_spec_kryschur <Nm> <Nk> <maxiter> <Nstop> <inFile> <outDir> <?rf>
|
||||
|
||||
Nm = Maximum size of approximation subspace.
|
||||
Nk = Size of truncation subspace
|
||||
maxiter = Maximum number of iterations.
|
||||
Nstop = Stop when Nstop eigenvalues have converged.
|
||||
inFile = Gauge configuration to read in.
|
||||
outDir = Directory to write output to.
|
||||
rf = (Optional) RitzFilter to sort with. Takes in any string in
|
||||
{EvalNormSmall, EvalNormLarge, EvalReSmall, EvalReLarge, EvalImSmall, EvalImLarge}
|
||||
|
||||
Output:
|
||||
${outDir}/evals.txt = Contains all eigenvalues. Each line is formatted as `$idx $eval $ritz`, where:
|
||||
- $idx is the index of the eigenvalue.
|
||||
- $eval is the eigenvalue, formated as "(re,im)".
|
||||
- $ritz is the Ritz estimate of the eigenvalue (deviation from being a true eigenvalue)
|
||||
${outDir}/evec${idx} = Eigenvector $idx written out in SCIDAC format (if LIME is enabled).
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./tests/Test_padded_cell.cc
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@bnl.edu>
|
||||
|
||||
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 */
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
|
||||
#include <Grid/parallelIO/IldgIOtypes.h>
|
||||
#include <Grid/parallelIO/IldgIO.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
namespace Grid {
|
||||
|
||||
struct LanczosParameters: Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass ,
|
||||
RealD, mstep ,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
Integer, ReadEvec,
|
||||
RealD, resid,
|
||||
RealD, ChebyLow,
|
||||
RealD, ChebyHigh,
|
||||
Integer, ChebyOrder)
|
||||
|
||||
LanczosParameters() {
|
||||
/////////////////////////////////
|
||||
}
|
||||
|
||||
template <class ReaderClass >
|
||||
LanczosParameters(Reader<ReaderClass> & TheReader){
|
||||
initialize(TheReader);
|
||||
}
|
||||
|
||||
template < class ReaderClass >
|
||||
void initialize(Reader<ReaderClass> &TheReader){
|
||||
read(TheReader, "HMC", *this);
|
||||
}
|
||||
|
||||
|
||||
void print_parameters() const {
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Trajectories : " << Trajectories << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Start trajectory : " << StartTrajectory << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Metropolis test (on/off): " << std::boolalpha << MetropolisTest << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Thermalization trajs : " << NoMetropolisUntil << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Starting type : " << StartingType << "\n";
|
||||
// MD.print_parameters();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#ifdef HAVE_LIME
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0); // Lexico
|
||||
WR.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the eigensystem of a Krylov Schur object to a directory.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* std::string path
|
||||
* Directory to write to.
|
||||
*/
|
||||
template <class Field>
|
||||
void writeEigensystem(KrylovSchur<Field> KS, std::string outDir) {
|
||||
int Nk = KS.getNk();
|
||||
std::cout << GridLogMessage << "Writing output to directory: " << outDir << std::endl;
|
||||
|
||||
// Write evals
|
||||
std::string evalPath = outDir + "/evals.txt";
|
||||
std::ofstream fEval;
|
||||
fEval.open(evalPath);
|
||||
Eigen::VectorXcd evals = KS.getEvals();
|
||||
std::vector<RealD> ritz = KS.getRitzEstimates();
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
// write eigenvalues and Ritz estimates
|
||||
fEval << i << " " << evals(i) << " " << ritz[i];
|
||||
if (i < Nk - 1) { fEval << "\n"; }
|
||||
}
|
||||
fEval.close();
|
||||
|
||||
// Write evecs (TODO: very heavy on storage costs! Don't write them all out)
|
||||
// std::vector<Field> evecs = KS.getEvecs();
|
||||
// for (int i = 0; i < Nk; i++) {
|
||||
// std::string fName = outDir + "/evec" + std::to_string(i);
|
||||
// writeFile(evecs[i], fName); // using method from Grid/HMC/ComputeWilsonFlow.cc
|
||||
// }
|
||||
}
|
||||
// Hermitize a DWF operator by squaring it
|
||||
template<class Matrix,class Field>
|
||||
class SquaredLinearOperator : public LinearOperatorBase<Field> {
|
||||
|
||||
public:
|
||||
Matrix &_Mat;
|
||||
|
||||
public:
|
||||
SquaredLinearOperator(Matrix &Mat): _Mat(Mat) {};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
// std::cout << "Op is overloaded as HermOp" << std::endl;
|
||||
HermOp(in, out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
HermOp(in, out);
|
||||
}
|
||||
void _Op (const Field &in, Field &out){
|
||||
// std::cout << "Op: M "<<std::endl;
|
||||
_Mat.M(in, out);
|
||||
}
|
||||
void _AdjOp (const Field &in, Field &out){
|
||||
// std::cout << "AdjOp: Mdag "<<std::endl;
|
||||
_Mat.Mdag(in, out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
// std::cout << "HermOp: Mdag M Mdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Op(in,tmp);
|
||||
_AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class PVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
public:
|
||||
PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(in,tmp);
|
||||
_Mat.Mdag(tmp,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
// _Mat.M(in,tmp);
|
||||
// _PV.Mdag(tmp,out);
|
||||
// _PV.M(out,tmp);
|
||||
// _Mat.Mdag(tmp,out);
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
// std::cout << "HermOp done "<<norm2(out)<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class ShiftedPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
RealD shift;
|
||||
public:
|
||||
ShiftedPVdagMLinearOperator(RealD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix, class Field>
|
||||
class ShiftedComplexPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
ComplexD shift;
|
||||
public:
|
||||
ShiftedComplexPVdagMLinearOperator(ComplexD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
|
||||
void resetShift(ComplexD newShift) {
|
||||
shift = newShift;
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
template<class Fobj,class CComplex,int nbasis>
|
||||
class MGPreconditioner : public LinearFunction< Lattice<Fobj> > {
|
||||
public:
|
||||
using LinearFunction<Lattice<Fobj> >::operator();
|
||||
|
||||
typedef Aggregation<Fobj,CComplex,nbasis> Aggregates;
|
||||
typedef typename Aggregation<Fobj,CComplex,nbasis>::FineField FineField;
|
||||
typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseVector CoarseVector;
|
||||
typedef typename Aggregation<Fobj,CComplex,nbasis>::CoarseMatrix CoarseMatrix;
|
||||
typedef LinearOperatorBase<FineField> FineOperator;
|
||||
typedef LinearFunction <FineField> FineSmoother;
|
||||
typedef LinearOperatorBase<CoarseVector> CoarseOperator;
|
||||
typedef LinearFunction <CoarseVector> CoarseSolver;
|
||||
Aggregates & _Aggregates;
|
||||
FineOperator & _FineOperator;
|
||||
FineSmoother & _PreSmoother;
|
||||
FineSmoother & _PostSmoother;
|
||||
CoarseOperator & _CoarseOperator;
|
||||
CoarseSolver & _CoarseSolve;
|
||||
|
||||
int level; void Level(int lv) {level = lv; };
|
||||
|
||||
MGPreconditioner(Aggregates &Agg,
|
||||
FineOperator &Fine,
|
||||
FineSmoother &PreSmoother,
|
||||
FineSmoother &PostSmoother,
|
||||
CoarseOperator &CoarseOperator_,
|
||||
CoarseSolver &CoarseSolve_)
|
||||
: _Aggregates(Agg),
|
||||
_FineOperator(Fine),
|
||||
_PreSmoother(PreSmoother),
|
||||
_PostSmoother(PostSmoother),
|
||||
_CoarseOperator(CoarseOperator_),
|
||||
_CoarseSolve(CoarseSolve_),
|
||||
level(1) { }
|
||||
|
||||
virtual void operator()(const FineField &in, FineField & out)
|
||||
{
|
||||
GridBase *CoarseGrid = _Aggregates.CoarseGrid;
|
||||
// auto CoarseGrid = _CoarseOperator.Grid();
|
||||
CoarseVector Csrc(CoarseGrid);
|
||||
CoarseVector Csol(CoarseGrid);
|
||||
FineField vec1(in.Grid());
|
||||
FineField vec2(in.Grid());
|
||||
|
||||
std::cout<<GridLogMessage << "Calling PreSmoother " <<std::endl;
|
||||
|
||||
// std::cout<<GridLogMessage << "Calling PreSmoother input residual "<<norm2(in) <<std::endl;
|
||||
double t;
|
||||
// Fine Smoother
|
||||
// out = in;
|
||||
out = Zero();
|
||||
t=-usecond();
|
||||
_PreSmoother(in,out);
|
||||
t+=usecond();
|
||||
|
||||
std::cout<<GridLogMessage << "PreSmoother took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Update the residual
|
||||
_FineOperator.Op(out,vec1); sub(vec1, in ,vec1);
|
||||
// std::cout<<GridLogMessage <<"Residual-1 now " <<norm2(vec1)<<std::endl;
|
||||
|
||||
// Fine to Coarse
|
||||
t=-usecond();
|
||||
_Aggregates.ProjectToSubspace (Csrc,vec1);
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "Project to coarse took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Coarse correction
|
||||
t=-usecond();
|
||||
Csol = Zero();
|
||||
_CoarseSolve(Csrc,Csol);
|
||||
//Csol=Zero();
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "Coarse solve took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Coarse to Fine
|
||||
t=-usecond();
|
||||
// _CoarseOperator.PromoteFromSubspace(_Aggregates,Csol,vec1);
|
||||
_Aggregates.PromoteFromSubspace(Csol,vec1);
|
||||
add(out,out,vec1);
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "Promote to this level took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
// Residual
|
||||
_FineOperator.Op(out,vec1); sub(vec1 ,in , vec1);
|
||||
// std::cout<<GridLogMessage <<"Residual-2 now " <<norm2(vec1)<<std::endl;
|
||||
|
||||
// Fine Smoother
|
||||
t=-usecond();
|
||||
// vec2=vec1;
|
||||
vec2=Zero();
|
||||
_PostSmoother(vec1,vec2);
|
||||
t+=usecond();
|
||||
std::cout<<GridLogMessage << "PostSmoother took "<< t/1000.0<< "ms" <<std::endl;
|
||||
|
||||
add( out,out,vec2);
|
||||
std::cout<<GridLogMessage << "Done " <<std::endl;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
// Usage : $ ./Example_spec_kryschur <Nm> <Nk> <maaxiter> <Nstop> <inFile> <outDir>
|
||||
std::string NmStr = argv[1];
|
||||
std::string NkStr = argv[2];
|
||||
std::string maxIterStr = argv[3];
|
||||
std::string NstopStr = argv[4];
|
||||
std::string file = argv[5];
|
||||
std::string outDir = argv[6];
|
||||
|
||||
RitzFilter RF;
|
||||
if (argc == 8) {
|
||||
std::string rf = argv[7];
|
||||
RF = selectRitzFilter(rf);
|
||||
} else {
|
||||
RF = EvalReSmall;
|
||||
}
|
||||
std::cout << "Sorting eigenvalues using " << rfToString(RF) << std::endl;
|
||||
|
||||
//const int Ls=16;
|
||||
const int Ls = 8;
|
||||
|
||||
// GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
|
||||
//std::vector<int> lat_size {16, 16, 16, 32};
|
||||
std::vector<int> lat_size {8, 8, 8, 8};
|
||||
std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size,
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
|
||||
std::vector<int> seeds4({1,2,3,4});
|
||||
std::vector<int> seeds5({5,6,7,8});
|
||||
GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5);
|
||||
GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4);
|
||||
|
||||
LatticeFermion src(FGrid); random(RNG5,src);
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
|
||||
FieldMetaData header;
|
||||
NerscIO::readConfiguration(Umu,header,file);
|
||||
|
||||
// RealD mass=0.01;
|
||||
RealD mass=0.001;
|
||||
RealD M5=1.8;
|
||||
|
||||
DomainWallFermionD Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5);
|
||||
DomainWallFermionD Dpv(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,1.0,M5);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
|
||||
typedef PVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> PVdagM_t;
|
||||
typedef ShiftedPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedPVdagM_t;
|
||||
typedef ShiftedComplexPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedComplexPVdagM_t;
|
||||
PVdagM_t PVdagM(Ddwf, Dpv);
|
||||
ShiftedPVdagM_t ShiftedPVdagM(0.1,Ddwf,Dpv);
|
||||
SquaredLinearOperator<DomainWallFermionD, LatticeFermionD> Dsq (Ddwf);
|
||||
NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
int Nm = std::stoi(NmStr);
|
||||
int Nk = std::stoi(NkStr);
|
||||
int maxIter = std::stoi(maxIterStr);
|
||||
int Nstop = std::stoi(NstopStr);
|
||||
|
||||
std::cout << GridLogMessage << "Runnning Krylov Schur. Nm = " << Nm << ", Nk = " << Nk << ", maxIter = " << maxIter
|
||||
<< ", Nstop = " << Nstop << std::endl;
|
||||
|
||||
KrylovSchur KrySchur (PVdagM, FGrid, 1e-8, RF); // use preconditioned PV^\dag D_{dwf}
|
||||
// KrylovSchur KrySchur (DLinOp, FGrid, 1e-8, RF); // use D_{dwf}
|
||||
KrySchur(src, maxIter, Nm, Nk, Nstop);
|
||||
|
||||
std::cout<<GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout<<GridLogMessage << "***************** RESULTS *****************" << std::endl;
|
||||
std::cout<<GridLogMessage << "*******************************************" << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Krylov Schur eigenvalues: " << std::endl << KrySchur.getEvals() << std::endl;
|
||||
|
||||
writeEigensystem(KrySchur, outDir);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Script for studying the Wilson eigenvectors resulting from the Krylov-Schur process.
|
||||
|
||||
Usage :
|
||||
$ ./Example_spec_kryschur <Nm> <Nk> <maxiter> <Nstop> <inFile> <outDir> <?rf>
|
||||
|
||||
Nm = Maximum size of approximation subspace.
|
||||
Nk = Size of truncation subspace
|
||||
maxiter = Maximum number of iterations.
|
||||
Nstop = Stop when Nstop eigenvalues have converged.
|
||||
inFile = Gauge configuration to read in.
|
||||
outDir = Directory to write output to.
|
||||
rf = (Optional) RitzFilter to sort with. Takes in any string in
|
||||
{EvalNormSmall, EvalNormLarge, EvalReSmall, EvalReLarge, EvalImSmall, EvalImLarge}
|
||||
|
||||
Output:
|
||||
${outDir}/evals.txt = Contains all eigenvalues. Each line is formatted as `$idx $eval $ritz`, where:
|
||||
- $idx is the index of the eigenvalue.
|
||||
- $eval is the eigenvalue, formated as "(re,im)".
|
||||
- $ritz is the Ritz estimate of the eigenvalue (deviation from being a true eigenvalue)
|
||||
${outDir}/evec${idx} = Eigenvector $idx written out in SCIDAC format (if LIME is enabled).
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./tests/Test_padded_cell.cc
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@bnl.edu>
|
||||
|
||||
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 */
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
|
||||
#include <Grid/parallelIO/IldgIOtypes.h>
|
||||
#include <Grid/parallelIO/IldgIO.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#ifdef HAVE_LIME
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0); // Lexico
|
||||
WR.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T> void readFile(T& out, std::string const fname){
|
||||
#ifdef HAVE_LIME
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Reads at: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
// Grid::ScidacReader SR(out.Grid()->IsBoss());
|
||||
Grid::ScidacReader SR;
|
||||
SR.open(fname);
|
||||
SR.readScidacFieldRecord(out, record);
|
||||
SR.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the eigensystem of a Krylov Schur object to a directory.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* std::string path
|
||||
* Directory to write to.
|
||||
*/
|
||||
template <class Field>
|
||||
void writeEigensystem(KrylovSchur<Field> KS, std::string outDir) {
|
||||
int Nk = KS.getNk();
|
||||
std::cout << GridLogMessage << "Writing output to directory: " << outDir << std::endl;
|
||||
|
||||
// Write evals
|
||||
std::string evalPath = outDir + "/evals.txt";
|
||||
std::ofstream fEval;
|
||||
fEval.open(evalPath);
|
||||
Eigen::VectorXcd evals = KS.getEvals();
|
||||
std::vector<RealD> ritz = KS.getRitzEstimates();
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
// write eigenvalues and Ritz estimates
|
||||
fEval << i << " " << evals(i) << " " << ritz[i];
|
||||
if (i < Nk - 1) { fEval << "\n"; }
|
||||
}
|
||||
fEval.close();
|
||||
|
||||
// Write evecs
|
||||
int Nevecs = Nk; // don't write all of them
|
||||
std::vector<Field> evecs = KS.getEvecs();
|
||||
for (int i = 0; i < Nevecs; i++) {
|
||||
std::string fName = outDir + "/evec" + std::to_string(i);
|
||||
writeFile(evecs[i], fName); // using method from Grid/HMC/ComputeWilsonFlow.cc
|
||||
}
|
||||
}
|
||||
|
||||
// Hermitize a DWF operator by squaring it
|
||||
template<class Matrix,class Field>
|
||||
class SquaredLinearOperator : public LinearOperatorBase<Field> {
|
||||
|
||||
public:
|
||||
Matrix &_Mat;
|
||||
|
||||
public:
|
||||
SquaredLinearOperator(Matrix &Mat): _Mat(Mat) {};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
// std::cout << "Op is overloaded as HermOp" << std::endl;
|
||||
HermOp(in, out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
HermOp(in, out);
|
||||
}
|
||||
void _Op (const Field &in, Field &out){
|
||||
// std::cout << "Op: M "<<std::endl;
|
||||
_Mat.M(in, out);
|
||||
}
|
||||
void _AdjOp (const Field &in, Field &out){
|
||||
// std::cout << "AdjOp: Mdag "<<std::endl;
|
||||
_Mat.Mdag(in, out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
// std::cout << "HermOp: Mdag M Mdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Op(in,tmp);
|
||||
_AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class PVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
public:
|
||||
PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(in,tmp);
|
||||
_Mat.Mdag(tmp,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
// _Mat.M(in,tmp);
|
||||
// _PV.Mdag(tmp,out);
|
||||
// _PV.M(out,tmp);
|
||||
// _Mat.Mdag(tmp,out);
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
// std::cout << "HermOp done "<<norm2(out)<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class ShiftedPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
RealD shift;
|
||||
public:
|
||||
ShiftedPVdagMLinearOperator(RealD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix, class Field>
|
||||
class ShiftedComplexPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
ComplexD shift;
|
||||
public:
|
||||
ShiftedComplexPVdagMLinearOperator(ComplexD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
|
||||
void resetShift(ComplexD newShift) {
|
||||
shift = newShift;
|
||||
}
|
||||
};
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
// Usage : $ ./Example_wilson_evecs ${inFile}
|
||||
std::string file = argv[1];
|
||||
|
||||
const int Ls=16;
|
||||
|
||||
// GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
|
||||
//std::vector<int> lat_size {16, 16, 16, 32};
|
||||
std::vector<int> lat_size {32, 32, 32, 32};
|
||||
std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size,
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
// GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
// GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
GridCartesian * FGrid = UGrid;
|
||||
GridRedBlackCartesian * FrbGrid = UrbGrid;
|
||||
|
||||
std::vector<int> seeds4({1,2,3,4});
|
||||
GridParallelRNG RNG4(UGrid);
|
||||
RNG4.SeedFixedIntegers(seeds4);
|
||||
|
||||
LatticeFermion src(FGrid); random(RNG4, src);
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
|
||||
FieldMetaData header;
|
||||
NerscIO::readConfiguration(Umu, header, file);
|
||||
|
||||
std::cout << GridLogMessage << "Loaded configuration" << std::endl;
|
||||
|
||||
// RealD mass = 0.01;
|
||||
RealD M5 = 1.8;
|
||||
|
||||
// Wilson mass
|
||||
RealD mass = -1.6;
|
||||
|
||||
std::cout << GridLogMessage << "masses specified" << std::endl;
|
||||
|
||||
std::vector<Complex> boundary = {1,1,1,-1};
|
||||
WilsonFermionD::ImplParams Params(boundary);
|
||||
|
||||
// DomainWallFermionD Ddwf(Umu, *FGrid, *FrbGrid, *UGrid, *UrbGrid, mass, M5);
|
||||
// NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
// WilsonFermionD Dwilson(Umu, *FGrid, *FrbGrid, mass);
|
||||
WilsonFermionD Dwilson(Umu, *UGrid, *UrbGrid, mass, Params);
|
||||
NonHermitianLinearOperator<WilsonFermionD, LatticeFermionD> DLinOp (Dwilson);
|
||||
|
||||
std::cout << GridLogMessage << "Dirac operator defined" << std::endl;
|
||||
|
||||
std::string eigenPath = "/home/poare/lqcd/multigrid/spectra/32cube-rho0.124-tau4/U_smr_3.000000/Nm72_Nk24_8111835.aurora-pbs-0001.hostmgmt.cm.aurora.alcf.anl.gov/";
|
||||
|
||||
std::cout << GridLogMessage << "Loading eigenvalues" << std::endl;
|
||||
std::ifstream evalFile(eigenPath + "evals.txt");
|
||||
std::string str;
|
||||
std::vector<ComplexD> evals;
|
||||
while (std::getline(evalFile, str)) {
|
||||
std::cout << GridLogMessage << "Reading line: " << str << std::endl;
|
||||
int i1 = str.find("(") + 1;
|
||||
int i2 = str.find(",") + 1;
|
||||
int i3 = str.find(")");
|
||||
std::cout << "i1,i2,i3 = " << i1 << "," << i2 << "," << i3 << std::endl;
|
||||
std::string reStr = str.substr(i1, i2 - i1);
|
||||
std::string imStr = str.substr(i2, i3 - i2);
|
||||
std::cout << GridLogMessage << "Parsed re = " << reStr << " and im = " << imStr << std::endl;
|
||||
// ComplexD z (std::stof(reStr), std::stof(imStr));
|
||||
ComplexD z (std::stod(reStr), std::stod(imStr));
|
||||
evals.push_back(z);
|
||||
}
|
||||
std::cout << GridLogMessage << "Eigenvalues: " << evals << std::endl;
|
||||
|
||||
int Nevecs = 24;
|
||||
std::vector<LatticeFermion> evecs;
|
||||
LatticeFermion evec (FGrid);
|
||||
for (int i = 0; i < Nevecs; i++) {
|
||||
std::string evecPath = eigenPath + "evec" + std::to_string(i);
|
||||
readFile(evec, evecPath);
|
||||
evecs.push_back(evec);
|
||||
}
|
||||
std::cout << GridLogMessage << "Evecs loaded" << std::endl;
|
||||
|
||||
// Compute < evec | D - \lambda | evec >
|
||||
std::cout << GridLogMessage << "Testing eigenvectors" << std::endl;
|
||||
LatticeFermion Devec (FGrid);
|
||||
ComplexD ritz;
|
||||
for (int i = 0; i < Nevecs; i++) {
|
||||
Devec = Zero();
|
||||
DLinOp.Op(evecs[i], Devec);
|
||||
ritz = std::sqrt(norm2(Devec - evals[i] * evecs[i]));
|
||||
std::cout << GridLogMessage << "i = " << i << ", || (D - lambda) |vi> || = " << ritz << std::endl;
|
||||
}
|
||||
// Eigen::MatrixXcd Dw_evecs;
|
||||
// Dw_evecs = Eigen::MatrixXcd::Zero(Nevecs, Nevecs);
|
||||
// for (int i = 0; i < Nevecs; i++) {
|
||||
// Linop.Op(evecs[i], Devec);
|
||||
// for (int j = 0; j < Nevecs; j++) {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Runs the Krylov-Schur algorithm on a Wilson fermion operator to determine part of its spectrum.
|
||||
|
||||
TODO rename this file: really is running the topology change jobs on Aurora.
|
||||
|
||||
Usage :
|
||||
$ ./Example_spec_kryschur <Nm> <Nk> <maxiter> <Nstop> <inFile> <outDir> <?rf>
|
||||
|
||||
Nm = Maximum size of approximation subspace.
|
||||
Nk = Size of truncation subspace
|
||||
maxiter = Maximum number of iterations.
|
||||
Nstop = Stop when Nstop eigenvalues have converged.
|
||||
inFile = Gauge configuration to read in.
|
||||
outDir = Directory to write output to.
|
||||
rf = (Optional) RitzFilter to sort with. Takes in any string in
|
||||
{EvalNormSmall, EvalNormLarge, EvalReSmall, EvalReLarge, EvalImSmall, EvalImLarge}
|
||||
|
||||
Output:
|
||||
${outDir}/evals.txt = Contains all eigenvalues. Each line is formatted as `$idx $eval $ritz`, where:
|
||||
- $idx is the index of the eigenvalue.
|
||||
- $eval is the eigenvalue, formated as "(re,im)".
|
||||
- $ritz is the Ritz estimate of the eigenvalue (deviation from being a true eigenvalue)
|
||||
${outDir}/evec${idx} = Eigenvector $idx written out in SCIDAC format (if LIME is enabled).
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./tests/Test_padded_cell.cc
|
||||
|
||||
Copyright (C) 2023
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Patrick Oare <poare@bnl.edu>
|
||||
|
||||
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 */
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
|
||||
#include <Grid/parallelIO/IldgIOtypes.h>
|
||||
#include <Grid/parallelIO/IldgIO.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#ifdef HAVE_LIME
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0); // Lexico
|
||||
WR.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the eigensystem of a Krylov Schur object to a directory.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* std::string path
|
||||
* Directory to write to.
|
||||
*/
|
||||
template <class Field>
|
||||
void writeEigensystem(KrylovSchur<Field> KS, std::string outDir) {
|
||||
int Nk = KS.getNk();
|
||||
std::cout << GridLogMessage << "Writing output to directory: " << outDir << std::endl;
|
||||
|
||||
// Write evals
|
||||
std::string evalPath = outDir + "/evals.txt";
|
||||
std::ofstream fEval;
|
||||
fEval.open(evalPath);
|
||||
Eigen::VectorXcd evals = KS.getEvals();
|
||||
std::vector<RealD> ritz = KS.getRitzEstimates();
|
||||
for (int i = 0; i < Nk; i++) {
|
||||
// write eigenvalues and Ritz estimates
|
||||
fEval << i << " " << evals(i) << " " << ritz[i];
|
||||
if (i < Nk - 1) { fEval << "\n"; }
|
||||
}
|
||||
fEval.close();
|
||||
|
||||
// Write evecs
|
||||
int Nevecs = Nk; // don't write all of them
|
||||
std::vector<Field> evecs = KS.getEvecs();
|
||||
for (int i = 0; i < Nevecs; i++) {
|
||||
std::string fName = outDir + "/evec" + std::to_string(i);
|
||||
writeFile(evecs[i], fName); // using method from Grid/HMC/ComputeWilsonFlow.cc
|
||||
}
|
||||
}
|
||||
|
||||
// Hermitize a DWF operator by squaring it
|
||||
template<class Matrix,class Field>
|
||||
class SquaredLinearOperator : public LinearOperatorBase<Field> {
|
||||
|
||||
public:
|
||||
Matrix &_Mat;
|
||||
|
||||
public:
|
||||
SquaredLinearOperator(Matrix &Mat): _Mat(Mat) {};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
// std::cout << "Op is overloaded as HermOp" << std::endl;
|
||||
HermOp(in, out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
HermOp(in, out);
|
||||
}
|
||||
void _Op (const Field &in, Field &out){
|
||||
// std::cout << "Op: M "<<std::endl;
|
||||
_Mat.M(in, out);
|
||||
}
|
||||
void _AdjOp (const Field &in, Field &out){
|
||||
// std::cout << "AdjOp: Mdag "<<std::endl;
|
||||
_Mat.Mdag(in, out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
// std::cout << "HermOp: Mdag M Mdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Op(in,tmp);
|
||||
_AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class PVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
public:
|
||||
PVdagMLinearOperator(Matrix &Mat,Matrix &PV): _Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(in,tmp);
|
||||
_Mat.Mdag(tmp,out);
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
// _Mat.M(in,tmp);
|
||||
// _PV.Mdag(tmp,out);
|
||||
// _PV.M(out,tmp);
|
||||
// _Mat.Mdag(tmp,out);
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
// std::cout << "HermOp done "<<norm2(out)<<std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class ShiftedPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
RealD shift;
|
||||
public:
|
||||
ShiftedPVdagMLinearOperator(RealD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Matrix, class Field>
|
||||
class ShiftedComplexPVdagMLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
Matrix &_PV;
|
||||
ComplexD shift;
|
||||
public:
|
||||
ShiftedComplexPVdagMLinearOperator(ComplexD _shift,Matrix &Mat,Matrix &PV): shift(_shift),_Mat(Mat),_PV(PV){};
|
||||
|
||||
void OpDiag (const Field &in, Field &out) { assert(0); }
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); };
|
||||
void Op (const Field &in, Field &out){
|
||||
std::cout << "Op: PVdag M "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_Mat.M(in,tmp);
|
||||
_PV.Mdag(tmp,out);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
std::cout << "AdjOp: Mdag PV "<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
_PV.M(tmp,out);
|
||||
_Mat.Mdag(in,tmp);
|
||||
out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ assert(0); }
|
||||
void HermOp(const Field &in, Field &out){
|
||||
std::cout << "HermOp: Mdag PV PVdag M"<<std::endl;
|
||||
Field tmp(in.Grid());
|
||||
Op(in,tmp);
|
||||
AdjOp(tmp,out);
|
||||
}
|
||||
|
||||
void resetShift(ComplexD newShift) {
|
||||
shift = newShift;
|
||||
}
|
||||
};
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
// Usage : $ ./Example_spec_kryschur <Nm> <Nk> <maaxiter> <Nstop> <inFile> <outDir>
|
||||
std::string NmStr = argv[1];
|
||||
std::string NkStr = argv[2];
|
||||
std::string maxIterStr = argv[3];
|
||||
std::string NstopStr = argv[4];
|
||||
std::string file = argv[5];
|
||||
std::string outDir = argv[6];
|
||||
|
||||
// RitzFilter RF;
|
||||
// if (argc == 8) {
|
||||
// std::string rf = argv[7];
|
||||
// RF = selectRitzFilter(rf);
|
||||
// } else {
|
||||
// RF = EvalReSmall;
|
||||
// }
|
||||
// RitzFilter RF;
|
||||
std::string rf = argv[7];
|
||||
RitzFilter RF = selectRitzFilter(rf);
|
||||
std::cout << "Sorting eigenvalues using " << rfToString(RF) << std::endl;
|
||||
|
||||
const int Ls=16;
|
||||
|
||||
// GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
|
||||
//std::vector<int> lat_size {16, 16, 16, 32};
|
||||
std::vector<int> lat_size {32, 32, 32, 32};
|
||||
std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size,
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
// GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
// GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
GridCartesian * FGrid = UGrid;
|
||||
GridRedBlackCartesian * FrbGrid = UrbGrid;
|
||||
|
||||
std::vector<int> seeds4({1,2,3,4});
|
||||
GridParallelRNG RNG4(UGrid);
|
||||
RNG4.SeedFixedIntegers(seeds4);
|
||||
|
||||
LatticeFermion src(FGrid); random(RNG4, src);
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
|
||||
FieldMetaData header;
|
||||
NerscIO::readConfiguration(Umu, header, file);
|
||||
|
||||
std::cout << GridLogMessage << "Loaded configuration" << std::endl;
|
||||
|
||||
// RealD mass = 0.01;
|
||||
RealD M5 = 1.8;
|
||||
|
||||
// Wilson mass
|
||||
RealD mass = -1.6;
|
||||
|
||||
std::cout << GridLogMessage << "masses specified" << std::endl;
|
||||
|
||||
std::vector<Complex> boundary = {1,1,1,-1};
|
||||
WilsonFermionD::ImplParams Params(boundary);
|
||||
|
||||
// DomainWallFermionD Ddwf(Umu, *FGrid, *FrbGrid, *UGrid, *UrbGrid, mass, M5);
|
||||
// NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
// WilsonFermionD Dwilson(Umu, *FGrid, *FrbGrid, mass);
|
||||
WilsonFermionD Dwilson(Umu, *UGrid, *UrbGrid, mass, Params);
|
||||
NonHermitianLinearOperator<WilsonFermionD, LatticeFermionD> DLinOp (Dwilson);
|
||||
|
||||
std::cout << GridLogMessage << "Dirac operator defined" << std::endl;
|
||||
|
||||
// Define PV^dag D (if we want)
|
||||
// DomainWallFermionD Dpv(Umu, *FGrid, *FrbGrid, *UGrid, *UrbGrid, 1.0, M5);
|
||||
// typedef PVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> PVdagM_t;
|
||||
// PVdagM_t PVdagM(Ddwf, Dpv);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
|
||||
// SquaredLinearOperator<WilsonFermionD, LatticeFermionD> Dsq (DWilson);
|
||||
// NonHermitianLinearOperator<WilsonFermionD, LatticeFermionD> DLinOp (DWilson);
|
||||
|
||||
int Nm = std::stoi(NmStr);
|
||||
int Nk = std::stoi(NkStr);
|
||||
int maxIter = std::stoi(maxIterStr);
|
||||
int Nstop = std::stoi(NstopStr);
|
||||
|
||||
std::cout << GridLogMessage << "Runnning Krylov Schur. Nm = " << Nm << ", Nk = " << Nk << ", maxIter = " << maxIter
|
||||
<< ", Nstop = " << Nstop << std::endl;
|
||||
|
||||
// KrylovSchur KrySchur (PVdagM, FGrid, 1e-8, RF); // use PV^\dag M
|
||||
KrylovSchur KrySchur (DLinOp, FGrid, 1e-8, RF); // use Ddwf
|
||||
KrySchur(src, maxIter, Nm, Nk, Nstop);
|
||||
|
||||
std::cout << GridLogMessage << "Checking eigensystem." << std::endl;
|
||||
KrySchur.checkRitzEstimate();
|
||||
|
||||
std::cout<<GridLogMessage << "*******************************************" << std::endl;
|
||||
std::cout<<GridLogMessage << "***************** RESULTS *****************" << std::endl;
|
||||
std::cout<<GridLogMessage << "*******************************************" << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Krylov Schur eigenvalues: " << std::endl << KrySchur.getEvals() << std::endl;
|
||||
|
||||
writeEigensystem(KrySchur, outDir);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<grid>
|
||||
<LanczosParameters>
|
||||
<mass>0</mass>
|
||||
<mstep>-0.025</mstep>
|
||||
<M5>1.8</M5>
|
||||
<Ls>48</Ls>
|
||||
<Nstop>100</Nstop>
|
||||
<Nk>100</Nk>
|
||||
<Np>100</Np>
|
||||
<ReadEvec>0</ReadEvec>
|
||||
<maxIter>1000</maxIter>
|
||||
<reorthog>1</reorthog>
|
||||
<Nblock>4</Nblock>
|
||||
<verify>0</verify>
|
||||
<shift>1.5</shift>
|
||||
<resid>1e-8</resid>
|
||||
<ChebyLow>1</ChebyLow>
|
||||
<ChebyHigh>100</ChebyHigh>
|
||||
<ChebyOrder>51</ChebyOrder>
|
||||
</LanczosParameters>
|
||||
</grid>
|
||||
@@ -0,0 +1,469 @@
|
||||
/*************************************************************************************
|
||||
|
||||
fft5d.cc — Fourier analysis of a time series of 4-D lattice scalar fields.
|
||||
|
||||
Designed for force-norm files from FTHMC (one RealD per site, SCIDAC or binary).
|
||||
Assembles a (4+1)-D structure [trajectory][t][z][y][x] using Grid lattice fields
|
||||
and performs three FFT analyses:
|
||||
|
||||
--fft spatial
|
||||
4-D spatial FFT using Grid's FFT class (MPI+GPU parallel via Cshift/cufft).
|
||||
Outputs shell-averaged P(|k|^2) averaged over trajectories, and a
|
||||
per-mode table P(kt,kz,ky,kx).
|
||||
|
||||
--fft traj
|
||||
1-D trajectory-axis FFT at each lattice site using FFTW3 locally at each
|
||||
MPI rank. Outputs site-averaged P(f_traj) and autocorrelation C(lag).
|
||||
Results are combined with GlobalSumVector.
|
||||
|
||||
--fft all
|
||||
Both of the above, plus a 2-D cross spectrum P(f_traj, |k_spatial|^2):
|
||||
spatial FFT per trajectory, then trajectory FFTW on the momentum-space series.
|
||||
|
||||
Normalisations:
|
||||
Spatial FFT (volume V): P_spatial(k) = |F(k)|^2 / V^2
|
||||
=> (1/V) * sum_k P(k) = site mean-square per trajectory
|
||||
Trajectory FFT (Ntraj): P_traj(f) = |F(f)|^2 / Ntraj^2
|
||||
=> sum_f P(f) = site mean-square over trajectories
|
||||
|
||||
Build: add to examples/Make.inc (see bottom of this file), then make.
|
||||
|
||||
Usage (follows Grid conventions):
|
||||
fft5d --grid Lx.Ly.Lz.Lt [--mpi Px.Py.Pz.Pt] \
|
||||
[--fft spatial|traj|all] [--format scidac|binary] \
|
||||
[--output PREFIX] file1 file2 ...
|
||||
|
||||
*************************************************************************************/
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
// Grid's FFT.h uses cufft on CUDA builds; for the trajectory axis we also need
|
||||
// CPU-side FFTW3 (already linked via -lfftw3 in GRID_LIBS).
|
||||
#include <fftw3.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Grid;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Global coordinate of a local site (osite, lane) on this MPI rank
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void globalCoor(int osite, int lane, GridCartesian* g, Coordinate& gc)
|
||||
{
|
||||
Coordinate oc, ic;
|
||||
Lexicographic::CoorFromIndex(oc, osite, g->_rdimensions);
|
||||
Lexicographic::CoorFromIndex(ic, lane, g->_simd_layout);
|
||||
for (int d = 0; d < Nd; d++)
|
||||
gc[d] = g->_processor_coor[d] * g->_ldimensions[d]
|
||||
+ oc[d] * g->_simd_layout[d] + ic[d];
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Shell-average a power LatticeRealD over |k|^2 bins (MPI-aware via GlobalSumVector)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void shellAverage(const LatticeRealD& power, GridCartesian* g,
|
||||
double norm, std::ostream& ofs)
|
||||
{
|
||||
const int Nsimd = vRealD::Nsimd();
|
||||
Coordinate fdims = g->_fdimensions;
|
||||
int maxk2 = 0;
|
||||
for (int d = 0; d < Nd; d++) { int h = fdims[d]/2; maxk2 += h*h; }
|
||||
|
||||
std::vector<RealD> psum(maxk2+1, 0.0), cnt(maxk2+1, 0.0);
|
||||
{
|
||||
auto pv = power.View(CpuRead);
|
||||
Coordinate gc(Nd);
|
||||
for (int os = 0; os < (int)g->oSites(); os++) {
|
||||
for (int lane = 0; lane < Nsimd; lane++) {
|
||||
globalCoor(os, lane, g, gc);
|
||||
int k2 = 0;
|
||||
for (int d = 0; d < Nd; d++) {
|
||||
int kd = std::min(gc[d], fdims[d] - gc[d]);
|
||||
k2 += kd*kd;
|
||||
}
|
||||
psum[k2] += (RealD)extractLane(lane, pv[os]);
|
||||
cnt [k2] += 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
g->GlobalSumVector(psum.data(), (int)psum.size());
|
||||
g->GlobalSumVector(cnt .data(), (int)cnt .size());
|
||||
|
||||
for (int k2 = 0; k2 <= maxk2; k2++)
|
||||
if (cnt[k2] > 0)
|
||||
ofs << k2 << " " << std::sqrt((double)k2) << " "
|
||||
<< (psum[k2] / cnt[k2]) * norm << "\n";
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Convert LatticeRealD → LatticeComplexD (zero imaginary part)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static LatticeComplexD toComplex(const LatticeRealD& r)
|
||||
{
|
||||
std::vector<RealD> lr;
|
||||
unvectorizeToLexOrdArray(lr, r);
|
||||
std::vector<ComplexD> lc(lr.size());
|
||||
for (size_t i = 0; i < lr.size(); i++) lc[i] = ComplexD(lr[i], 0.0);
|
||||
LatticeComplexD c(r.Grid());
|
||||
vectorizeFromLexOrdArray(lc, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// File readers
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static LatticeComplexD readScidac(const std::string& fname, GridCartesian* g)
|
||||
{
|
||||
LatticeRealD field(g);
|
||||
emptyUserRecord rec;
|
||||
ScidacReader RD;
|
||||
RD.open(fname);
|
||||
RD.readScidacFieldRecord(field, rec);
|
||||
RD.close();
|
||||
return toComplex(field);
|
||||
}
|
||||
|
||||
static LatticeComplexD readBinary(const std::string& fname, GridCartesian* g)
|
||||
{
|
||||
// Raw IEEE doubles in lex order [x][y][z][t] written serially.
|
||||
// Rank 0 reads the file, broadcasts to all ranks via GlobalSumVector.
|
||||
Coordinate fdims = g->_fdimensions;
|
||||
long vol4 = 1; for (int d = 0; d < Nd; d++) vol4 *= fdims[d];
|
||||
|
||||
std::vector<RealD> buf(vol4, 0.0);
|
||||
if (g->IsBoss()) {
|
||||
std::ifstream f(fname, std::ios::binary);
|
||||
if (!f) throw std::runtime_error("Cannot open: " + fname);
|
||||
f.read(reinterpret_cast<char*>(buf.data()), vol4 * sizeof(RealD));
|
||||
}
|
||||
g->GlobalSumVector(buf.data(), (int)vol4); // broadcast from rank 0
|
||||
|
||||
LatticeRealD field(g);
|
||||
Coordinate ldims = g->_ldimensions, pcoor = g->_processor_coor;
|
||||
for (long ls = 0; ls < g->lSites(); ls++) {
|
||||
Coordinate lc;
|
||||
g->LocalIndexToLocalCoor(ls, lc);
|
||||
long glex = 0, stride = 1;
|
||||
for (int d = 0; d < Nd; d++) {
|
||||
glex += (pcoor[d]*ldims[d] + lc[d]) * stride;
|
||||
stride *= fdims[d];
|
||||
}
|
||||
pokeLocalSite(buf[glex], field, lc);
|
||||
}
|
||||
return toComplex(field);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Load all files into a trajectory vector
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static std::vector<LatticeComplexD>
|
||||
loadFiles(const std::vector<std::string>& files, GridCartesian* g,
|
||||
const std::string& fmt)
|
||||
{
|
||||
std::vector<LatticeComplexD> traj;
|
||||
traj.reserve(files.size());
|
||||
for (int n = 0; n < (int)files.size(); n++) {
|
||||
std::cout << GridLogMessage << "[" << n << "] reading " << files[n] << "\n";
|
||||
if (fmt == "scidac") traj.push_back(readScidac(files[n], g));
|
||||
else traj.push_back(readBinary (files[n], g));
|
||||
}
|
||||
return traj;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Spatial FFT analysis
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void analyzeSpatial(const std::vector<LatticeComplexD>& traj,
|
||||
GridCartesian* g, const std::string& pfx)
|
||||
{
|
||||
int Ntraj = (int)traj.size();
|
||||
long vol4 = 1; for (int d = 0; d < Nd; d++) vol4 *= g->_fdimensions[d];
|
||||
|
||||
FFT theFFT(g);
|
||||
LatticeRealD pavg(g); pavg = Zero();
|
||||
for (int n = 0; n < Ntraj; n++) {
|
||||
LatticeComplexD fk(g);
|
||||
theFFT.FFT_all_dim(fk, traj[n], FFT::forward);
|
||||
// Evaluate product before applying real() — real() is not defined for
|
||||
// unevaluated LatticeBinaryExpression.
|
||||
LatticeComplexD fk_sq(g); fk_sq = conjugate(fk) * fk;
|
||||
std::vector<ComplexD> lc; unvectorizeToLexOrdArray(lc, fk_sq);
|
||||
std::vector<RealD> lr(lc.size());
|
||||
for (size_t i = 0; i < lc.size(); i++) lr[i] = lc[i].real();
|
||||
LatticeRealD pk(g); vectorizeFromLexOrdArray(lr, pk);
|
||||
pavg += pk;
|
||||
}
|
||||
pavg *= (1.0 / Ntraj);
|
||||
|
||||
// Shell-averaged spectrum
|
||||
{
|
||||
std::ofstream fs(pfx + "_spatial_shell.dat");
|
||||
fs << "# k2 |k| P_shell_avg (P = |F|^2 / V^2 / shell_count)\n";
|
||||
shellAverage(pavg, g, 1.0 / ((double)vol4 * vol4), fs);
|
||||
std::cout << GridLogMessage << "Written: " << pfx << "_spatial_shell.dat\n";
|
||||
}
|
||||
|
||||
// Per-mode table (rank 0 only, via peekSite)
|
||||
if (g->IsBoss()) {
|
||||
std::ofstream fm(pfx + "_spatial_modes.dat");
|
||||
fm << "# kt kz ky kx k2 |k| P_traj_avg\n";
|
||||
Coordinate fdims = g->_fdimensions, site(Nd);
|
||||
double norm = 1.0 / ((double)vol4 * vol4);
|
||||
for (site[3]=0; site[3]<fdims[3]; site[3]++) { int kt=std::min(site[3],fdims[3]-site[3]);
|
||||
for (site[2]=0; site[2]<fdims[2]; site[2]++) { int kz=std::min(site[2],fdims[2]-site[2]);
|
||||
for (site[1]=0; site[1]<fdims[1]; site[1]++) { int ky=std::min(site[1],fdims[1]-site[1]);
|
||||
for (site[0]=0; site[0]<fdims[0]; site[0]++) { int kx=std::min(site[0],fdims[0]-site[0]);
|
||||
RealD p; peekSite(p, pavg, site);
|
||||
int k2 = kt*kt+kz*kz+ky*ky+kx*kx;
|
||||
fm << kt<<" "<<kz<<" "<<ky<<" "<<kx<<" "<<k2<<" "
|
||||
<<std::sqrt((double)k2)<<" "<<p*norm<<"\n";
|
||||
}}}}
|
||||
std::cout << GridLogMessage << "Written: " << pfx << "_spatial_modes.dat\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Local FFTW trajectory FFT: each rank FFTs its own local sites independently
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void trajFFT(const std::vector<LatticeComplexD>& in,
|
||||
std::vector<LatticeComplexD>& out)
|
||||
{
|
||||
int Ntraj = (int)in.size();
|
||||
GridBase* g = in[0].Grid();
|
||||
long lsites = g->lSites();
|
||||
|
||||
// fftw_complex is double[2] — std::vector<double[2]> is not supported by nvcc.
|
||||
// Use std::vector<double> with 2x size and reinterpret_cast.
|
||||
std::vector<double> ibuf((long)Ntraj * lsites * 2, 0.0);
|
||||
for (int n = 0; n < Ntraj; n++) {
|
||||
std::vector<ComplexD> lc;
|
||||
unvectorizeToLexOrdArray(lc, in[n]);
|
||||
for (long s = 0; s < lsites; s++) {
|
||||
ibuf[((long)n*lsites + s)*2 + 0] = lc[s].real();
|
||||
ibuf[((long)n*lsites + s)*2 + 1] = lc[s].imag();
|
||||
}
|
||||
}
|
||||
|
||||
// lsites transforms of length Ntraj, stride=lsites, dist=1
|
||||
std::vector<double> obuf((long)Ntraj * lsites * 2, 0.0);
|
||||
fftw_complex* iptr = reinterpret_cast<fftw_complex*>(ibuf.data());
|
||||
fftw_complex* optr = reinterpret_cast<fftw_complex*>(obuf.data());
|
||||
int n1[1] = {Ntraj};
|
||||
fftw_plan p = fftw_plan_many_dft(
|
||||
1, n1, (int)lsites,
|
||||
iptr, nullptr, (int)lsites, 1,
|
||||
optr, nullptr, (int)lsites, 1,
|
||||
FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
fftw_execute(p);
|
||||
fftw_destroy_plan(p);
|
||||
|
||||
// vector::assign triggers _M_fill_assign which needs a default constructor;
|
||||
// Lattice has none. Use explicit emplace_back instead.
|
||||
out.clear(); out.reserve(Ntraj);
|
||||
for (int k = 0; k < Ntraj; k++) out.emplace_back(g);
|
||||
for (int k = 0; k < Ntraj; k++) {
|
||||
std::vector<ComplexD> lc(lsites);
|
||||
for (long s = 0; s < lsites; s++)
|
||||
lc[s] = ComplexD(obuf[((long)k*lsites+s)*2], obuf[((long)k*lsites+s)*2+1]);
|
||||
vectorizeFromLexOrdArray(lc, out[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Trajectory-axis FFT analysis
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void analyzeTraj(const std::vector<LatticeComplexD>& traj,
|
||||
GridCartesian* g, const std::string& pfx)
|
||||
{
|
||||
int Ntraj = (int)traj.size();
|
||||
int Nf = Ntraj / 2 + 1;
|
||||
long vol4 = 1; for (int d = 0; d < Nd; d++) vol4 *= g->_fdimensions[d];
|
||||
|
||||
std::vector<LatticeComplexD> ftraj;
|
||||
trajFFT(traj, ftraj);
|
||||
|
||||
// P(k) = (1/vol4) * sum_sites |F_traj(k)|^2 / Ntraj^2
|
||||
std::vector<RealD> Pavg(Ntraj, 0.0);
|
||||
for (int k = 0; k < Ntraj; k++) {
|
||||
LatticeComplexD tmp(g); tmp = conjugate(ftraj[k]) * ftraj[k];
|
||||
std::vector<ComplexD> lc; unvectorizeToLexOrdArray(lc, tmp);
|
||||
std::vector<RealD> lr(lc.size());
|
||||
for (size_t i = 0; i < lc.size(); i++) lr[i] = lc[i].real();
|
||||
LatticeRealD pk(g); vectorizeFromLexOrdArray(lr, pk);
|
||||
RealD s = 0.0;
|
||||
for (long ls = 0; ls < g->lSites(); ls++) {
|
||||
Coordinate lc; g->LocalIndexToLocalCoor(ls, lc);
|
||||
RealD v; peekLocalSite(v, pk, lc);
|
||||
s += v;
|
||||
}
|
||||
g->GlobalSum(s);
|
||||
Pavg[k] = s / ((double)vol4 * Ntraj * Ntraj);
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream f(pfx + "_traj_power.dat");
|
||||
f << "# k freq P_avg (sum_k P = site mean-square)\n";
|
||||
for (int k = 0; k < Nf; k++)
|
||||
f << k << " " << (double)k/Ntraj << " " << Pavg[k] << "\n";
|
||||
std::cout << GridLogMessage << "Written: " << pfx << "_traj_power.dat\n";
|
||||
}
|
||||
|
||||
// Autocorrelation via IFFT of the power spectrum
|
||||
std::vector<double> Pc_buf(Nf * 2, 0.0);
|
||||
for (int k = 0; k < Nf; k++) { Pc_buf[k*2] = Pavg[k]; Pc_buf[k*2+1] = 0.0; }
|
||||
fftw_complex* Pc = reinterpret_cast<fftw_complex*>(Pc_buf.data());
|
||||
std::vector<double> acorr(Ntraj, 0.0);
|
||||
fftw_plan ip = fftw_plan_dft_c2r(1, &Ntraj, Pc, acorr.data(), FFTW_ESTIMATE);
|
||||
fftw_execute(ip); fftw_destroy_plan(ip);
|
||||
double c0 = acorr[0] / Ntraj;
|
||||
{
|
||||
std::ofstream f(pfx + "_traj_autocorr.dat");
|
||||
f << "# lag C(lag) C(lag)/C(0)\n";
|
||||
for (int lag = 0; lag < Ntraj/2; lag++) {
|
||||
double c = acorr[lag] / Ntraj;
|
||||
f << lag << " " << c << " " << (c0 > 0 ? c/c0 : 0.0) << "\n";
|
||||
}
|
||||
std::cout << GridLogMessage << "Written: " << pfx << "_traj_autocorr.dat\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Full 5-D analysis: spatial FFT → trajectory FFT → 2-D cross spectrum
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void analyzeAll5D(const std::vector<LatticeComplexD>& traj,
|
||||
GridCartesian* g, const std::string& pfx)
|
||||
{
|
||||
int Ntraj = (int)traj.size();
|
||||
int Nf = Ntraj / 2 + 1;
|
||||
long vol4 = 1; for (int d = 0; d < Nd; d++) vol4 *= g->_fdimensions[d];
|
||||
Coordinate fdims = g->_fdimensions;
|
||||
|
||||
// Step 1: spatial FFT for each trajectory
|
||||
FFT theFFT(g);
|
||||
std::vector<LatticeComplexD> sfft(Ntraj, LatticeComplexD(g));
|
||||
for (int n = 0; n < Ntraj; n++)
|
||||
theFFT.FFT_all_dim(sfft[n], traj[n], FFT::forward);
|
||||
|
||||
// Step 2: trajectory FFTW on the momentum-space series
|
||||
std::vector<LatticeComplexD> tfft;
|
||||
trajFFT(sfft, tfft);
|
||||
|
||||
// Step 3: P(f_traj, |k_spatial|^2) shell-averaged
|
||||
int maxk2 = 0;
|
||||
for (int d = 0; d < Nd; d++) { int h = fdims[d]/2; maxk2 += h*h; }
|
||||
|
||||
long nb = (long)Nf * (maxk2+1);
|
||||
std::vector<RealD> p2d(nb, 0.0), cnt2d(nb, 0.0);
|
||||
const int Nsimd = vComplexD::Nsimd();
|
||||
double norm = 1.0 / ((double)Ntraj * Ntraj * vol4 * vol4);
|
||||
|
||||
for (int kf = 0; kf < Nf; kf++) {
|
||||
LatticeComplexD tmp(g); tmp = conjugate(tfft[kf]) * tfft[kf];
|
||||
std::vector<ComplexD> lc2; unvectorizeToLexOrdArray(lc2, tmp);
|
||||
std::vector<RealD> lr2(lc2.size());
|
||||
for (size_t i = 0; i < lc2.size(); i++) lr2[i] = lc2[i].real();
|
||||
LatticeRealD pk(g); vectorizeFromLexOrdArray(lr2, pk);
|
||||
auto pv = pk.View(CpuRead);
|
||||
Coordinate gc(Nd);
|
||||
for (int os = 0; os < (int)g->oSites(); os++) {
|
||||
for (int lane = 0; lane < Nsimd; lane++) {
|
||||
globalCoor(os, lane, g, gc);
|
||||
int k2 = 0;
|
||||
for (int d = 0; d < Nd; d++) {
|
||||
int kd = std::min(gc[d], fdims[d] - gc[d]);
|
||||
k2 += kd*kd;
|
||||
}
|
||||
long b = (long)kf*(maxk2+1) + k2;
|
||||
p2d [b] += (RealD)extractLane(lane, pv[os]);
|
||||
cnt2d[b] += 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
g->GlobalSumVector(p2d .data(), (int)nb);
|
||||
g->GlobalSumVector(cnt2d.data(), (int)nb);
|
||||
|
||||
if (g->IsBoss()) {
|
||||
std::ofstream f(pfx + "_5d_cross.dat");
|
||||
f << "# k_traj freq_traj k2_spatial |k_spatial| P_avg\n";
|
||||
for (int kf = 0; kf < Nf; kf++) {
|
||||
double freq = (double)kf / Ntraj;
|
||||
for (int k2 = 0; k2 <= maxk2; k2++) {
|
||||
long b = (long)kf*(maxk2+1) + k2;
|
||||
if (cnt2d[b] > 0)
|
||||
f << kf << " " << freq << " " << k2 << " "
|
||||
<< std::sqrt((double)k2) << " "
|
||||
<< (p2d[b]/cnt2d[b]) * norm << "\n";
|
||||
}
|
||||
f << "\n"; // blank line between kf slices for gnuplot pm3d
|
||||
}
|
||||
std::cout << GridLogMessage << "Written: " << pfx << "_5d_cross.dat\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Main
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
static void usage(const char* prog)
|
||||
{
|
||||
std::cerr
|
||||
<< "Usage: " << prog << " --grid Lx.Ly.Lz.Lt [--mpi Px.Py.Pz.Pt]\n"
|
||||
<< " [--fft spatial|traj|all] [--format scidac|binary]\n"
|
||||
<< " [--output PREFIX] file1 [file2 ...]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv); // consumes --grid, --mpi, etc.
|
||||
|
||||
std::string fftMode = "all";
|
||||
std::string fmt = "scidac";
|
||||
std::string pfx = "fft5d";
|
||||
std::vector<std::string> files;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string a = argv[i];
|
||||
if (a == "--fft" && i+1 < argc) fftMode = argv[++i];
|
||||
else if (a == "--format" && i+1 < argc) fmt = argv[++i];
|
||||
else if (a == "--output" && i+1 < argc) pfx = argv[++i];
|
||||
else if (!a.empty() && a[0] != '-') files.push_back(a);
|
||||
// unknown --flags skipped (may be Grid flags already consumed)
|
||||
}
|
||||
|
||||
if (files.empty()) usage(argv[0]);
|
||||
|
||||
GridCartesian* grid = SpaceTimeGrid::makeFourDimGrid(
|
||||
GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd, vComplexD::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
|
||||
Coordinate latt = GridDefaultLatt();
|
||||
std::cout << GridLogMessage << "Lattice : "
|
||||
<< latt[0]<<"x"<<latt[1]<<"x"<<latt[2]<<"x"<<latt[3] << "\n"
|
||||
<< GridLogMessage << "Ntraj : " << files.size() << "\n"
|
||||
<< GridLogMessage << "FFT : " << fftMode << "\n"
|
||||
<< GridLogMessage << "Format : " << fmt << "\n"
|
||||
<< GridLogMessage << "Output : " << pfx << "\n";
|
||||
|
||||
auto traj = loadFiles(files, grid, fmt);
|
||||
|
||||
if (fftMode == "spatial" || fftMode == "all") analyzeSpatial(traj, grid, pfx);
|
||||
if (fftMode == "traj" || fftMode == "all") analyzeTraj (traj, grid, pfx);
|
||||
if (fftMode == "all") analyzeAll5D (traj, grid, pfx);
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Add to examples/Make.inc to build:
|
||||
|
||||
bin_PROGRAMS += ... fft5d
|
||||
fft5d_SOURCES = fft5d.cc
|
||||
fft5d_LDADD = $(top_builddir)/Grid/libGrid.a
|
||||
*/
|
||||
@@ -0,0 +1,271 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: tests/core/Test_plaquette_stats.cc
|
||||
|
||||
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 */
|
||||
|
||||
/**
|
||||
* Test_plaquette_stats
|
||||
*
|
||||
* Measure every plaquette Re Tr[U_mu(x) U_nu(x+mu) U_mu†(x+nu) U_nu†(x)] / Nc
|
||||
* across all sites and all (mu,nu) planes and report max, min, and average.
|
||||
*
|
||||
* Usage:
|
||||
* ./Test_plaquette_stats [Grid options] [--file <nersc_config>] [--hot]
|
||||
* [--threshold <val>]
|
||||
*
|
||||
* --file <path> Read gauge field from NERSC-format file
|
||||
* --hot Use random (hot) SU(3) start (default: cold/unit start)
|
||||
* --threshold <val> Print coordinates of every plaquette with Re Tr/Nc < val
|
||||
*
|
||||
* Grid size defaults to 8^3 x 16; override with --grid (e.g. --grid 4.4.4.8).
|
||||
*/
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <limits>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
template<class T>
|
||||
static void writeFile(T& in, const std::string& fname)
|
||||
{
|
||||
#ifdef HAVE_LIME
|
||||
std::cout << GridLogMessage << "Writing: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in, record, 0);
|
||||
WR.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the plane label string for output
|
||||
static std::string planeName(int mu, int nu)
|
||||
{
|
||||
const char dirs[] = "xyzt";
|
||||
std::string s;
|
||||
s += dirs[mu];
|
||||
s += dirs[nu];
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv);
|
||||
|
||||
// ---- Lattice geometry ----
|
||||
Coordinate latt_size = GridDefaultLatt();
|
||||
if (latt_size.size() == 0) {
|
||||
latt_size = Coordinate(std::vector<int>{8, 8, 8, 16});
|
||||
}
|
||||
Coordinate simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd());
|
||||
Coordinate mpi_layout = GridDefaultMpi();
|
||||
|
||||
GridCartesian grid(latt_size, simd_layout, mpi_layout);
|
||||
|
||||
std::cout << GridLogMessage << "Lattice: ";
|
||||
for (int d = 0; d < Nd; d++)
|
||||
std::cout << latt_size[d] << (d < Nd-1 ? "x" : "\n");
|
||||
|
||||
// ---- Gauge field ----
|
||||
LatticeGaugeField Umu(&grid);
|
||||
|
||||
// Check for --file argument
|
||||
std::string config_file = "";
|
||||
for (int i = 1; i < argc - 1; i++) {
|
||||
if (std::string(argv[i]) == "--file") {
|
||||
config_file = argv[i+1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool doHot = false;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (std::string(argv[i]) == "--hot") { doHot = true; break; }
|
||||
}
|
||||
|
||||
double threshold = std::numeric_limits<double>::quiet_NaN();
|
||||
bool doThresh = false;
|
||||
for (int i = 1; i < argc - 1; i++) {
|
||||
if (std::string(argv[i]) == "--threshold") {
|
||||
threshold = std::stod(argv[i+1]);
|
||||
doThresh = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config_file.empty()) {
|
||||
std::cout << GridLogMessage << "Reading gauge field from " << config_file << std::endl;
|
||||
FieldMetaData header;
|
||||
NerscIO::readConfiguration(Umu, header, config_file);
|
||||
} else {
|
||||
std::vector<int> seeds({1, 2, 3, 4});
|
||||
GridParallelRNG pRNG(&grid);
|
||||
pRNG.SeedFixedIntegers(seeds);
|
||||
if (doHot) {
|
||||
std::cout << GridLogMessage << "Generating hot (random SU(3)) start" << std::endl;
|
||||
SU<Nc>::HotConfiguration(pRNG, Umu);
|
||||
} else {
|
||||
std::cout << GridLogMessage << "Using cold (unit) gauge start" << std::endl;
|
||||
SU<Nc>::ColdConfiguration(pRNG, Umu);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Extract link matrices ----
|
||||
std::vector<LatticeColourMatrix> U(Nd, &grid);
|
||||
for (int mu = 0; mu < Nd; mu++)
|
||||
U[mu] = PeekIndex<LorentzIndex>(Umu, mu);
|
||||
|
||||
// ---- Per-plane plaquette statistics ----
|
||||
//
|
||||
// For each (mu, nu) plane (mu > nu) compute
|
||||
// P_munu(x) = Re Tr[plaquette] / Nc
|
||||
// then report max, min, mean over all sites.
|
||||
//
|
||||
// WilsonLoops::traceDirPlaquette gives Tr[U_mu U_nu U_mu† U_nu†] (complex).
|
||||
|
||||
double vol = grid.gSites();
|
||||
|
||||
// Accumulate site-average plaquette (sum over planes / Nplanes / Nc)
|
||||
LatticeComplex plaq_all(&grid);
|
||||
plaq_all = Zero();
|
||||
int Nplanes = 0;
|
||||
|
||||
// Per-site running sum over planes (Re Tr/Nc, normalised per plane)
|
||||
LatticeRealD plaq_sum(&grid);
|
||||
plaq_sum = Zero();
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== Per-plane plaquette statistics ========" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< std::setw(6) << "plane"
|
||||
<< std::setw(20) << "max"
|
||||
<< std::setw(20) << "min"
|
||||
<< std::setw(20) << "average"
|
||||
<< std::endl;
|
||||
|
||||
for (int mu = 1; mu < Nd; mu++) {
|
||||
for (int nu = 0; nu < mu; nu++) {
|
||||
|
||||
// Per-site trace of plaquette in (mu,nu) plane
|
||||
LatticeComplex sitePlaq(&grid);
|
||||
ColourWilsonLoops::traceDirPlaquette(sitePlaq, U, mu, nu);
|
||||
|
||||
plaq_all = plaq_all + sitePlaq;
|
||||
Nplanes++;
|
||||
|
||||
// --- global average via sum() ---
|
||||
TComplex Tsum = sum(sitePlaq);
|
||||
ComplexD csum = TensorRemove(Tsum);
|
||||
RealD avg = csum.real() / vol / Nc;
|
||||
|
||||
// --- global max and min via unvectorize + GlobalMax/GlobalMin ---
|
||||
std::vector<TComplex> sv;
|
||||
unvectorizeToLexOrdArray(sv, sitePlaq);
|
||||
|
||||
RealD local_max = -1e38, local_min = 1e38;
|
||||
for (auto& tc : sv) {
|
||||
RealD r = TensorRemove(tc).real() / Nc;
|
||||
if (r > local_max) local_max = r;
|
||||
if (r < local_min) local_min = r;
|
||||
}
|
||||
|
||||
// Reduce across MPI ranks (no GlobalMin; negate to use GlobalMax)
|
||||
grid.GlobalMax(local_max);
|
||||
local_min = -local_min;
|
||||
grid.GlobalMax(local_min);
|
||||
local_min = -local_min;
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< std::setw(6) << planeName(mu, nu)
|
||||
<< std::setw(20) << std::setprecision(10) << local_max
|
||||
<< std::setw(20) << std::setprecision(10) << local_min
|
||||
<< std::setw(20) << std::setprecision(10) << avg
|
||||
<< std::endl;
|
||||
|
||||
if (doThresh) {
|
||||
std::vector<RealD> vr(sv.size(), 0.0);
|
||||
for (int s = 0; s < (int)sv.size(); s++) {
|
||||
RealD val = TensorRemove(sv[s]).real() / Nc;
|
||||
if (val < threshold) {
|
||||
Coordinate lc(Nd), gc(Nd);
|
||||
Lexicographic::CoorFromIndex(lc, s, grid._ldimensions);
|
||||
for (int d = 0; d < Nd; d++)
|
||||
gc[d] = grid._processor_coor[d] * grid._ldimensions[d] + lc[d];
|
||||
std::cerr << "BELOW_THRESHOLD plane=" << planeName(mu, nu)
|
||||
// << " global=(" << grid._processor_coor[0] << "," << grid._processor_coor[1] << "," << grid._processor_coor[2] << "," << grid._processor_coor[3] << ")"
|
||||
// << " dim=(" << grid._ldimensions[0] << "," << grid._ldimensions[1] << "," << grid._ldimensions[2] << "," << grid._ldimensions[3] << ")"
|
||||
<< " site=(" << gc[0] << "," << gc[1] << "," << gc[2] << "," << gc[3] << ")"
|
||||
<< " P=" << std::setprecision(10) << val << std::endl;
|
||||
vr[s] = val;
|
||||
}
|
||||
}
|
||||
LatticeRealD plaqRe(&grid);
|
||||
vectorizeFromLexOrdArray(vr, plaqRe);
|
||||
plaq_sum = plaq_sum + plaqRe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write per-site sum of below-threshold plaquette values over all planes
|
||||
if (doThresh)
|
||||
writeFile(plaq_sum, "./plaq_sum");
|
||||
|
||||
// ---- Overall (averaged over all planes) statistics ----
|
||||
// plaq_all = sum of Tr[...] over all 6 (mu,nu) planes
|
||||
// Normalise to Re Tr / Nc per plane
|
||||
plaq_all = plaq_all * (1.0 / Nc / Nplanes);
|
||||
|
||||
TComplex Tsum_all = sum(plaq_all);
|
||||
RealD avg_all = TensorRemove(Tsum_all).real() / vol;
|
||||
|
||||
std::vector<TComplex> sv_all;
|
||||
unvectorizeToLexOrdArray(sv_all, plaq_all);
|
||||
|
||||
RealD max_all = -1e38, min_all = 1e38;
|
||||
for (auto& tc : sv_all) {
|
||||
RealD r = TensorRemove(tc).real();
|
||||
if (r > max_all) max_all = r;
|
||||
if (r < min_all) min_all = r;
|
||||
}
|
||||
grid.GlobalMax(max_all);
|
||||
min_all = -min_all;
|
||||
grid.GlobalMax(min_all);
|
||||
min_all = -min_all;
|
||||
|
||||
// Cross-check with built-in avgPlaquette
|
||||
RealD avg_builtin = ColourWilsonLoops::avgPlaquette(Umu);
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "======== Overall plaquette statistics (all planes) ========" << std::endl;
|
||||
std::cout << GridLogMessage << " max = " << max_all << std::endl;
|
||||
std::cout << GridLogMessage << " min = " << min_all << std::endl;
|
||||
std::cout << GridLogMessage << " average = " << avg_all << std::endl;
|
||||
std::cout << GridLogMessage << " avgPlaquette (builtin check) = " << avg_builtin << std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
<grid>
|
||||
<LanczosParameters>
|
||||
<mass>0.00107</mass>
|
||||
<mass>0</mass>
|
||||
<mstep>-0.025</mstep>
|
||||
<M5>1.8</M5>
|
||||
<Ls>48</Ls>
|
||||
<Nstop>10</Nstop>
|
||||
<Nk>15</Nk>
|
||||
<Np>85</Np>
|
||||
<ChebyLow>0.003</ChebyLow>
|
||||
<ChebyHigh>60</ChebyHigh>
|
||||
<ChebyOrder>201</ChebyOrder>
|
||||
<Nstop>5</Nstop>
|
||||
<Nk>5</Nk>
|
||||
<Np>5</Np>
|
||||
<ReadEvec>0</ReadEvec>
|
||||
<maxIter>10000</maxIter>
|
||||
<resid>1e-10</resid>
|
||||
<ChebyLow>1</ChebyLow>
|
||||
<ChebyHigh>100</ChebyHigh>
|
||||
<ChebyOrder>51</ChebyOrder>
|
||||
</LanczosParameters>
|
||||
</grid>
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Test for BlockedKrylovSchur: verifies the KS decomposition A V = V H + F B^dag
|
||||
by explicit operator applies, before and after each restart.
|
||||
|
||||
Uses DumbOperator (diagonal, real, Hermitian) from Test_synthetic_lanczos.
|
||||
|
||||
Tests Nblock=1 (scalar, regression) and Nblock=2,3 (exercises the B^H fix).
|
||||
|
||||
*************************************************************************************/
|
||||
#include <Grid/Grid.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
// Diagonal real Hermitian operator (eigenvalues = scale lattice sites)
|
||||
template<class Field>
|
||||
class DumbOperator : public LinearOperatorBase<Field> {
|
||||
public:
|
||||
LatticeComplex scale;
|
||||
|
||||
DumbOperator(GridBase* grid) : scale(grid) {
|
||||
GridParallelRNG pRNG(grid);
|
||||
std::vector<int> seeds({5,6,7,8});
|
||||
pRNG.SeedFixedIntegers(seeds);
|
||||
random(pRNG, scale);
|
||||
scale = exp(-Grid::real(scale) * 3.0);
|
||||
}
|
||||
|
||||
void OpDirAll(const Field& in, std::vector<Field>& out) {}
|
||||
void OpDiag(const Field& in, Field& out) {}
|
||||
void OpDir(const Field& in, Field& out, int dir, int disp) {}
|
||||
|
||||
void Op(const Field& in, Field& out) { out = scale * in; }
|
||||
void AdjOp(const Field& in, Field& out) { out = scale * in; }
|
||||
void HermOp(const Field& in, Field& out) { out = scale * in; }
|
||||
void HermOpAndNorm(const Field& in, Field& out, double& n1, double& n2) {
|
||||
out = scale * in;
|
||||
ComplexD d = innerProduct(in, out); n1 = real(d);
|
||||
d = innerProduct(out, out); n2 = real(d);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv);
|
||||
|
||||
GridCartesian* grid = SpaceTimeGrid::makeFourDimGrid(
|
||||
GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd, vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
|
||||
GridParallelRNG RNG(grid);
|
||||
RNG.SeedFixedIntegers({1,2,3,4});
|
||||
|
||||
typedef LatticeComplex Field;
|
||||
|
||||
DumbOperator<Field> op(grid);
|
||||
|
||||
int nFail = 0;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Helper lambda: run BKS with doVerify and check it doesn't crash
|
||||
//--------------------------------------------------------------------
|
||||
auto runTest = [&](const std::string& label, int Nblock, int Nm, int Nk,
|
||||
int maxIter, int Nstop) {
|
||||
std::cout << GridLogMessage << "===== " << label << " =====" << std::endl;
|
||||
|
||||
BlockKrylovSchur<Field> bks(op, grid, 1e-6, EvalReSmall);
|
||||
|
||||
std::vector<Field> v0(Nblock, Field(grid));
|
||||
for (int t = 0; t < Nblock; t++) random(RNG, v0[t]);
|
||||
|
||||
bks(v0, maxIter, Nm, Nk, Nstop, Nblock,
|
||||
/*doubleOrthog=*/true, /*doVerify=*/true);
|
||||
|
||||
std::cout << GridLogMessage << label << " done." << std::endl;
|
||||
};
|
||||
|
||||
// Test 1: Nblock=1 — scalar case, regression (Nm,Nk now total vectors)
|
||||
runTest("Nblock=1 Nm=10 Nk=5 maxIter=3", 1, 10, 5, 3, 5);
|
||||
|
||||
// Test 2: Nblock=2 — exercises the B^H fix for off-diagonal elements
|
||||
runTest("Nblock=2 Nm=16 Nk=8 maxIter=3", 2, 16, 8, 3, 4);
|
||||
|
||||
// Test 3: Nblock=3 — further stress-test the B^H fix
|
||||
runTest("Nblock=3 Nm=27 Nk=9 maxIter=3", 3, 27, 9, 3, 3);
|
||||
|
||||
// Test 4: Nblock=2, larger cycle — more restarts
|
||||
runTest("Nblock=2 Nm=24 Nk=12 maxIter=5", 2, 24, 12, 5, 6);
|
||||
|
||||
if (nFail == 0)
|
||||
std::cout << GridLogMessage << "All BlockedKrylovSchur tests completed." << std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return nFail;
|
||||
}
|
||||
@@ -32,9 +32,13 @@ directory
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
//typedef WilsonFermionD FermionOp;
|
||||
#if 0
|
||||
typedef DomainWallFermionD FermionOp;
|
||||
typedef typename DomainWallFermionD::FermionField FermionField;
|
||||
#else
|
||||
typedef MobiusFermionD FermionOp;
|
||||
typedef typename MobiusFermionD::FermionField FermionField;
|
||||
#endif
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#ifdef HAVE_LIME
|
||||
@@ -179,12 +183,14 @@ int main(int argc, char** argv) {
|
||||
Np=LanParams.Np;
|
||||
|
||||
int Nm = Nk + Np;
|
||||
int MaxIt = 100;
|
||||
RealD resid = 1.0e-4;
|
||||
|
||||
int MaxIt = 10000;
|
||||
RealD resid = 1.0e-5;
|
||||
RealD mob_b=1.5;
|
||||
|
||||
//while ( mass > - 5.0){
|
||||
FermionOp Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5);
|
||||
// FermionOp Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5);
|
||||
FermionOp Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,mob_b,mob_b-1.);
|
||||
MdagMLinearOperator<FermionOp,FermionField> HermOp(Ddwf); /// <-----
|
||||
// Gamma5HermitianLinearOperator <FermionOp,LatticeFermion> HermOp2(WilsonOperator); /// <-----
|
||||
Gamma5R5HermitianLinearOperator<FermionOp, LatticeFermion> G5R5Herm(Ddwf);
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Test for Gamma5BlockLanczos on a simple diagonal operator.
|
||||
|
||||
The operator is D = diag(scale_i) where scale is complex random.
|
||||
γ5 is taken to be the identity (scalar field has no spin structure),
|
||||
so the γ5-inner product reduces to the standard Euclidean inner product
|
||||
and the algorithm should find the eigenvalues of D directly.
|
||||
|
||||
For a genuine Wilson Dirac test, pass the actual γ5 functor.
|
||||
|
||||
*************************************************************************************/
|
||||
#include <Grid/Grid.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
// Diagonal complex operator: out = scale * in
|
||||
template<class Field>
|
||||
class DumbOperator : public LinearOperatorBase<Field> {
|
||||
public:
|
||||
LatticeComplex scale;
|
||||
DumbOperator(GridBase* grid) : scale(grid) {
|
||||
GridParallelRNG pRNG(grid);
|
||||
pRNG.SeedFixedIntegers({5,6,7,8});
|
||||
random(pRNG, scale);
|
||||
scale = exp(-Grid::real(scale) * 3.0);
|
||||
}
|
||||
void OpDirAll(const Field& in, std::vector<Field>& out) {}
|
||||
void OpDiag(const Field& in, Field& out) {}
|
||||
void OpDir(const Field& in, Field& out, int dir, int disp) {}
|
||||
void Op(const Field& in, Field& out) { out = scale * in; }
|
||||
void AdjOp(const Field& in, Field& out) { out = scale * in; }
|
||||
void HermOp(const Field& in, Field& out) { out = scale * in; }
|
||||
void HermOpAndNorm(const Field& in, Field& out, double& n1, double& n2) {
|
||||
out = scale * in;
|
||||
ComplexD d = innerProduct(in, out); n1 = real(d);
|
||||
d = innerProduct(out, out); n2 = real(d);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv);
|
||||
|
||||
GridCartesian* grid = SpaceTimeGrid::makeFourDimGrid(
|
||||
GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd, vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
|
||||
GridParallelRNG RNG(grid);
|
||||
RNG.SeedFixedIntegers({1,2,3,4});
|
||||
|
||||
typedef LatticeComplex Field;
|
||||
DumbOperator<Field> op(grid);
|
||||
|
||||
// For LatticeComplex (scalar field) γ5 = identity
|
||||
auto gamma5 = [](const Field& in, Field& out){ out = in; };
|
||||
|
||||
Field v0(grid);
|
||||
random(RNG, v0);
|
||||
|
||||
const int maxSteps = 20;
|
||||
const int Nstop = 4;
|
||||
const RealD tol = 1e-6;
|
||||
|
||||
std::cout << GridLogMessage
|
||||
<< "\n========================================" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " Gamma5BlockLanczos (maxSteps=" << maxSteps
|
||||
<< " Nstop=" << Nstop << ")" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< "========================================\n" << std::endl;
|
||||
|
||||
Gamma5BlockLanczos<Field> g5bl(op, grid, gamma5, tol);
|
||||
g5bl.doEvalCheck = true;
|
||||
g5bl(v0, maxSteps, Nstop, /*reorthog=*/true);
|
||||
|
||||
auto evals = g5bl.getEvals();
|
||||
std::cout << GridLogMessage
|
||||
<< "Gamma5BlockLanczos eigenvalues (" << evals.size() << "):" << std::endl;
|
||||
for (int k = 0; k < (int)evals.size(); k++)
|
||||
std::cout << GridLogMessage << " [" << k << "] " << evals(k) << std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Comparison test: HarmonicBlockedKrylovSchur vs BlockedKrylovSchur.
|
||||
|
||||
Both algorithms are run on the same diagonal Hermitian operator and the
|
||||
resulting eigenvalues are compared. doVerify=true is used so the KS
|
||||
decomposition check max|H-M| and the per-column residuals are printed
|
||||
at each step. For BKS these should be O(machine epsilon) at all times.
|
||||
For HBKS they should be O(machine epsilon) AFTER Arnoldi, but may show
|
||||
large per-column deviations AFTER restart+truncation (because the rotation
|
||||
Q from Schur(Hhat) does not give an upper-triangular H_new, so the
|
||||
truncated KS relation is only approximate).
|
||||
|
||||
*************************************************************************************/
|
||||
#include <Grid/Grid.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
// Diagonal real Hermitian operator out = scale * in
|
||||
template<class Field>
|
||||
class DumbOperator : public LinearOperatorBase<Field> {
|
||||
public:
|
||||
LatticeComplex scale;
|
||||
DumbOperator(GridBase* grid) : scale(grid) {
|
||||
GridParallelRNG pRNG(grid);
|
||||
pRNG.SeedFixedIntegers({5,6,7,8});
|
||||
random(pRNG, scale);
|
||||
scale = exp(-Grid::real(scale) * 3.0);
|
||||
}
|
||||
void OpDirAll(const Field& in, std::vector<Field>& out) {}
|
||||
void OpDiag(const Field& in, Field& out) {}
|
||||
void OpDir(const Field& in, Field& out, int dir, int disp) {}
|
||||
void Op(const Field& in, Field& out) { out = scale * in; }
|
||||
void AdjOp(const Field& in, Field& out) { out = scale * in; }
|
||||
void HermOp(const Field& in, Field& out) { out = scale * in; }
|
||||
void HermOpAndNorm(const Field& in, Field& out, double& n1, double& n2) {
|
||||
out = scale * in;
|
||||
ComplexD d = innerProduct(in, out); n1 = real(d);
|
||||
d = innerProduct(out, out); n2 = real(d);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Grid_init(&argc, &argv);
|
||||
|
||||
GridCartesian* grid = SpaceTimeGrid::makeFourDimGrid(
|
||||
GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd, vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
|
||||
GridParallelRNG RNG(grid);
|
||||
RNG.SeedFixedIntegers({1,2,3,4});
|
||||
|
||||
typedef LatticeComplex Field;
|
||||
DumbOperator<Field> op(grid);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Parameters (kept small so output is readable)
|
||||
//----------------------------------------------------------------------
|
||||
const int Nblock = 4;
|
||||
const int Nm = 20; // total vectors (= 5 blocks * Nblock=4)
|
||||
const int Nk = 8; // total kept (= 2 blocks * Nblock=4)
|
||||
const int Nstop = 4;
|
||||
const int maxIter = 8;
|
||||
const RealD tol = 1e-6;
|
||||
|
||||
// Two identical starting blocks
|
||||
std::vector<Field> v0(Nblock, Field(grid));
|
||||
std::vector<Field> v0b(Nblock, Field(grid));
|
||||
for (int t = 0; t < Nblock; t++) {
|
||||
random(RNG, v0[t]);
|
||||
v0b[t] = v0[t]; // identical start for fair comparison
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Run BlockedKrylovSchur with doVerify=true
|
||||
//----------------------------------------------------------------------
|
||||
std::cout << GridLogMessage
|
||||
<< "\n========================================" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " BlockKrylovSchur (Nblock=" << Nblock
|
||||
<< " Nm=" << Nm << " Nk=" << Nk << ")" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< "========================================\n" << std::endl;
|
||||
|
||||
BlockKrylovSchur<Field> bks(op, grid, tol, EvalImNormSmall);
|
||||
bks(v0, maxIter, Nm, Nk, Nstop, Nblock,
|
||||
/*doubleOrthog=*/true, /*doVerify=*/true);
|
||||
|
||||
auto bks_evals = bks.getEvals();
|
||||
std::cout << GridLogMessage
|
||||
<< "BKS eigenvalues (" << bks_evals.size() << "):" << std::endl;
|
||||
for (int k = 0; k < (int)bks_evals.size(); k++)
|
||||
std::cout << GridLogMessage << " [" << k << "] " << bks_evals[k] << std::endl;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Run HarmonicBlockedKrylovSchur with doVerify=true
|
||||
//----------------------------------------------------------------------
|
||||
std::cout << GridLogMessage
|
||||
<< "\n========================================" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< " HarmonicBlockKrylovSchur (Nblock=" << Nblock
|
||||
<< " Nm=" << Nm << " Nk=" << Nk << " shift=0)" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< "========================================\n" << std::endl;
|
||||
|
||||
HarmonicBlockKrylovSchur<Field> hbks(op, grid, tol, 0.0, EvalImNormSmall);
|
||||
hbks(v0b, maxIter, Nm, Nk, Nstop, Nblock,
|
||||
/*doubleOrthog=*/true, /*doVerify=*/true);
|
||||
|
||||
auto hbks_evals = hbks.getEvals();
|
||||
std::cout << GridLogMessage
|
||||
<< "HBKS eigenvalues (" << hbks_evals.size() << "):" << std::endl;
|
||||
for (int k = 0; k < (int)hbks_evals.size(); k++)
|
||||
std::cout << GridLogMessage << " [" << k << "] " << hbks_evals[k] << std::endl;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Compare
|
||||
//----------------------------------------------------------------------
|
||||
std::cout << GridLogMessage
|
||||
<< "\n========================================" << std::endl;
|
||||
std::cout << GridLogMessage << " Eigenvalue comparison" << std::endl;
|
||||
std::cout << GridLogMessage
|
||||
<< "========================================" << std::endl;
|
||||
|
||||
// Sort both sets by real part for comparison
|
||||
std::vector<ComplexD> bvec(bks_evals.data(),
|
||||
bks_evals.data() + bks_evals.size());
|
||||
std::vector<ComplexD> hvec(hbks_evals.data(),
|
||||
hbks_evals.data() + hbks_evals.size());
|
||||
auto cmpRe = [](const ComplexD& a, const ComplexD& b){ return a.real() < b.real(); };
|
||||
std::sort(bvec.begin(), bvec.end(), cmpRe);
|
||||
std::sort(hvec.begin(), hvec.end(), cmpRe);
|
||||
|
||||
int nCmp = std::min(bvec.size(), hvec.size());
|
||||
double maxDiff = 0.0;
|
||||
for (int k = 0; k < nCmp; k++) {
|
||||
double diff = std::abs(bvec[k].real() - hvec[k].real()) + std::abs(bvec[k].imag() - hvec[k].imag());
|
||||
maxDiff = std::max(maxDiff, diff);
|
||||
std::cout << GridLogMessage
|
||||
<< " k=" << k
|
||||
<< " BKS=" << bvec[k]
|
||||
<< " HBKS=" << hvec[k]
|
||||
<< " |diff|=" << diff << std::endl;
|
||||
}
|
||||
std::cout << GridLogMessage << " max |BKS - HBKS| = " << maxDiff << std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -113,6 +113,9 @@ struct LanczosParameters: Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass ,
|
||||
RealD, resid,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
RealD, ChebyLow,
|
||||
RealD, ChebyHigh,
|
||||
Integer, ChebyOrder)
|
||||
@@ -204,7 +207,6 @@ int main(int argc, char** argv) {
|
||||
int Nstop = 5;
|
||||
int Nk = 10;
|
||||
int Np = 90;
|
||||
int Nm = Nk + Np;
|
||||
int MaxIt = 10000;
|
||||
RealD resid = 1.0e-5;
|
||||
|
||||
@@ -226,10 +228,14 @@ int main(int argc, char** argv) {
|
||||
XmlWriter HMCwr("LanParams.xml.out");
|
||||
write(HMCwr,"LanczosParameters",LanParams);
|
||||
}
|
||||
|
||||
Nstop=LanParams.Nstop;
|
||||
Nk=LanParams.Nk;
|
||||
Np=LanParams.Np;
|
||||
mass=LanParams.mass;
|
||||
resid=LanParams.resid;
|
||||
|
||||
int Nm = Nk + Np;
|
||||
|
||||
|
||||
while ( mass > - 5.0){
|
||||
FermionOp WilsonOperator(Umu,*FGrid,*FrbGrid,2.+mass);
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Grid physics library, www.github.com/paboyle/Grid
|
||||
|
||||
Source file: ./tests/lanczos/Test_wilson_bilanczos.cc
|
||||
|
||||
Copyright (C) 2025
|
||||
|
||||
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 */
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/lattice/PaddedCell.h>
|
||||
#include <Grid/stencil/GeneralLocalStencil.h>
|
||||
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidual.h>
|
||||
#include <Grid/algorithms/iterative/PrecGeneralisedConjugateResidualNonHermitian.h>
|
||||
#include <Grid/algorithms/iterative/BiCGSTAB.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
|
||||
namespace Grid {
|
||||
|
||||
struct LanczosParameters: Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass ,
|
||||
RealD, mstep ,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
Integer, ReadEvec,
|
||||
Integer, maxIter,
|
||||
RealD, resid,
|
||||
RealD, ChebyLow,
|
||||
RealD, ChebyHigh,
|
||||
Integer, ChebyOrder)
|
||||
|
||||
LanczosParameters() {
|
||||
////////////////////////////// Default values
|
||||
mass = 0;
|
||||
/////////////////////////////////
|
||||
}
|
||||
|
||||
template <class ReaderClass >
|
||||
LanczosParameters(Reader<ReaderClass> & TheReader){
|
||||
initialize(TheReader);
|
||||
}
|
||||
|
||||
template < class ReaderClass >
|
||||
void initialize(Reader<ReaderClass> &TheReader){
|
||||
// std::cout << GridLogMessage << "Reading HMC\n";
|
||||
read(TheReader, "HMC", *this);
|
||||
}
|
||||
|
||||
|
||||
void print_parameters() const {
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Trajectories : " << Trajectories << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Start trajectory : " << StartTrajectory << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Metropolis test (on/off): " << std::boolalpha << MetropolisTest << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Thermalization trajs : " << NoMetropolisUntil << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Starting type : " << StartingType << "\n";
|
||||
// MD.print_parameters();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#if 1
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0);
|
||||
WR.close();
|
||||
#endif
|
||||
// What is the appropriate way to throw error?
|
||||
}
|
||||
|
||||
|
||||
typedef WilsonFermionD WilsonOp;
|
||||
typedef typename WilsonFermionD::FermionField FermionField;
|
||||
|
||||
template<class Matrix,class Field>
|
||||
class InvertNonHermitianLinearOperator : public LinearOperatorBase<Field> {
|
||||
Matrix &_Mat;
|
||||
RealD _stp;
|
||||
public:
|
||||
InvertNonHermitianLinearOperator(Matrix &Mat,RealD stp=1e-8): _Mat(Mat),_stp(stp){};
|
||||
// Support for coarsening to a multigrid
|
||||
void OpDiag (const Field &in, Field &out) {
|
||||
// _Mat.Mdiag(in,out);
|
||||
// out = out + shift*in;
|
||||
assert(0);
|
||||
}
|
||||
void OpDir (const Field &in, Field &out,int dir,int disp) {
|
||||
// _Mat.Mdir(in,out,dir,disp);
|
||||
assert(0);
|
||||
}
|
||||
void OpDirAll (const Field &in, std::vector<Field> &out){
|
||||
// _Mat.MdirAll(in,out);
|
||||
assert(0);
|
||||
};
|
||||
void Op (const Field &in, Field &out){
|
||||
Field tmp(in.Grid());
|
||||
_Mat.Mdag(in,tmp);
|
||||
MdagMLinearOperator<Matrix,Field> HermOp(_Mat);
|
||||
ConjugateGradient<Field> CG(_stp,10000);
|
||||
CG(HermOp,tmp,out);
|
||||
}
|
||||
void AdjOp (const Field &in, Field &out){
|
||||
_Mat.Mdag(in,out);
|
||||
// out = out + shift * in;
|
||||
}
|
||||
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
|
||||
assert(0);
|
||||
}
|
||||
void HermOp(const Field &in, Field &out){
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Field>
|
||||
void testSchurFromHess(Arnoldi<Field>& Arn, Field& src, int Nlarge, int Nm, int Nk) {
|
||||
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout << GridLogMessage << "Testing Schur reordering, Nm = " << Nm << ", Nk = " << Nk << std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "Running Arnoldi for 1 iteration to get a Hessenberg." << std::endl;
|
||||
Arn(src, 1, Nlarge, Nm, Nlarge);
|
||||
Eigen::MatrixXcd Hess = Arn.getHessenbergMat();
|
||||
std::cout << GridLogMessage << "Hessenberg for use: " << std::endl << Hess << std::endl;
|
||||
|
||||
ComplexSchurDecomposition schur (Hess, true);
|
||||
bool isDecomposed = schur.checkDecomposition();
|
||||
std::cout << "Schur decomp holds? " << isDecomposed << std::endl;
|
||||
|
||||
std::cout << GridLogMessage << "S = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << GridLogMessage << "Swapping S(3, 3) with S(4, 4)" << std::endl;
|
||||
schur.swapEvals(3);
|
||||
std::cout << GridLogMessage << "S after swap = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << "Schur decomp still holds? " << schur.checkDecomposition() << std::endl;
|
||||
|
||||
// Now move last diagonal element all the way to the front.
|
||||
std::cout << GridLogMessage << "Moving last eval to front. S at start = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
for (int i = 0; i < Nk - 1; i++) {
|
||||
int swapIdx = Nk - 2 - i;
|
||||
schur.swapEvals(swapIdx);
|
||||
std::cout << GridLogMessage << "S after swap of index " << swapIdx << " = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << "Schur decomp still holds? " << schur.checkDecomposition() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage << "Testing Schur reorder" << std::endl;
|
||||
schur.schurReorder(Nk);
|
||||
std::cout << GridLogMessage << "S after reorder = " << std::endl << schur.getMatrixS() << std::endl;
|
||||
std::cout << "Schur decomp still holds? " << schur.checkDecomposition() << std::endl;
|
||||
|
||||
}
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
const int Ls=16;
|
||||
|
||||
// GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());
|
||||
// std::vector<int> lat_size {32, 32, 32, 32};
|
||||
// std::cout << "Lattice size: " << lat_size << std::endl;
|
||||
GridCartesian * UGrid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian * UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
// GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
||||
// GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
||||
GridCartesian * FGrid = UGrid;
|
||||
GridRedBlackCartesian * FrbGrid = UrbGrid;
|
||||
|
||||
// Construct a coarsened grid
|
||||
// poare TODO: replace this with the following line?
|
||||
Coordinate clatt = GridDefaultLatt();
|
||||
// Coordinate clatt = GridDefaultLatt(); // [PO] initial line before I edited it
|
||||
for(int d=0;d<clatt.size();d++){
|
||||
std::cout << GridLogMessage<< clatt[d] <<std::endl;
|
||||
clatt[d] = clatt[d]/2;
|
||||
// clatt[d] = clatt[d]/4;
|
||||
}
|
||||
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt, GridDefaultSimd(Nd,vComplex::Nsimd()),GridDefaultMpi());;
|
||||
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
|
||||
|
||||
std::vector<int> seeds4({1,2,3,4});
|
||||
std::vector<int> seeds5({5,6,7,8});
|
||||
std::vector<int> cseeds({5,6,7,8});
|
||||
GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers(seeds5);
|
||||
GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers(seeds4);
|
||||
GridParallelRNG CRNG(Coarse5d);CRNG.SeedFixedIntegers(cseeds);
|
||||
|
||||
LatticeFermion result(FGrid); result=Zero();
|
||||
LatticeFermion ref(FGrid); ref=Zero();
|
||||
LatticeFermion tmp(FGrid);
|
||||
LatticeFermion err(FGrid);
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
|
||||
FieldMetaData header;
|
||||
std::string file("config");
|
||||
// std::string file("Users/patrickoare/libraries/PETSc-Grid/ckpoint_lat.4000");
|
||||
NerscIO::readConfiguration(Umu,header,file);
|
||||
|
||||
LanczosParameters LanParams;
|
||||
{
|
||||
XmlReader HMCrd("LanParams.xml");
|
||||
read(HMCrd,"LanczosParameters",LanParams);
|
||||
}
|
||||
|
||||
std::cout << GridLogMessage<< LanParams <<std::endl;
|
||||
{
|
||||
XmlWriter HMCwr("LanParams.xml.out");
|
||||
write(HMCwr,"LanczosParameters",LanParams);
|
||||
}
|
||||
|
||||
|
||||
RealD mass=0.01;
|
||||
RealD M5=1.8;
|
||||
|
||||
// PowerMethod<LatticeFermion> PM; PM(PVdagM, src);
|
||||
int Nm = 50;
|
||||
int Nk = 12;
|
||||
int Np = 38;
|
||||
// int Nk = Nm+1; // if just running once
|
||||
int maxIter = 10000;
|
||||
int Nstop = 10;
|
||||
RealD resid = 1.0e-5;
|
||||
|
||||
std::vector<Complex> boundary = {1,1,1,-1};
|
||||
WilsonOp::ImplParams Params(boundary);
|
||||
|
||||
// DomainWallFermionD Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5);
|
||||
// DomainWallFermionD Dpv(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,1.0,M5);
|
||||
|
||||
mass=LanParams.mass;
|
||||
std::cout << GridLogIRL<< "mass "<<mass<<std::endl;
|
||||
WilsonOp WilsonOperator(Umu,*UGrid,*UrbGrid,mass,Params);
|
||||
|
||||
// const int nbasis = 20; // size of approximate basis for low-mode space
|
||||
const int nbasis = 3; // size of approximate basis for low-mode space
|
||||
const int cb = 0 ;
|
||||
LatticeFermion prom(FGrid);
|
||||
|
||||
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
|
||||
typedef LittleDiracOperator::CoarseVector CoarseVector;
|
||||
|
||||
NextToNearestStencilGeometry5D geom(Coarse5d);
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
|
||||
// typedef PVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> PVdagM_t;
|
||||
// typedef ShiftedPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedPVdagM_t;
|
||||
// typedef ShiftedComplexPVdagMLinearOperator<DomainWallFermionD,LatticeFermionD> ShiftedComplexPVdagM_t;
|
||||
// PVdagM_t PVdagM(Ddwf, Dpv);
|
||||
// ShiftedPVdagM_t ShiftedPVdagM(0.1,Ddwf,Dpv);
|
||||
// SquaredLinearOperator<DomainWallFermionD, LatticeFermionD> Dsq (Ddwf);
|
||||
// NonHermitianLinearOperator<DomainWallFermionD, LatticeFermionD> DLinOp (Ddwf);
|
||||
|
||||
|
||||
NonHermitianLinearOperator<WilsonOp,FermionField> Dwilson(WilsonOperator); /// <-----
|
||||
// InvertNonHermitianLinearOperator<WilsonOp,FermionField> Iwilson(WilsonOperator); /// <-----
|
||||
MdagMLinearOperator<WilsonOp,FermionField> HermOp(WilsonOperator); /// <-----
|
||||
Gamma5HermitianLinearOperator <WilsonOp,LatticeFermion> HermOp2(WilsonOperator); /// <----
|
||||
|
||||
// PowerMethod<LatticeFermion> PM; PM(PVdagM, src);
|
||||
resid=LanParams.resid;
|
||||
Nstop=LanParams.Nstop;
|
||||
Nk=LanParams.Nk;
|
||||
Np=LanParams.Np;
|
||||
maxIter=LanParams.maxIter;
|
||||
Nm = Nk + Np;
|
||||
int Nu=16;
|
||||
std::vector<LatticeFermion> src(Nu,FGrid);
|
||||
for(int i=0;i<Nu;i++) random(RNG5,src[i]);
|
||||
|
||||
if(LanParams.ReadEvec) {
|
||||
std::string evecs_file="evec_in";
|
||||
std::cout << GridLogIRL<< "Reading evecs from "<<evecs_file<<std::endl;
|
||||
emptyUserRecord record;
|
||||
Grid::ScidacReader RD;
|
||||
RD.open(evecs_file);
|
||||
RD.readScidacFieldRecord(src[0],record);
|
||||
RD.close();
|
||||
}
|
||||
|
||||
Coordinate origin ({0,0,0,0});
|
||||
auto tmpSrc = peekSite(src[0], origin);
|
||||
std::cout << "[DEBUG] Source at origin = " << tmpSrc << std::endl;
|
||||
LatticeFermion src2 = src[0];
|
||||
|
||||
// Run KrylovSchur and Arnoldi on a Hermitian matrix
|
||||
std::cout << GridLogMessage << "Running Krylov Schur" << std::endl;
|
||||
#if 0
|
||||
#if 1
|
||||
RealD shift=1.5;
|
||||
KrylovSchur KrySchur (Dwilson, UGrid, resid,EvalImNormSmall);
|
||||
KrySchur(src[0], maxIter, Nm, Nk, Nstop,&shift);
|
||||
#else
|
||||
KrylovSchur KrySchur (Iwilson, UGrid, resid,EvalImNormSmall);
|
||||
KrySchur(src[0], maxIter, Nm, Nk, Nstop);
|
||||
#endif
|
||||
std::cout << GridLogMessage << "evec.size= " << KrySchur.evecs.size()<< std::endl;
|
||||
#else
|
||||
LanczosBidiagonalization<FermionField> LB(Dwilson, UGrid);
|
||||
LB.run(src[0], Nm, resid);
|
||||
RestartedLanczosBidiagonalization<FermionField> IRLBA(Dwilson, UGrid, Nstop, Nm, resid, maxIter,false);
|
||||
IRLBA.run(src[0]);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
src[0]=KrySchur.evecs[0];
|
||||
for (int i=1;i<Nstop;i++) src[0]+=KrySchur.evecs[i];
|
||||
for (int i=0;i<Nstop;i++)
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_"+std::to_string(i));
|
||||
auto evdensity = localInnerProduct(KrySchur.evecs[i],KrySchur.evecs[i] );
|
||||
writeFile(evdensity,evfile);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_sum");
|
||||
// auto evdensity = localInnerProduct(evec[i],evec[i] );
|
||||
writeFile(src[0],evfile);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
std::cout << GridLogMessage << "Running Arnoldi" << std::endl;
|
||||
// Arnoldi Arn (Dsq, FGrid, 1e-8);
|
||||
Arnoldi Arn (DLinOp, FGrid, 1e-8);
|
||||
testSchurFromHess<LatticeFermion>(Arn, src, 10, 6, 4);
|
||||
|
||||
Arnoldi Arn2 (DLinOp, FGrid, 1e-8);
|
||||
testSchurFromHess<LatticeFermion>(Arn2, src, 16, 12, 8);
|
||||
*/
|
||||
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage<<"*******************************************"<<std::endl;
|
||||
std::cout<<GridLogMessage<<std::endl;
|
||||
std::cout<<GridLogMessage << "Done "<< std::endl;
|
||||
|
||||
Grid_finalize();
|
||||
return 0;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ Source file: ./tests/Test_dwf_lanczos.cc
|
||||
|
||||
Copyright (C) 2015
|
||||
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
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
|
||||
@@ -27,6 +27,9 @@ directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/parallelIO/IldgIOtypes.h>
|
||||
#include <Grid/algorithms/iterative/ImplicitlyRestartedBlockLanczos.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
@@ -38,18 +41,111 @@ typedef typename WilsonFermionD::FermionField FermionField;
|
||||
|
||||
RealD AllZero(RealD x) { return 0.; }
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#if 1
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0);
|
||||
WR.close();
|
||||
#endif
|
||||
// What is the appropriate way to throw error?
|
||||
}
|
||||
|
||||
|
||||
namespace Grid {
|
||||
|
||||
struct LanczosParameters: Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass ,
|
||||
RealD, mstep ,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
Integer, ReadEvec,
|
||||
RealD, resid,
|
||||
RealD, ChebyLow,
|
||||
RealD, ChebyHigh,
|
||||
Integer, ChebyOrder)
|
||||
// Integer, StartTrajectory,
|
||||
// Integer, Trajectories, /* @brief Number of sweeps in this run */
|
||||
// bool, MetropolisTest,
|
||||
// Integer, NoMetropolisUntil,
|
||||
// std::string, StartingType,
|
||||
// Integer, SW,
|
||||
// RealD, Kappa,
|
||||
// IntegratorParameters, MD)
|
||||
|
||||
LanczosParameters() {
|
||||
////////////////////////////// Default values
|
||||
mass = 0;
|
||||
// MetropolisTest = true;
|
||||
// NoMetropolisUntil = 10;
|
||||
// StartTrajectory = 0;
|
||||
// SW = 2;
|
||||
// Trajectories = 10;
|
||||
// StartingType = "HotStart";
|
||||
/////////////////////////////////
|
||||
}
|
||||
|
||||
template <class ReaderClass >
|
||||
LanczosParameters(Reader<ReaderClass> & TheReader){
|
||||
initialize(TheReader);
|
||||
}
|
||||
|
||||
template < class ReaderClass >
|
||||
void initialize(Reader<ReaderClass> &TheReader){
|
||||
// std::cout << GridLogMessage << "Reading HMC\n";
|
||||
read(TheReader, "HMC", *this);
|
||||
}
|
||||
|
||||
|
||||
void print_parameters() const {
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Trajectories : " << Trajectories << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Start trajectory : " << StartTrajectory << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Metropolis test (on/off): " << std::boolalpha << MetropolisTest << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Thermalization trajs : " << NoMetropolisUntil << "\n";
|
||||
// std::cout << GridLogMessage << "[HMC parameters] Starting type : " << StartingType << "\n";
|
||||
// MD.print_parameters();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Grid_init(&argc, &argv);
|
||||
|
||||
int Ndir=4;
|
||||
auto mpi_layout = GridDefaultMpi();
|
||||
std::vector<int> nblock(4,1);
|
||||
std::vector<int> mpi_split(4,1);
|
||||
//Interested in avoiding degeneracy only for now
|
||||
nblock[3]=2;
|
||||
|
||||
int mrhs=1;
|
||||
for(int i =0;i<Ndir;i++){
|
||||
mpi_split[i] = mpi_layout[i] / nblock[i];
|
||||
mrhs *= nblock[i];
|
||||
}
|
||||
|
||||
|
||||
GridCartesian* UGrid = SpaceTimeGrid::makeFourDimGrid(
|
||||
GridDefaultLatt(), GridDefaultSimd(Nd, vComplex::Nsimd()),
|
||||
GridDefaultMpi());
|
||||
GridRedBlackCartesian* UrbGrid =
|
||||
SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
GridCartesian * SGrid = new GridCartesian(GridDefaultLatt(),
|
||||
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
||||
mpi_split,
|
||||
*UGrid);
|
||||
|
||||
GridRedBlackCartesian* UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
||||
|
||||
GridCartesian* FGrid = UGrid;
|
||||
GridRedBlackCartesian* FrbGrid = UrbGrid;
|
||||
printf("UGrid=%p UrbGrid=%p FGrid=%p FrbGrid=%p\n", UGrid, UrbGrid, FGrid,
|
||||
FrbGrid);
|
||||
// printf("UGrid=%p UrbGrid=%p FGrid=%p FrbGrid=%p\n", UGrid, UrbGrid, FGrid, FrbGrid);
|
||||
|
||||
std::vector<int> seeds4({1, 2, 3, 4});
|
||||
std::vector<int> seeds5({5, 6, 7, 8});
|
||||
@@ -61,7 +157,16 @@ int main(int argc, char** argv) {
|
||||
RNG5.SeedFixedIntegers(seeds5);
|
||||
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
SU<Nc>::HotConfiguration(RNG4, Umu);
|
||||
// SU<Nc>::HotConfiguration(RNG4, Umu);
|
||||
// SU<Nc>::ColdConfiguration(Umu);
|
||||
|
||||
FieldMetaData header;
|
||||
std::string file("./config");
|
||||
|
||||
// int precision32 = 0;
|
||||
// int tworow = 0;
|
||||
// NerscIO::writeConfiguration(Umu,file,tworow,precision32);
|
||||
NerscIO::readConfiguration(Umu,header,file);
|
||||
|
||||
/*
|
||||
std::vector<LatticeColourMatrix> U(4, UGrid);
|
||||
@@ -70,30 +175,100 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
*/
|
||||
|
||||
RealD mass = -0.1;
|
||||
FermionOp WilsonOperator(Umu,*FGrid,*FrbGrid,mass);
|
||||
MdagMLinearOperator<FermionOp,LatticeFermion> HermOp(WilsonOperator); /// <-----
|
||||
//SchurDiagTwoOperator<FermionOp,FermionField> HermOp(WilsonOperator);
|
||||
int Nstop = 10;
|
||||
int Nu = 1;
|
||||
int Nk = 20;
|
||||
int Np = 80;
|
||||
int Nm = Nk + Np;
|
||||
int MaxIt = 10000;
|
||||
RealD resid = 1.0e-5;
|
||||
|
||||
const int Nstop = 20;
|
||||
const int Nk = 60;
|
||||
const int Np = 60;
|
||||
const int Nm = Nk + Np;
|
||||
const int MaxIt = 10000;
|
||||
RealD resid = 1.0e-6;
|
||||
RealD mass = -1.0;
|
||||
|
||||
LanczosParameters LanParams;
|
||||
#if 1
|
||||
{
|
||||
XmlReader HMCrd("LanParams.xml");
|
||||
read(HMCrd,"LanczosParameters",LanParams);
|
||||
}
|
||||
#else
|
||||
{
|
||||
LanParams.mass = mass;
|
||||
}
|
||||
#endif
|
||||
std::cout << GridLogMessage<< LanParams <<std::endl;
|
||||
{
|
||||
XmlWriter HMCwr("LanParams.xml.out");
|
||||
write(HMCwr,"LanczosParameters",LanParams);
|
||||
}
|
||||
|
||||
mass=LanParams.mass;
|
||||
resid=LanParams.resid;
|
||||
Nstop=LanParams.Nstop;
|
||||
Nu = mrhs;
|
||||
Nk=LanParams.Nk;
|
||||
Np=LanParams.Np;
|
||||
Nm = Nk + Np;
|
||||
|
||||
// FermionField src(FGrid);
|
||||
std::vector<FermionField> src(Nu,FGrid);
|
||||
for(int i =0;i<Nu;i++) gaussian(RNG5, src[i]);
|
||||
|
||||
if(LanParams.ReadEvec) {
|
||||
std::string evecs_file="evec_in";
|
||||
std::cout << GridLogIRL<< "Reading evecs from "<<evecs_file<<std::endl;
|
||||
emptyUserRecord record;
|
||||
Grid::ScidacReader RD;
|
||||
RD.open(evecs_file);
|
||||
RD.readScidacFieldRecord(src[0],record);
|
||||
RD.close();
|
||||
}
|
||||
|
||||
std::vector<Complex> boundary = {1,1,1,-1};
|
||||
// std::vector<Complex> boundary = {1,1,1,1};
|
||||
FermionOp::ImplParams Params(boundary);
|
||||
|
||||
GridCartesian * SFGrid = SGrid;
|
||||
GridRedBlackCartesian * SFrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(SFGrid);
|
||||
// GridRedBlackCartesian * SFrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(JP.Ls,SGrid);
|
||||
|
||||
LatticeGaugeField s_Umu(SGrid);
|
||||
Grid_split (Umu,s_Umu);
|
||||
|
||||
|
||||
|
||||
while ( mass > - 2.0){
|
||||
FermionOp WilsonOperator(Umu,*FGrid,*FrbGrid,mass,Params);
|
||||
MdagMLinearOperator<FermionOp,FermionField> HermOp(WilsonOperator); /// <-----
|
||||
FermionOp WilsonSplit(s_Umu,*SFGrid,*SFrbGrid,mass,Params);
|
||||
MdagMLinearOperator<FermionOp,FermionField> SHermOp(WilsonSplit); /// <-----
|
||||
//SchurDiagTwoOperator<FermionOp,FermionField> HermOp(WilsonOperator);
|
||||
Gamma5HermitianLinearOperator <FermionOp,LatticeFermion> HermOp2(WilsonOperator); /// <-----
|
||||
|
||||
std::vector<double> Coeffs{0, 1.};
|
||||
Polynomial<FermionField> PolyX(Coeffs);
|
||||
Chebyshev<FermionField> Cheby(0.0, 10., 12);
|
||||
// Chebyshev<FermionField> Cheby(0.5, 60., 31);
|
||||
// RealD, ChebyLow,
|
||||
// RealD, ChebyHigh,
|
||||
// Integer, ChebyOrder)
|
||||
|
||||
Chebyshev<FermionField> Cheby(LanParams.ChebyLow,LanParams.ChebyHigh,LanParams.ChebyOrder);
|
||||
|
||||
FunctionHermOp<FermionField> OpCheby(Cheby,HermOp);
|
||||
PlainHermOp<FermionField> Op (HermOp);
|
||||
PlainHermOp<FermionField> Op2 (HermOp2);
|
||||
|
||||
ImplicitlyRestartedLanczos<FermionField> IRL(OpCheby, Op, Nstop, Nk, Nm, resid, MaxIt);
|
||||
|
||||
// ImplicitlyRestartedLanczos<FermionField> IRL(OpCheby, Op2, Nstop, Nk, Nm, resid, MaxIt);
|
||||
// SimpleLanczos<FermionField> IRL(Op,Nstop, Nk, Nm, resid, MaxIt);
|
||||
ImplicitlyRestartedBlockLanczos<FermionField> IRBL(HermOp, SHermOp,
|
||||
FrbGrid,SFrbGrid,mrhs,
|
||||
Cheby,
|
||||
Nstop, Nstop*2,
|
||||
Nu, Nk, Nm,
|
||||
resid, MaxIt,
|
||||
IRBLdiagonaliseWithEigen);
|
||||
IRBL.split_test=1;
|
||||
std::vector<RealD> eval(Nm);
|
||||
FermionField src(FGrid);
|
||||
gaussian(RNG5, src);
|
||||
std::vector<FermionField> evec(Nm, FGrid);
|
||||
for (int i = 0; i < 1; i++) {
|
||||
std::cout << i << " / " << Nm << " grid pointer " << evec[i].Grid()
|
||||
@@ -101,9 +276,40 @@ int main(int argc, char** argv) {
|
||||
};
|
||||
|
||||
int Nconv;
|
||||
IRL.calc(eval, evec, src, Nconv);
|
||||
// IRL.calc(eval, evec, src, Nconv);
|
||||
IRBL.calc(eval, evec, src, Nconv,LanczosType::irbl);
|
||||
|
||||
std::cout << eval << std::endl;
|
||||
std::cout << mass <<" : " << eval << std::endl;
|
||||
|
||||
Gamma g5(Gamma::Algebra::Gamma5) ;
|
||||
ComplexD dot;
|
||||
FermionField tmp(FGrid);
|
||||
FermionField sav(FGrid);
|
||||
sav=evec[0];
|
||||
for (int i = 0; i < Nstop ; i++) {
|
||||
tmp = g5*evec[i];
|
||||
dot = innerProduct(tmp,evec[i]);
|
||||
std::cout << mass << " : " << eval[i] << " " << real(dot) << " " << imag(dot) << std::endl ;
|
||||
// if ( i<1)
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_"+std::to_string(i));
|
||||
auto evdensity = localInnerProduct(evec[i],evec[i] );
|
||||
writeFile(evdensity,evfile);
|
||||
}
|
||||
if (i>0) sav += evec[i];
|
||||
}
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_sum");
|
||||
// auto evdensity = localInnerProduct(evec[i],evec[i] );
|
||||
writeFile(sav,evfile);
|
||||
}
|
||||
for(int i =0;i<Nu;i++) src[i]=evec[i];
|
||||
for(int i=Nu;i<Nstop;i++) src[i%Nu] +=evec[i];
|
||||
// src = evec[0]+evec[1]+evec[2];
|
||||
// src += evec[3]+evec[4]+evec[5];
|
||||
// src += evec[6]+evec[7]+evec[8];
|
||||
mass += LanParams.mstep;
|
||||
}
|
||||
|
||||
Grid_finalize();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ directory
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/parallelIO/IldgIOtypes.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Grid;
|
||||
@@ -38,11 +39,32 @@ typedef typename WilsonFermionD::FermionField FermionField;
|
||||
|
||||
RealD AllZero(RealD x) { return 0.; }
|
||||
|
||||
template <class T> void writeFile(T& in, std::string const fname){
|
||||
#if 1
|
||||
// Ref: https://github.com/paboyle/Grid/blob/feature/scidac-wp1/tests/debug/Test_general_coarse_hdcg_phys48.cc#L111
|
||||
std::cout << Grid::GridLogMessage << "Writes to: " << fname << std::endl;
|
||||
Grid::emptyUserRecord record;
|
||||
Grid::ScidacWriter WR(in.Grid()->IsBoss());
|
||||
WR.open(fname);
|
||||
WR.writeScidacFieldRecord(in,record,0);
|
||||
WR.close();
|
||||
#endif
|
||||
// What is the appropriate way to throw error?
|
||||
}
|
||||
|
||||
|
||||
namespace Grid {
|
||||
|
||||
struct LanczosParameters: Serializable {
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters,
|
||||
RealD, mass ,
|
||||
RealD, mstep ,
|
||||
Integer, Nstop,
|
||||
Integer, Nk,
|
||||
Integer, Np,
|
||||
Integer, ReadEvec,
|
||||
Integer, maxIter,
|
||||
RealD, resid,
|
||||
RealD, ChebyLow,
|
||||
RealD, ChebyHigh,
|
||||
Integer, ChebyOrder)
|
||||
@@ -115,12 +137,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
LatticeGaugeField Umu(UGrid);
|
||||
// SU<Nc>::HotConfiguration(RNG4, Umu);
|
||||
// SU<Nc>::ColdConfiguration(Umu);
|
||||
|
||||
FieldMetaData header;
|
||||
std::string file("./config");
|
||||
|
||||
int precision32 = 0;
|
||||
int tworow = 0;
|
||||
// int precision32 = 0;
|
||||
// int tworow = 0;
|
||||
// NerscIO::writeConfiguration(Umu,file,tworow,precision32);
|
||||
NerscIO::readConfiguration(Umu,header,file);
|
||||
|
||||
@@ -158,10 +181,32 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
mass=LanParams.mass;
|
||||
resid=LanParams.resid;
|
||||
Nstop=LanParams.Nstop;
|
||||
Nk=LanParams.Nk;
|
||||
Np=LanParams.Np;
|
||||
MaxIt=LanParams.maxIter;
|
||||
Nm = Nk + Np;
|
||||
|
||||
FermionField src(FGrid);
|
||||
gaussian(RNG5, src);
|
||||
if(LanParams.ReadEvec) {
|
||||
std::string evecs_file="evec_in";
|
||||
std::cout << GridLogIRL<< "Reading evecs from "<<evecs_file<<std::endl;
|
||||
emptyUserRecord record;
|
||||
Grid::ScidacReader RD;
|
||||
RD.open(evecs_file);
|
||||
RD.readScidacFieldRecord(src,record);
|
||||
RD.close();
|
||||
}
|
||||
|
||||
std::vector<Complex> boundary = {1,1,1,-1};
|
||||
// std::vector<Complex> boundary = {1,1,1,1};
|
||||
FermionOp::ImplParams Params(boundary);
|
||||
|
||||
|
||||
while ( mass > - 5.0){
|
||||
FermionOp WilsonOperator(Umu,*FGrid,*FrbGrid,mass);
|
||||
while ( mass > - 2.0){
|
||||
FermionOp WilsonOperator(Umu,*FGrid,*FrbGrid,mass,Params);
|
||||
MdagMLinearOperator<FermionOp,FermionField> HermOp(WilsonOperator); /// <-----
|
||||
//SchurDiagTwoOperator<FermionOp,FermionField> HermOp(WilsonOperator);
|
||||
Gamma5HermitianLinearOperator <FermionOp,LatticeFermion> HermOp2(WilsonOperator); /// <-----
|
||||
@@ -179,11 +224,10 @@ while ( mass > - 5.0){
|
||||
PlainHermOp<FermionField> Op (HermOp);
|
||||
PlainHermOp<FermionField> Op2 (HermOp2);
|
||||
|
||||
ImplicitlyRestartedLanczos<FermionField> IRL(OpCheby, Op2, Nstop, Nk, Nm, resid, MaxIt);
|
||||
ImplicitlyRestartedLanczos<FermionField> IRL(OpCheby, Op, Nstop, Nk, Nm, resid, MaxIt);
|
||||
// SimpleLanczos<FermionField> IRL(Op,Nstop, Nk, Nm, resid, MaxIt);
|
||||
|
||||
std::vector<RealD> eval(Nm);
|
||||
FermionField src(FGrid);
|
||||
gaussian(RNG5, src);
|
||||
std::vector<FermionField> evec(Nm, FGrid);
|
||||
for (int i = 0; i < 1; i++) {
|
||||
std::cout << i << " / " << Nm << " grid pointer " << evec[i].Grid()
|
||||
@@ -192,19 +236,46 @@ while ( mass > - 5.0){
|
||||
|
||||
int Nconv;
|
||||
IRL.calc(eval, evec, src, Nconv);
|
||||
// IRL.calc(eval, src, Nconv);
|
||||
|
||||
std::cout << mass <<" : " << eval << std::endl;
|
||||
|
||||
Gamma g5(Gamma::Algebra::Gamma5) ;
|
||||
ComplexD dot;
|
||||
FermionField tmp(FGrid);
|
||||
FermionField sav(FGrid);
|
||||
sav=evec[0];
|
||||
for (int i = 0; i < Nstop ; i++) {
|
||||
tmp = g5*evec[i];
|
||||
dot = innerProduct(tmp,evec[i]);
|
||||
std::cout << mass << " : " << eval[i] << " " << real(dot) << " " << imag(dot) << std::endl ;
|
||||
// if ( i<1)
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_"+std::to_string(i));
|
||||
auto evdensity = localInnerProduct(evec[i],evec[i] );
|
||||
writeFile(evdensity,evfile);
|
||||
// if(LanParams.ReadEvec) {
|
||||
// std::string evecs_file="evec_in";
|
||||
{
|
||||
std::cout << GridLogIRL<< "Reading evecs from "<<evfile<<std::endl;
|
||||
emptyUserRecord record;
|
||||
Grid::ScidacReader RD;
|
||||
RD.open(evfile);
|
||||
RD.readScidacFieldRecord(evdensity,record);
|
||||
RD.close();
|
||||
}
|
||||
}
|
||||
if (i>0) sav += evec[i];
|
||||
}
|
||||
{
|
||||
std::string evfile ("./evec_"+std::to_string(mass)+"_sum");
|
||||
// auto evdensity = localInnerProduct(evec[i],evec[i] );
|
||||
writeFile(sav,evfile);
|
||||
}
|
||||
src = evec[0]+evec[1]+evec[2];
|
||||
mass += -0.1;
|
||||
src += evec[3]+evec[4]+evec[5];
|
||||
src += evec[6]+evec[7]+evec[8];
|
||||
mass += LanParams.mstep;
|
||||
}
|
||||
|
||||
Grid_finalize();
|
||||
|
||||
Reference in New Issue
Block a user