https://github.com/ITensor/ITensor
Tip revision: 6b54fa39438c296f80049dc937ca966746eded68 authored by Miles Stoudenmire on 11 April 2016, 18:02:29 UTC
Updated localop.h diag method to correctly use delta function to tie indices. Fixes #96 - thanks to Xiongjie Yu for reporting it.
Updated localop.h diag method to correctly use delta function to tie indices. Fixes #96 - thanks to Xiongjie Yu for reporting it.
Tip revision: 6b54fa3
eigensolver.h
//
// Distributed under the ITensor Library License, Version 1.2
// (See accompanying LICENSE file.)
//
#ifndef __ITENSOR_EIGENSOLVER_H
#define __ITENSOR_EIGENSOLVER_H
#include "itensor/util/range.h"
#include "itensor/iqtensor.h"
#include "itensor/tensor/algs.h"
namespace itensor {
//
// Use the Davidson algorithm to find the
// eigenvector of the Hermitian matrix A with minimal eigenvalue.
// (BigMatrixT objects must implement the methods product, size and diag.)
// Returns the minimal eigenvalue lambda such that
// A phi = lambda phi.
//
template <class BigMatrixT, class Tensor>
Real
davidson(BigMatrixT const& A,
Tensor& phi,
Args const& args = Args::global());
//
// Use Davidson to find the N eigenvectors with smallest
// eigenvalues of the Hermitian matrix A, given a vector of N
// initial guesses (zero indexed).
// (BigMatrixT objects must implement the methods product, size and diag.)
// Returns a vector of the N smallest eigenvalues corresponding
// to the set of eigenvectors phi.
//
template <class BigMatrixT, class Tensor>
std::vector<Real>
davidson(BigMatrixT const& A,
std::vector<Tensor>& phi,
Args const& args = Args::global());
//
//
// Implementations
//
//
template <class BigMatrixT, class Tensor>
Real
davidson(BigMatrixT const& A,
Tensor& phi,
Args const& args)
{
auto v = std::vector<Tensor>(1);
v.front() = phi;
auto eigs = davidson(A,v,args);
phi = v.front();
return eigs.front();
}
template <class BigMatrixT, class Tensor>
std::vector<Real>
davidson(BigMatrixT const& A,
std::vector<Tensor>& phi,
Args const& args)
{
auto maxiter_ = args.getInt("MaxIter",2);
auto errgoal_ = args.getReal("ErrGoal",1E-4);
auto debug_level_ = args.getInt("DebugLevel",-1);
auto miniter_ = args.getInt("MinIter",1);
Real Approx0 = 1E-12;
auto nget = phi.size();
if(nget == 0) Error("No initial vectors passed to davidson.");
for(auto j : range(nget))
{
auto nrm = norm(phi[j]);
while(nrm == 0.0)
{
randomize(phi[j]);
nrm = norm(phi[j]);
}
phi[j] *= 1./nrm;
}
auto maxsize = A.size();
auto actual_maxiter = std::min(maxiter_,static_cast<decltype(maxiter_)>(maxsize)-1);
if(debug_level_ >= 2)
{
printfln("maxsize-1 = %d, maxiter = %d, actual_maxiter = %d",
(maxsize-1), maxiter_, actual_maxiter);
}
if(area(phi.front().inds()) != size_t(maxsize))
{
println("area(phi.front().inds()) = ",area(phi.front().inds()));
println("A.size() = ",A.size());
Error("davidson: size of initial vector should match linear matrix size");
}
auto V = std::vector<Tensor>(actual_maxiter+2);
auto AV = std::vector<Tensor>(actual_maxiter+2);
//Storage for Matrix that gets diagonalized
//set to NAN to ensure failure if we use uninitialized elements
auto M = CMatrix(actual_maxiter+2,actual_maxiter+2);
for(auto& el : M) el = Cplx(NAN,NAN);
auto NC = CVector(actual_maxiter+2);
//Mref holds current projection of A into V's
auto Mref = subMatrix(M,0,1,0,1);
//Get diagonal of A to use later
//auto Adiag = A.diag();
Real qnorm = NAN;
size_t t = 0; //which eigenvector we are currently targeting
Vector D;
CMatrix U;
Real last_lambda = 1000.;
auto eigs = std::vector<Real>(nget,NAN);
V[0] = phi.front();
A.product(V[0],AV[0]);
auto initEn = ((dag(V[0])*AV[0]).cplx()).real();
if(debug_level_ > 2)
printfln("Initial Davidson energy = %.10f",initEn);
int iter = 0;
for(int ii = 0; ii <= actual_maxiter; ++ii)
{
//Diagonalize dag(V)*A*V
//and compute the residual q
auto ni = ii+1;
auto& q = V.at(ni);
auto& phi_t = phi.at(t);
auto& lambda = eigs.at(t);
//Step A (or I) of Davidson (1975)
if(ii == 0)
{
lambda = initEn;
stdx::fill(Mref,lambda);
//Calculate residual q
q = AV[0] - lambda*V[0];
//printfln("ii=%d, q = \n%f",ii,q);
}
else // ii != 0
{
Mref *= -1;
diagHermitian(Mref,U,D);
Mref *= -1;
D *= -1;
lambda = D(t);
phi_t = U(0,t)*V[0];
q = U(0,t)*AV[0];
for(int k = 1; k <= ii; ++k)
{
phi_t += U(k,t)*V[k];
q += U(k,t)*AV[k];
}
//Step B of Davidson (1975)
//Calculate residual q
q += (-lambda)*phi_t;
//Fix sign
if(U(t,0).real() < 0)
{
phi_t *= -1;
q *= -1;
}
if(debug_level_ >= 3)
{
println("D = ",D);
printfln("lambda = %.10f",lambda);
}
//printfln("ii=%d, full q = \n%f",ii,q);
}
//Step C of Davidson (1975)
//Check convergence
qnorm = norm(q);
bool converged = (qnorm < errgoal_ && std::abs(lambda-last_lambda) < errgoal_)
|| qnorm < std::max(Approx0,errgoal_ * 1E-3);
last_lambda = lambda;
if((qnorm < 1E-20) || (converged && ii >= miniter_) || (ii == actual_maxiter))
{
if(t < (nget-1) && ii < actual_maxiter)
{
++t;
last_lambda = 1000.;
}
else
{
if(debug_level_ >= 3) //Explain why breaking out of Davidson loop early
{
if((qnorm < errgoal_ && std::fabs(lambda-last_lambda) < errgoal_))
printfln("Exiting Davidson because errgoal=%.0E reached",errgoal_);
else if(ii < miniter_ || qnorm < std::max(Approx0,errgoal_ * 1.0e-3))
printfln("Exiting Davidson because small residual=%.0E obtained",qnorm);
else if(ii == actual_maxiter)
println("Exiting Davidson because ii == actual_maxiter");
}
goto done;
}
}
if(debug_level_ >= 2 || (ii == 0 && debug_level_ >= 1))
{
printf("I %d q %.0E E",iter,qnorm);
for(auto eig : eigs)
{
if(std::isnan(eig)) break;
printf(" %.10f",eig);
}
println();
}
//Compute next trial vector by
//first applying Davidson preconditioner
//formula then orthogonalizing against
//other vectors
//Step D of Davidson (1975)
//Apply Davidson preconditioner
//
//TODO add preconditioner step (may require
//non-contracting product to do efficiently)
//
//if(Adiag)
// {
// //Function which applies the mapping
// // f(x,theta) = 1/(theta - x)
// auto precond = [theta=lambda.real()](Real val)
// {
// return (theta==val) ? 0 : 1./(theta-val);
// };
// auto cond= Adiag;
// cond.apply(precond);
// q /= cond;
// }
//Step E and F of Davidson (1975)
//Do Gram-Schmidt on d (Npass times)
//to include it in the subbasis
int Npass = 1;
auto Vq = std::vector<Cplx>(ni);
int step = 0;
for(auto pass : range1(Npass))
{
++step;
for(auto k : range(ni))
{
Vq[k] = (dag(V[k])*q).cplx();
}
for(auto k : range(ni))
{
q += (-Vq[k])*V[k];
}
auto qnrm = norm(q);
if(qnrm < 1E-10)
{
//Orthogonalization failure,
//try randomizing
if(debug_level_ >= 2)
println("Vector not independent, randomizing");
q = V.at(ni-1);
randomize(q);
if(ni >= maxsize)
{
//Not be possible to orthogonalize if
//max size of q (vecSize after randomize)
//is size of current basis
if(debug_level_ >= 3)
println("Breaking out of Davidson: max Hilbert space size reached");
goto done;
}
if(step > Npass * 3)
{
// Maybe the size of the matrix is only 1?
if(debug_level_ >= 3)
println("Breaking out of Davidson: orthog step too big");
goto done;
}
qnrm = norm(q);
--pass;
}
q *= 1./qnrm;
}
//Check V's are orthonormal
//Mat Vo(ni+1,ni+1,NAN);
//for(int r = 1; r <= ni+1; ++r)
//for(int c = r; c <= ni+1; ++c)
// {
// z = (dag(V[r-1])*V[c-1]).cplx();
// Vo(r,c) = abs(z);
// Vo(c,r) = Vo(r,c);
// }
//println("Vo = \n",Vo);
if(debug_level_ >= 3)
{
if(std::fabs(norm(q)-1.0) > 1E-10)
{
println("norm(q) = ",norm(q));
Error("q not normalized after Gram Schmidt.");
}
}
//Step G of Davidson (1975)
//Expand AV and M
//for next step
A.product(V[ni],AV[ni]);
//Step H of Davidson (1975)
//Add new row and column to M
Mref = subMatrix(M,0,ni+1,0,ni+1);
auto newCol = subVector(NC,0,1+ni);
for(int k = 0; k <= ni; ++k)
{
newCol(k) = (dag(V.at(k))*AV.at(ni)).cplx();
}
column(Mref,ni) &= newCol;
row(Mref,ni) &= conj(newCol);
++iter;
} //for(ii)
done:
for(auto& T : phi)
{
if(T.scale().logNum() > 2) T.scaleTo(1.);
}
//Compute any remaining eigenvalues and eigenvectors requested
//(zero indexed) value of t indicates how many have been "targeted" so far
for(size_t j = t+1; j < nget; ++j)
{
Error("Need to check this section for transpose diagSymmetric U convention");
eigs.at(j) = D(j);
auto& phi_j = phi.at(j);
auto Nr = nrows(U);
phi_j = U(0,t)*V[0];
for(auto k : range(Nr-1))
{
phi_j += U(k,1+t)*V[k];
}
}
if(debug_level_ >= 3)
{
//Check V's are orthonormal
auto Vo_final = CMatrix(iter+1,iter+1);
for(int r = 0; r < iter+1; ++r)
for(int c = r; c < iter+1; ++c)
{
auto z = (dag(V[r])*V[c]).cplx();
Vo_final(r,c) = std::abs(z);
Vo_final(c,r) = Vo_final(r,c);
}
println("Vo_final = \n",Vo_final);
}
if(debug_level_ > 0)
{
printf("I %d q %.0E E",iter,qnorm);
for(auto eig : eigs)
{
if(std::isnan(eig)) break;
printf(" %.10f",eig);
}
println();
}
return eigs;
}
} //namespace itensor
#endif