Example: TDVP

Time-evolution with time dependent variational principle (TDVP).

A straight-forward TDVP (with dynamical sweeps) can be performed as follows. Note that TDVP computes ψ' = exp(time_step * H) * ψ. Therefore, for real-time dynamics with step dt, time_step should be -im * dt.

using ITensors
using TenNetLib

let
    N = 32
    sites = siteinds("S=1/2",N)
    os = OpSum()
    
    for j=1:N-1
        os += 1, "Sz", j, "Sz", j+1
        os += 0.5, "S+", j, "S-", j+1
        os += 0.5, "S-", j, "S+", j+1
    end
    
    H = MPO(os,sites)
    states = [isodd(n) ? "Up" : "Dn" for n in 1:N]
    psi0 = MPS(sites, states)

    tau = -0.01im    
    engine = TDVPEngine(psi0, H)
    for ii = 1:100

    	# `nsite = "dynamic"` for dynamical selection between
	# single- and two-site variants at different bonds
        tdvpsweep!(engine, tau,
                   nsite = "dynamic";
                   maxdim = 200,
                   cutoff = 1E-12,
                   extendat = 5)

	# Errors in the last sweep and the total error till this point
	swerr = sweeperror(engine)
	totalerr = totalerror(engine)
	
	psi = getpsi(engine)
	
	# DO STUFF
    end
end

Here, we have used OpSum and MPO. Alternatively, standard OpStrings and CouplingModel can be used.

Optionally, one can use pure single- or two-site updates. If the TDVPEngine is created with a single MPO then Global Subspace Expansion can be performed.

for ii = 1:100

    # GSE at every 5th sweep.
    if ii % 5 == 1
        krylov_extend!(engine)
    end
    
    # `nsite = 1` for single-site update
    tdvpsweep!(engine, tau,
               nsite = 1;
               maxdim = 200,
               cutoff = 1E-12)

    # Errors in the last sweep and the total error till this point
    swerr = sweeperror(engine)
    totalerr = totalerror(engine)
	
    psi = getpsi(engine)
	
    # DO STUFF
end

Time-evolution with time-dependent Hamiltonian

For time-dependent Hamiltonian, updateH! can be used.

using ITensors
using TenNetLib

function makeHt(sites, t)

    os = OpSum()    
    for j=1:N-1
        os += 1, "Sz", j, "Sz", j+1
        os += 0.5, "S+", j, "S-", j+1
        os += 0.5, "S-", j, "S+", j+1
    end
    for j=1:N
        os += t, "Sz", j
    end
    return MPO(os,sites)
end

let
    N = 32
    sites = siteinds("S=1/2",N)

    states = [isodd(n) ? "Up" : "Dn" for n in 1:N]
    psi0 = MPS(sites, states)

    dt = 0.01
    tau = -im * dt

    H0 = makeHt(sites, 0.0)
    engine = TDVPEngine(psi0, H0)
    
    for ii = 1:100

    	# `nsite = "dynamic"` for dynamical selection between
	# single- and two-site variants at different bonds
        tdvpsweep!(engine, tau,
                   nsite = "dynamic";
                   maxdim = 200,
                   cutoff = 1E-12,
                   extendat = 5)

	# Errors in the last sweep and the total error till this point
	swerr = sweeperror(engine)
	totalerr = totalerror(engine)
	
	psi = getpsi(engine)
	
	# DO STUFF

	# update Hamiltonian for the next iteration
	Ht = makeHt(sites, ii*dt)
	updateH!(engine, Ht)
    end
end

Note 1: The above example for time-dependent Hamiltonian is very crude. In real situations, the update in the Hamiltonian should be done with proper care.

Note 2: When the TDVPEngine is created from a single MPO and dt is small, one can use recalcEnv = false in updateH!, so that environments from the last step is reused.