https://github.com/ITensor/ITensor
Tip revision: e2c6ea98c5d68dee7ad38c16a7f71574a46589e0 authored by Miles Stoudenmire on 20 August 2015, 19:47:53 UTC
Shortened some type names in cplx_literal.h and names of constants in global.h
Shortened some type names in cplx_literal.h and names of constants in global.h
Tip revision: e2c6ea9
mpo.h
//
// Distributed under the ITensor Library License, Version 1.1.
// (See accompanying LICENSE file.)
//
#ifndef __ITENSOR_MPO_H
#define __ITENSOR_MPO_H
#include "mps.h"
#include "sweeps.h"
namespace itensor {
static const Real DefaultLogRefScale(2.0255);
template<class Tensor> class MPOt;
using MPO = MPOt<ITensor>;
using IQMPO = MPOt<IQTensor>;
//
// class MPOt
//
// (defines MPO and IQMPO via typedefs)
//
template<class Tensor>
class MPOt : private MPSt<Tensor>
{
public:
using Parent = MPSt<Tensor>;
using TensorT = Tensor;
using IndexT = typename Tensor::IndexT;
using IndexValT = typename Tensor::IndexValT;
using CombinerT = typename Tensor::CombinerT;
//MPOt: Constructors -----------------------------------------
MPOt();
MPOt(const SiteSet& sites,
Real _refNorm = DefaultLogRefScale);
//Accessor Methods ------------------------------
using Parent::N;
using Parent::sites;
using Parent::valid;
explicit operator bool() const { return valid(); }
using Parent::rightLim;
using Parent::leftLim;
using Parent::A;
using Parent::Anc;
using Parent::doWrite;
using Parent::read;
using Parent::write;
//MPOt: operators ------------------------------------------------------
MPOt&
operator*=(Real a) { Parent::operator*=(a); return *this; }
MPOt
operator*(Real r) const { MPOt res(*this); res *= r; return res; }
friend MPOt inline
operator*(Real r, MPOt res) { res *= r; return res; }
MPOt&
operator*=(Complex z) { Parent::operator*=(z); return *this; }
MPOt
operator*(Complex z) const { MPOt res(*this); res *= z; return res; }
friend MPOt inline
operator*(Complex z, MPOt res) { res *= z; return res; }
MPOt&
plusEq(const MPOt& R,
const Args& args = Global::args());
MPOt<ITensor>
toMPO() const;
MPOt<IQTensor>
toIQMPO() const;
//MPOt: index methods --------------------------------------------------
using Parent::mapprime;
using Parent::primelinks;
using Parent::noprimelink;
void
primeall() // sites i,i' -> i',i''; link: l -> l'
{
for(int i = 1; i <= this->N(); i++)
{
Anc(i).mapprime(0,1,Link);
Anc(i).mapprime(1,2,Site);
Anc(i).mapprime(0,1,Site);
}
}
void
svdBond(int b, const Tensor& AA, Direction dir, const Args& args = Global::args())
{
Parent::svdBond(b,AA,dir,args + Args("UseSVD",true,"LogRefNorm",logrefNorm_));
}
//Move the orthogonality center to site i
//(l_orth_lim_ = i-1, r_orth_lim_ = i+1)
void
position(int i, const Args& args = Global::args()) { Parent::position(i,args + Args("UseSVD")); }
void
orthogonalize(const Args& args = Global::args()) { Parent::orthogonalize(args + Args("UseSVD")); }
using Parent::isOrtho;
using Parent::orthoCenter;
using Parent::isComplex;
void
toIQ(QN totalq, MPOt<IQTensor>& res, Real cut = 1E-12) const
{
res = MPOt<IQTensor>(*sites_,logrefNorm_);
convertToIQ(*sites_,A_,res.A_,totalq,cut);
}
private:
///////////
using Parent::N_;
using Parent::A_;
using Parent::l_orth_lim_;
using Parent::r_orth_lim_;
using Parent::sites_;
Real logrefNorm_;
///////////
MPOt&
addAssumeOrth(const MPOt& oth, const Args& args = Global::args())
{
Parent::addAssumeOrth(oth,args+Args("UseSVD",true,"LogRefNorm",logrefNorm_,"DoRelCutoff",true));
return *this;
}
friend class MPOt<ITensor>;
friend class MPOt<IQTensor>;
}; //class MPOt<Tensor>
template <> inline
MPO IQMPO::
toMPO() const
{
MPO res(*sites_,logrefNorm_);
for(int j = 0; j <= N()+1; ++j)
{
res.A_.at(j) = A(j).toITensor();
}
return res;
}
//toMPO method fails unless template class
//Tensor is set to IQTensor (object is an IQMPO)
template<class Tensor>
MPO MPOt<Tensor>::
toMPO() const
{
Error("toMPO only implemented for class IQMPO");
return MPO();
}
template <> inline
IQMPO MPO::
toIQMPO() const
{
IQMPO res(*sites_,logrefNorm_);
convertToIQ(*sites_,A_,res.A_);
return res;
}
//toMPO method fails unless template class
//Tensor is set to IQTensor (object is an IQMPO)
template<class Tensor>
IQMPO MPOt<Tensor>::
toIQMPO() const
{
Error("toIQMPO only implemented for class MPO");
return IQMPO();
}
int
findCenter(const IQMPO& psi);
void inline
checkQNs(const MPO& psi) { }
void
checkQNs(const IQMPO& psi);
template <class Tensor>
MPOt<Tensor>
sum(const MPOt<Tensor>& L,
const MPOt<Tensor>& R,
const Args& args = Global::args())
{
MPOt<Tensor> res(L);
res.plusEq(R,args+Args("UseSVD",true,"DoRelCutoff",true));
return res;
}
template <class Tensor>
void
psiHphi(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const MPSt<Tensor>& phi, Real& re, Real& im) //<psi|H|phi>
{
const int N = H.N();
if(phi.N() != N || psi.N() != N) Error("psiHphi: mismatched N");
Tensor L = phi.A(1);
//Some Hamiltonians may store edge tensors in H.A(0) and H.A(N+1)
L *= (H.A(0) ? H.A(0)*H.A(1) : H.A(1));
L *= dag(prime(psi.A(1)));
for(int i = 2; i < N; ++i)
{
L *= phi.A(i);
L *= H.A(i);
L *= dag(prime(psi.A(i)));
}
L *= phi.A(N);
L *= H.A(N);
if(H.A(N+1)) L *= H.A(N+1);
Complex z = BraKet(prime(psi.A(N)),L);
re = z.real();
im = z.imag();
}
template <class Tensor>
Real
psiHphi(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const MPSt<Tensor>& phi) //Re[<psi|H|phi>]
{
Real re, im;
psiHphi(psi,H,phi,re,im);
if(std::fabs(im) > 1.0e-12 * std::fabs(re))
printfln("\nReal psiHphi: WARNING, dropping non-zero (=%.5E) imaginary part of expectation value.",im);
return re;
}
template <class Tensor>
Complex
psiHphiC(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const MPSt<Tensor>& phi) //Re[<psi|H|phi>]
{
Real re, im;
psiHphi(psi,H,phi,re,im);
return Complex(re,im);
}
template<class Tensor>
void
psiHphi(const MPSt<Tensor>& psi,
const MPOt<Tensor>& H,
const Tensor& LB,
const Tensor& RB,
const MPSt<Tensor>& phi,
Real& re,
Real& im) //<psi|H|phi>
{
int N = psi.N();
if(N != phi.N() || H.N() < N) Error("mismatched N in psiHphi");
Tensor L = (LB ? LB*phi.A(1) : phi.A(1));
L *= H.A(1);
L *= dag(prime(psi.A(1)));
for(int i = 2; i <= N; ++i)
{
L *= phi.A(i);
L *= H.A(i);
L *= dag(prime(psi.A(i)));
}
if(RB) L *= RB;
Complex z = L.toComplex();
re = z.real();
im = z.imag();
}
template <class Tensor>
Real
psiHphi(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const Tensor& LB, const Tensor& RB, const MPSt<Tensor>& phi) //Re[<psi|H|phi>]
{
Real re,im; psiHphi(psi,H,LB,RB,phi,re,im);
if(std::fabs(im) > 1.0e-12 * std::fabs(re))
printfln("Real psiHphi: WARNING, dropping non-zero imaginary part (=%.5E) of expectation value.",im);
return re;
}
template <class Tensor>
void
psiHKphi(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const MPOt<Tensor>& K,const MPSt<Tensor>& phi, Real& re, Real& im) //<psi|H K|phi>
{
if(psi.N() != phi.N() || psi.N() != H.N() || psi.N() != K.N()) Error("Mismatched N in psiHKphi");
int N = psi.N();
MPSt<Tensor> psidag(psi);
for(int i = 1; i <= N; i++)
{
psidag.Anc(i) = dag(psi.A(i));
psidag.Anc(i).mapprime(0,2);
}
MPOt<Tensor> Kp(K);
Kp.mapprime(1,2);
Kp.mapprime(0,1);
//scales as m^2 k^2 d
Tensor L = (((phi.A(1) * H.A(1)) * Kp.A(1)) * psidag.A(1));
for(int i = 2; i < N; i++)
{
//scales as m^3 k^2 d + m^2 k^3 d^2
L = ((((L * phi.A(i)) * H.A(i)) * Kp.A(i)) * psidag.A(i));
}
//scales as m^2 k^2 d
L = ((((L * phi.A(N)) * H.A(N)) * Kp.A(N)) * psidag.A(N));
//cout << "in psiHKpsi, L is "; PrintData(L);
Complex z = L.toComplex();
re = z.real();
im = z.imag();
}
template <class Tensor>
Real
psiHKphi(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const MPOt<Tensor>& K,const MPSt<Tensor>& phi) //<psi|H K|phi>
{
Real re,im;
psiHKphi(psi,H,K,phi,re,im);
if(std::fabs(im) > 1.0e-12 * std::fabs(re))
Error("Non-zero imaginary part in psiHKphi");
return re;
}
template <class Tensor>
Complex
psiHKphiC(const MPSt<Tensor>& psi, const MPOt<Tensor>& H, const MPOt<Tensor>& K,const MPSt<Tensor>& phi) //<psi|H K|phi>
{
Real re,im;
psiHKphi(psi,H,K,phi,re,im);
return Complex(re,im);
}
template <class MPOType>
void
nmultMPO(const MPOType& Aorig, const MPOType& Borig, MPOType& res,
const Args& args = Global::args());
//
// Applies an MPO to an MPS using the zip-up method described
// more fully in Stoudenmire and White, New. J. Phys. 12, 055026 (2010).
//
// This method applies the MPO to an MPS one site at a time,
// with the new MPS being calculated at each step via an
// SVD of the MPO-MPS product.
//
// Uses cutoff and max of MPS psi unless specified.
//
template<class Tensor>
void
zipUpApplyMPO(const MPSt<Tensor>& psi,
const MPOt<Tensor>& K,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Applies an MPO K to an MPS x with no approximation (|res>=K|x>)
//The bond dimension of res will be the product of bond dimensions
//of x and K.
template<class Tensor>
void
exactApplyMPO(const MPSt<Tensor>& x,
const MPOt<Tensor>& K,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Applies an MPO K to an MPS psi (|res>=K|psi>) using a sweeping/DMRG-like
//fitting approach. Warning: this method can get stuck i.e. fail to converge
//if the initial value of res is too different from the product K|psi>.
//List of options recognized:
// Nsweep (default: 1) - number of sweeps to use
// Maxm (default: res.maxm()) - maximum number of states to keep
// Minm (default: res.minm()) - minimum number of states to keep
// Cutoff (default: res.cutoff()) - maximum truncation error goal
template<class Tensor>
void
fitApplyMPO(const MPSt<Tensor>& psi,
const MPOt<Tensor>& K,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Applies an MPO K to an MPS psi including an overall scalar factor (|res>=fac*K|psi>)
//using a sweeping/DMRG-like fitting approach.
//Warning: this method can get stuck i.e. fail to converge
//if the initial value of res is too different from the product fac*K|psi>.
// Nsweep (default: 1) - number of sweeps to use
// Maxm (default: res.maxm()) - maximum number of states to keep
// Minm (default: res.minm()) - minimum number of states to keep
// Cutoff (default: res.cutoff()) - maximum truncation error goal
template<class Tensor>
void
fitApplyMPO(Real fac,
const MPSt<Tensor>& psi,
const MPOt<Tensor>& K,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Applies an MPO K to an MPS psi including an overall scalar factor (|res>=fac*K|psi>)
//using a sweeping/DMRG-like fitting approach.
//Warning: this method can get stuck i.e. fail to converge
//if the initial value of res is too different from the product fac*K|psi>.
//Try setting noise > 0 in the Sweeps argument to overcome this.
//Arguments recognized:
// "Verbose" (default: false): print out extra information
// "Normalize" (default: true): normalize the result MPS "res" at every step
//
template<class Tensor>
void
fitApplyMPO(Real fac,
const MPSt<Tensor>& psi,
const MPOt<Tensor>& K,
MPSt<Tensor>& res,
const Sweeps& sweeps,
Args args);
//Computes |res> = |psiA> + mpofac*H*|psiB>
//using a sweeping/DMRG-like fitting approach.
//Warning: this method can get stuck i.e. fail to converge
//if the initial value of res is too different from desired exact result.
// Nsweep (default: 1) - number of sweeps to use
// Maxm (default: res.maxm()) - maximum number of states to keep
// Minm (default: res.minm()) - minimum number of states to keep
// Cutoff (default: res.cutoff()) - maximum truncation error goal
template<class Tensor>
Real
fitApplyMPO(const MPSt<Tensor>& psiA,
Real mpofac,
const MPSt<Tensor>& psiB,
const MPOt<Tensor>& H,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Computes |res> = mpsfac*|psiA> + mpofac*H*|psiB>
//using a sweeping/DMRG-like fitting approach.
//Warning: this method can get stuck i.e. fail to converge
//if the initial value of res is too different from desired exact result.
// Nsweep (default: 1) - number of sweeps to use
// Maxm (default: res.maxm()) - maximum number of states to keep
// Minm (default: res.minm()) - minimum number of states to keep
// Cutoff (default: res.cutoff()) - maximum truncation error goal
template<class Tensor>
Real
fitApplyMPO(Real mpsfac,
const MPSt<Tensor>& psiA,
Real mpofac,
const MPSt<Tensor>& psiB,
const MPOt<Tensor>& H,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Computes the exponential of the MPO H: K=exp(-tau*(H-Etot))
template<class Tensor>
void
expH(const MPOt<Tensor>& H, MPOt<Tensor>& K, Real tau, Real Etot,
Real Kcutoff, int ndoub, const Args& args = Global::args());
//
//Approximately computes |res> = exp(-tau*H)|psi>.
//Works by expanding the exponential to order n.
//The default order is n=4 but can be increased via the "Order" argument.
//E.g. applyExpH(H,tau,psi,res,{"Order",n});
//List of named arguments recognized:
// "Order" : order of Taylor series expansion of exp(-tau*H)
// "Cutoff": maximum truncation error allowed
// "Maxm" : maximum number of states after truncation
// "Minm" : minimum number of states after truncation
// "Nsweep": number of sweeps used to apply H MPO to intermediate MPS
//
template<class Tensor>
void
applyExpH(const MPSt<Tensor>& psi,
const MPOt<Tensor>& H,
Real tau,
MPSt<Tensor>& res,
const Args& args = Global::args());
//Given an MPO with no Link indices between site operators,
//put in links (of bond dimension 1).
//In the IQMPO case ensure that links carry the proper QNs.
void
putMPOLinks(MPO& W, const Args& args = Global::args());
void
putMPOLinks(IQMPO& W, const Args& args = Global::args());
template <class Tensor>
std::ostream&
operator<<(std::ostream& s, const MPOt<Tensor>& M);
} //namespace itensor
#endif