Skip to content

Tower Of States attractive Hubbard model

Author Hannes Karlsson

We perform a tower of states (TOS) analysis [1],[2] on the attractive Hubbard model on a square lattice. The (particle-hole symmetric) model can be written as $$ \mathcal{H} = -t\sum_{\langle i j \rangle} c_i^{\dagger} c_j - U\sum_{i} (n_{i\uparrow}-\frac{1}{2})(n_{i\downarrow}-\frac{1}{2}). $$

The attractive Hubbard model is known to display a superconducting ground state, which in mean-field theory is well described by a fermionic U\((1)\) coherent state $$ \ket{\psi} = \prod_{k} (u_k + v_k c_{k\uparrow}^\dagger c_{-k\downarrow}^\dagger)\ket{0}, $$ i.e, the ground state is degenerate in the number of particle pairs. We thus expect that in the superfluid state, in the neighbourhood of the most favourable particle number (determined by the chemical potential), there will be a tower of almost degenerate energy states, containing different number of superconducting pairs, but otherwise the same.

To perform the TOS analysis, we gather the eigenvalues using either full exact diagonalization (ED) or the Lanczos algorithm in each symmetry sector, defined by the particle number. Here we use the Lanczos algorithm with \(3\) eigenvalues well converged.

Image title

As we can see in the spectra above (computed for a \(4\times 4\) lattice), for large enough values of \(U\), the ground state energy is almost degenerate in the different particle number sectors, providing strong evidence for U\((1)\) spontaneous symmetry breaking.

#include <xdiag/all.hpp>

using namespace xdiag;

int main() try {
  say_hello();
  int Lx = 4;
  int Ly = 4;
  int Nmin = 4;
  int Nmax = 2 * Lx * Ly - 4;
  std::vector<double> Us = {0.0, -2.0, -10.0};

  for (double U : Us) {

    // Construct Hamiltonian
    auto ops = OpSum();

    ops += "U" * Op("HubbardU"); // apply Hubbard interaction to all sites

    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 += "mu" * Op("Ntot", s);     // chemical potential
      }
    }
    ops["t"] = 1;
    ops["mu"] = -U / 2;
    ops["U"] = U;

    for (int N = Nmin; N <= Nmax; N += 2) {
      int nup = N / 2;
      int ndn = N / 2;
      auto block = Electron(Lx * Ly, nup, ndn); // create Hilbert space

      auto res = eigs_lanczos(ops, block, 3);

      std::string outfile = fmt::format(
          "data/tos_ahm/U({})_N({})_Lx({})_Ly({}).h5", U, N, Lx, Ly);
      auto f = FileH5(outfile, "w!");
      f["spectrum"] = res.eigenvalues;
    }
  }
  return 0;
} catch (Error e) {
  error_trace(e);
}
using XDiag
using HDF5

"""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 potential
    end

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

    return ops

end

function main(U::Float64,Lx::Int64,Ly::Int64,Ns::StepRange{Int64, Int64})

    for N in Ns

        ops = ham(U,Lx,Ly) # create the Hamiltonian
        nup = N%2 == 0 ? N ÷ 2 : (N+1) ÷ 2
        ndn = N%2 == 0 ? N ÷ 2 : (N-1) ÷ 2

        block = Electron(Lx*Ly, nup, ndn)   # create Hilbert space
        res = eigs_lanczos(ops, block, neigvals = 3)
        eigs = res.eigenvalues

        filename = "data/tos_ahm/U($U)_N($N)_Lx($Lx)_Ly($Ly).h5"

        h5open(filename,"w") do f
            write(f,"spectrum",eigs)
        end

    end

end

Us = [0.0,-2.0,-10.0]
Lx, Ly = (4,4)
Nmin = 4
Nmax = 2*Lx*Ly - 4
Ns = Nmin:2:Nmax

say_hello()
for U in Us
    println("Now doing U=$U")
    main(U,Lx,Ly,Ns)
end

references

[1] P. W. Anderson, An Approximate Quantum Theory of the Antiferromagnetic Ground State, Phys. Rev. 86, 694 (1952)

[3] Alexander Wietek, Michael Schuler and Andreas M. Läuchli, Studying Continuous Symmetry Breaking using Energy Level Spectroscopy, arXiv:1704.08622 (2017).