Skip to content

Time evolution of quench

Author Hannes Karlsson

In this example, we will first find the ground state for the free electron on a 2D lattice $$ H = -t \sum_{\langle ij,\sigma \rangle} c_{i\sigma}^\dagger c_{j\sigma} $$ then quench it with a Hubbard interaction, i.e., we will time evolve it with the full Hubbard Hamiltonian $$ H = -t \sum_{\langle ij,\sigma \rangle} c_{i\sigma}^\dagger c_{j\sigma} + U\sum_i n_{i\uparrow}n_{i\downarrow}. $$. As an observable, we will study the total double occupancy $$ \hat{O} = \sum_i \langle n_{i\uparrow}n_{i\downarrow} \rangle $$

Image title

We see that the double occupancy initially increases sharply, before going down again and entering an oscillatory behaviour.

#include <xdiag/all.hpp>

using namespace xdiag;

int main() {
  say_hello();
  int Lx = 4;
  int Ly = 4;

  // construct Hamiltonian
  auto ops = OpSum();
  for (int x = 0; x < Lx; x++) {
    for (int y = 0; y < Ly; y++) {
      int s = x * Ly + y;
      int sx = ((x + 1) % Lx) * Ly + y;
      int sy = x * Ly + (y + 1) % Ly;

      ops += "t" * Op("Hop", {s, sx}); // hopping in x-direction
      ops += "t" * Op("Hop", {s, sy}); // hopping in y-direction
    }
  }
  ops += "U" * Op("HubbardU");

  ops["t"] = -1.0;
  ops["U"] = 0.0;

  int nup = Lx * Ly / 2;
  int ndn = Lx * Ly / 2;
  auto block = Electron(Lx * Ly, nup, ndn);

  auto [e0, psi] = eig0(ops, block);

  ops["U"] = -10.0;

  auto obs = OpSum();
  obs += Op("HubbardU");

  arma::vec times = arma::linspace(0, 100, 2);
  arma::vec obs_vec = arma::vec(times.size());

  for (int i = 0; i < times.size(); i++) {
    time_evolve_inplace(ops, psi, times[1] - times[0]);
    obs_vec[i] = inner(obs, psi);
  }

  // save and plot

  return 0;
}
using XDiag
using CairoMakie

"""Struct for lattice sites"""
struct LatticeBond
    s1::Int64
    x1::Float64
    y1::Float64
    s2::Int64
    x2::Float64
    y2::Float64
end

"""Returns list of all bonds on the square lattice"""
function square_lattice(Lx::Int,Ly::Int;xperiodic=true,yperiodic=true)

    bonds = Vector{LatticeBond}()

    for x1 in 1:Lx, y1 in 1:Ly
        s1 = (x1-1)*Ly+y1
        for (x2,y2) in [( mod(x1,Lx)+1, y1 ),( x1, mod(y1,Ly)+1 )]
            s2 = (x2-1)*Ly+y2
            set = reduce(hcat,[[s1,x1,y1],[s2,x2,y2]])
            set = set[:,sortperm(set[1,:])]     # sort according to s1 and s2
            b = LatticeBond(set[:,1]...,set[:,2]...)
            push!(bonds,b)
        end
    end
    return bonds
end

"""Returns the Hamiltonian for the Hubbard model"""
function ham(U::Float64,Lx::Int64,Ly::Int64)

    lattice = square_lattice(Lx,Ly)
    ops = OpSum()

    for b in lattice # hopping term
        ops += "t" * Op("Hop", [b.s1,b.s2])     # both for spin up and down
    end

    ops += "U" * Op("HubbardU")     # applies Hubbard interaction over the entire lattice
    for i in 1:Lx*Ly
        ops += "mu" * Op("Ntot",i)     # chemical potental
    end

    ops["t"] = 1.0
    ops["U"] = U
    #ops["mu"] = -U/2
    ops["mu"] = 0.0

    return ops

end

let
    U = 0.0
    Lx,Ly = (4,3)
    N = Lx*Ly
        # ground state for U
    ops = ham(U,Lx,Ly)
    hspace = Electron(N, N ÷ 2, N ÷ 2)

    e,psi = eig0(ops,hspace)

    U = -10.0
    ops = ham(U,Lx,Ly)
    dt = 0.1
    time = 10

    times = range(dt,time,length=Int(time/dt))
    obs_vec = Array{Float64}(undef,length(times))

    obs = OpSum()
    obs += Op("HubbardU")

    for i in 1:length(times)
        time_evolve_inplace(ops,psi,float(dt))
        # do measurements
        obs_vec[i] = real(inner(obs,psi))
    end

    f = Figure()
    ax = Axis(f[1,1],
        xlabel=L"time, $t$",
        ylabel=L"$\sum_i \langle n_{i\uparrow} n_{i\downarrow} \rangle $"
        )


    lines!(ax,times,obs_vec)
    save("../plots/quench/Lx($Lx)_Ly($Ly).png",f)
end