## Learn to Use ITensor

main / formulas / tevol_trotter C++v2 | C++v3

# Time Evolving an MPS with Trotter Gates

A very accurate, efficient, and simple way to time evolve a matrix product state (MPS) is by using a Trotter decomposition of the time evolution operator.

Although the discussion below focuses on the case of a nearest-neighbor one-dimensional Hamiltonian, the method can be extended to Hamiltonians with arbitrary finite-range interactions by using swap gates to temporarily exchange sites. (For information about using swap gates, see New J. Phys. 12, 055026.)

If the Hamiltonian is a sum of local terms

$$H = \sum_j h_{j,j+1}$$

where $h_{j,j+1}$ only acts non-trivially on sites j and (j+1), then a Trotter decomposition that is particularly well suited for use with MPS techniques is

$$e^{-i \tau H} \approx e^{-i h_{1,2} \tau/2} e^{-i h_{2,3} \tau/2} \cdots e^{-i h_{N-1,N} \tau/2} e^{-i h_{N-1,N} \tau/2} e^{-i h_{N-2,N-1} \tau/2} \cdots e^{-i h_{1,2} \tau/2} + O(\tau^2)$$

Note the factors of two in each exponential. The error in the above decomposition is of order $\tau^2$ , so this will be the error accumulated per time step. Because of the time-step error, one takes $\tau$ to be small and then applies the above set of operators to an MPS as a single sweep, then does a number $(t/\tau)$ of sweeps to evolve for a total time $t$ .

The same decomposition can be used for imaginary time evolution just by replacing $i \tau \rightarrow \tau$ .

Below is a fully working code that applies the above ideas to time evolve an MPS which is initially a simple product state with the Heisenberg Hamiltonian.

The BondGate class has a constructor that accepts a local Hamiltonian term as a tensor, and automatically exponentiates it to make a Trotter gate with the specified time step. The gateTEvol function is a helper function that handles the logic of applying a container of gates to an MPS the right number of times and with the appropriate truncation of the MPS after each step.

#include "itensor/all.h"

using namespace itensor;
using std::vector;

int main()
{
int N = 50; //number of sites
Real tstep = 0.02; //time step (smaller is generally more accurate)
Real ttotal = 1.0; //total time to evolve
Real cutoff = 1E-8; //truncation error cutoff when restoring MPS form

//Define a site set object "sites" which lets us
//easily obtain Site indices defining our Hilbert space
//and S=1/2 single-site operators
auto sites = SpinHalf(N);

//Make initial MPS psi to be in the Neel state
auto state = InitState(sites);
for(auto j : range1(N))
{
state.set(j,j%2==1?"Up":"Dn");
}
auto psi = MPS(state);

//Define the type "Gate" as a shorthand for BondGate<ITensor>
using Gate = BondGate<ITensor>;

//Create a std::vector (dynamically sizeable array)
//to hold the Trotter gates
auto gates = vector<Gate>();

//Create the gates exp(-i*tstep/2*hterm)
for(int b = 1; b <= N-1; ++b)
{
auto hterm = sites.op("Sz",b)*sites.op("Sz",b+1);
hterm += 0.5*sites.op("S+",b)*sites.op("S-",b+1);
hterm += 0.5*sites.op("S-",b)*sites.op("S+",b+1);

auto g = Gate(sites,b,b+1,Gate::tReal,tstep/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 = sites.op("Sz",b)*sites.op("Sz",b+1);
hterm += 0.5*sites.op("S+",b)*sites.op("S-",b+1);
hterm += 0.5*sites.op("S-",b)*sites.op("S+",b+1);

auto g = Gate(sites,b,b+1,Gate::tReal,tstep/2.,hterm);
gates.push_back(g);
}

//Save initial state;
auto psi0 = psi;

//Time evolve, overwriting psi when done
gateTEvol(gates,ttotal,tstep,psi,{"Cutoff=",cutoff,"Verbose=",true});

printfln("Maximum MPS bond dimension after time evolution is %d",maxM(psi));

//Print overlap of final state with initial state
//(Will be complex so using overlapC which can return complex);
Print(overlapC(psi,psi0));

return 0;
}


Back to Formulas
Back to Main