I am comparing the two methods for time-evolution.
First, I write the Hamiltonian in MPO form using autoMPO, and then time evolve using toExpH and applyMPO.
Secondly, I implement a TEBD type algorithm by creating a sequence of two-site gates using BondGate and time evolving with gateTEvol.
However, both give very different results for the dynamics for a Fermi-Hubbard model. I have tested this for a spin-1/2 chain and this works fine, I am wondering if the trotter gate method does not properly take into account the fermionic statistics?
Here is sample code:
#include "itensor/all.h"
using namespace itensor;
int main(int argc, char* argv[])
{
int BD = 1024;
int t1 = 1;
int N = 20;
auto sites = Electron(N);
auto state = InitState(sites);
int p=1;
for(int i = N; i >= 1; --i)
{
if(p == 1)
{
state.set(i,"Up");
p = 2;
}
else
{
state.set(i,"Dn");
p = 1;
}
}
auto psi = MPS(state);
//
// Create the Hamiltonian using AutoMPO
//
auto ampo = AutoMPO(sites);
for(int b = 1; b < N; ++b)
{
ampo += -t1,"Cdagup",b,"Cup",b+1;
ampo += -t1,"Cup",b,"Cdagup",b+1;
ampo += -t1,"Cdagdn",b,"Cdn",b+1;
ampo += -t1,"Cdn",b,"Cdagdn",b+1;
}
auto H = toMPO(ampo);
auto tau = 0.01;
auto expH = toExpH(ampo,tau*Cplx_i);
auto ttotal = 1.0;
auto nt = int(ttotal/tau+(1e-9*(ttotal/tau)));
std::ofstream ofGup("DATA/MPO_TE_Test.txt");
for(int n = 1; n <= nt; ++n)
{
cpu_time sw_time;
psi = applyMPO(expH,psi,{"Method=","DensityMatrix","MaxDim=",BD,"Cutoff=",1E-15});
psi.noPrime().normalize();
double updo;
for(int j = 1; j <= N; ++j)
{
psi.position(j);
updo = (eltC(dag(prime(psi(j),"Site"))*op(sites,"Nup",j)*psi(j))).real();
printfln(ofGup,"%d ",updo);
}
printfln("MPO: Time %d",n*tau);
printfln("Maximum MPS bond dimension is %d",maxLinkDim(psi));
auto sm = sw_time.sincemark();
printfln("CPU time = %s ",showtime(sm.time));
}
auto sites2 = Electron(N);
auto state2 = InitState(sites2);
p=1;
for(int i = N; i >= 1; --i)
{
if(p == 1)
{
state2.set(i,"Up");
p = 2;
}
else
{
state2.set(i,"Dn");
p = 1;
}
}
auto psi_TEBD = MPS(state2);
Print(totalQN(psi_TEBD));
auto gates = vector<BondGate>();
//Create the gates exp(-i*tstep/2*hterm)
//and add them to gates
for(int b = 1; b <= N-1; ++b)
{
auto hterm = -t1*op(sites2,"Cdagup",b)*op(sites2,"Cup",b+1);
hterm += -t1*op(sites2,"Cup",b)*op(sites2,"Cdagup",b+1);
hterm += -t1*op(sites2,"Cdagdn",b)*op(sites2,"Cdn",b+1);
hterm += -t1*op(sites2,"Cdn",b)*op(sites2,"Cdagdn",b+1);
auto g = BondGate(sites2,b,b+1,BondGate::tReal,tau/2.,hterm);
gates.push_back(g);
}
//Create the gates exp(-i*tstep/2*hterm) in reverse
//order (to get a second order Trotter breakup which
//does a time step of "tstep") and add them to gates
for(int b = N-1; b >= 1; --b)
{
auto hterm = -t1*op(sites2,"Cdagup",b)*op(sites2,"Cup",b+1);
hterm += -t1*op(sites2,"Cup",b)*op(sites2,"Cdagup",b+1);
hterm += -t1*op(sites2,"Cdagdn",b)*op(sites2,"Cdn",b+1);
hterm += -t1*op(sites2,"Cdn",b)*op(sites2,"Cdagdn",b+1);
auto g = BondGate(sites2,b,b+1,BondGate::tReal,tau/2.,hterm);
// auto g = BondGate(sites,b,b+1);
gates.push_back(g);
}
Real cutoff = 1E-15; //truncation error cutoff when restoring MPS form
std::ofstream ofGup2("DATA/TEBD_TE_Test.txt");
for(int n = 1; n <= nt; ++n)
{
cpu_time sw_time;
//Time evolve, overwriting psi when done
gateTEvol(gates,tau,tau,psi_TEBD,{"Cutoff=",cutoff,"Verbose=",true,"MaxDim=",BD});
psi_TEBD.noPrime().normalize();
double updo;
for(int j = 1; j <= N; ++j)
{
psi_TEBD.position(j);
updo = (eltC(dag(prime(psi_TEBD(j),"Site"))*op(sites2,"Nup",j)*psi_TEBD(j))).real();
printfln(ofGup2,"%d ",updo);
}
printfln("Time %d",n*tau);
printfln("Maximum MPS bond dimension is %d",maxLinkDim(psi_TEBD));
auto sm = sw_time.sincemark();
printfln("CPU time = %s ",showtime(sm.time));
}
return 0;
}
What have I done wrong??
Note that this is not an issue with the time-step or bond dimension.
Thanks,
Stuart