Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions src/hubbardoperators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module HubbardOperators
using TensorKit

export hubbard_space
export e_num, u_num, d_num, ud_num
export e_num, u_num, d_num, ud_num, half_ud_num
export S_x, S_y, S_z, S_plus, S_min
export u_plus_u_min, d_plus_d_min
export u_min_u_plus, d_min_d_plus
Expand Down Expand Up @@ -56,15 +56,17 @@ function hubbard_space(::Type{U1Irrep}, ::Type{SU2Irrep})
)
end
function hubbard_space(::Type{SU2Irrep}, ::Type{Trivial})
return Vect[FermionParity ⊠ SU2Irrep]((0, 0) => 2, (1, 1 // 2) => 1)
return Vect[FermionParity ⊠ SU2Irrep]((0, 1 // 2) => 1, (1, 0) => 2)
end
function hubbard_space(::Type{SU2Irrep}, ::Type{U1Irrep})
return Vect[FermionParity ⊠ SU2Irrep ⊠ U1Irrep](
(0, 0, 0) => 1, (1, 1 // 2, 1) => 1, (0, 0, 2) => 1
(0, 1 // 2, 0) => 1, (1, 0, -1 // 2) => 1, (1, 0, 1 // 2) => 1
)
end
function hubbard_space(::Type{SU2Irrep}, ::Type{SU2Irrep})
return Vect[FermionParity ⊠ SU2Irrep ⊠ SU2Irrep]((1, 1 // 2, 1 // 2) => 1)
return Vect[FermionParity ⊠ SU2Irrep ⊠ SU2Irrep](
(0, 1 // 2, 0) => 1, (1, 0, 1 // 2) => 1
)
end

# Single-site operators
Expand Down Expand Up @@ -239,6 +241,27 @@ function ud_num(elt::Type{<:Number}, ::Type{U1Irrep}, ::Type{SU2Irrep})
end
const nꜛꜜ = ud_num

@doc """
half_ud_num([elt::Type{<:Number}], [particle_symmetry::Type{<:Sector}], [spin_symmetry::Type{<:Sector}])

Return the the one-body operator that is equivalent to `(nꜛ - 1/2)(nꜜ - 1/2)`, which respects the particle-hole symmetry.
"""
half_ud_num(P::Type{<:Sector}, S::Type{<:Sector}) = half_ud_num(ComplexF64, P, S)
function half_ud_num(
elt::Type{<:Number}, particle_symmetry::Type{<:Sector},
spin_symmetry::Type{<:Sector}
)
I = id(hubbard_space(particle_symmetry, spin_symmetry))
return (u_num(elt, particle_symmetry, spin_symmetry) - I / 2) *
(d_num(elt, particle_symmetry, spin_symmetry) - I / 2)
end
function half_ud_num(elt::Type{<:Number}, ::Type{SU2Irrep}, ::Type{SU2Irrep})
t = single_site_operator(elt, SU2Irrep, SU2Irrep)
block(t, sectortype(t)(0, 1 // 2, 0)) .= 1 // 4
block(t, sectortype(t)(1, 0, 1 // 2)) .= -1 // 4
return t
end

@doc """
S_plus(elt::Type{<:Number}, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector})
S⁺(elt::Type{<:Number}, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector})
Expand Down Expand Up @@ -586,6 +609,22 @@ function e_hopping(
return e_plus_e_min(elt, particle_symmetry, spin_symmetry) -
e_min_e_plus(elt, particle_symmetry, spin_symmetry)
end
function e_hopping(elt::Type{<:Number}, ::Type{SU2Irrep}, ::Type{SU2Irrep})
elt <: Complex || throw(DomainError(elt, "SU₂ × SU₂ symmetry requires complex entries"))
t = two_site_operator(elt, SU2Irrep, SU2Irrep)
I = sectortype(t)
even = I(0, 1 // 2, 0)
odd = I(1, 0, 1 // 2)
f1 = only(fusiontrees((odd, odd), one(I)))
f2 = only(fusiontrees((even, even), one(I)))
t[f1, f2] .= 2im
t[f2, f1] .= -2im
f3 = only(fusiontrees((even, odd), I((1, 1 // 2, 1 // 2))))
f4 = only(fusiontrees((odd, even), I((1, 1 // 2, 1 // 2))))
t[f3, f4] .= im
t[f4, f3] .= -im
return t
end
const e_hop = e_hopping

@doc """
Expand Down
78 changes: 63 additions & 15 deletions test/hubbardoperators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,26 @@ using StableRNGs
implemented_symmetries = [
(Trivial, Trivial), (Trivial, U1Irrep), (Trivial, SU2Irrep),
(U1Irrep, Trivial), (U1Irrep, U1Irrep), (U1Irrep, SU2Irrep),
(SU2Irrep, SU2Irrep),
]

@testset "Compare symmetric with trivial tensors" begin
for particle_symmetry in [Trivial, U1Irrep, SU2Irrep],
spin_symmetry in [Trivial, U1Irrep, SU2Irrep]

if (particle_symmetry, spin_symmetry) in implemented_symmetries
space = hubbard_space(particle_symmetry, spin_symmetry)
space = @inferred hubbard_space(particle_symmetry, spin_symmetry)

if particle_symmetry == spin_symmetry == SU2Irrep
O = e_hopping(ComplexF64, SU2Irrep, SU2Irrep)
O_triv = e_hopping(ComplexF64, Trivial, Trivial)
test_operator(O, O_triv)

O = half_ud_num(ComplexF64, SU2Irrep, SU2Irrep)
O_triv = half_ud_num(ComplexF64, Trivial, Trivial)
test_operator(O, O_triv)
continue
end

O = e_plus_e_min(ComplexF64, particle_symmetry, spin_symmetry)
O_triv = e_plus_e_min(ComplexF64, Trivial, Trivial)
Expand Down Expand Up @@ -50,6 +62,7 @@ end
@test dim(space) == 4

if (particle_symmetry, spin_symmetry) in implemented_symmetries
particle_symmetry == spin_symmetry == SU2Irrep && continue
# test hopping operator
epem = e_plus_e_min(particle_symmetry, spin_symmetry)
emep = e_min_e_plus(particle_symmetry, spin_symmetry)
Expand Down Expand Up @@ -168,61 +181,96 @@ end
end
end

function hubbard_hamiltonian(particle_symmetry, spin_symmetry; t, U, mu, L)
hopping = -t * e_hopping(particle_symmetry, spin_symmetry)
interaction = U * ud_num(particle_symmetry, spin_symmetry)
chemical_potential = -mu * e_num(particle_symmetry, spin_symmetry)
function hubbard_hamiltonian(particle_symmetry, spin_symmetry; t, U, mu)
L = length(t) + 1
@assert length(t) + 1 == length(U) == length(mu)
hopping = e_hopping(particle_symmetry, spin_symmetry)
interaction = ud_num(particle_symmetry, spin_symmetry)
chemical_potential = e_num(particle_symmetry, spin_symmetry)
I = id(hubbard_space(particle_symmetry, spin_symmetry))
H = sum(1:(L - 1)) do i
return reduce(⊗, insert!(collect(Any, fill(I, L - 2)), i, hopping))
return reduce(⊗, insert!(collect(Any, fill(I, L - 2)), i, hopping * -t[i]))
end +
sum(1:L) do i
return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, interaction))
return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, interaction * U[i]))
end +
sum(1:L) do i
return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, chemical_potential))
return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, chemical_potential * -mu[i]))
end
return H
end
function hubbard_hamiltonian(::Type{SU2Irrep}, ::Type{SU2Irrep}; t, U, mu = U ./ 2)
L = length(t) + 1
@assert length(t) + 1 == length(U) == length(mu)
@assert mu ≈ U / 2
hopping = e_hopping(SU2Irrep, SU2Irrep)
interaction = half_ud_num(SU2Irrep, SU2Irrep)
I = id(hubbard_space(SU2Irrep, SU2Irrep))
H = sum(1:(L - 1)) do i
return reduce(⊗, insert!(collect(Any, fill(I, L - 2)), i, hopping * -t[i]))
end + sum(1:L) do i
return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, interaction * U[i]))
end
return H
end

@testset "spectrum" begin
L = 4
t = randn()
U = randn()
mu = randn()
t = randn(L - 1)
U = randn(L)
mu = randn(L)

H_triv = hubbard_hamiltonian(Trivial, Trivial; t, U, mu, L)
H_triv = hubbard_hamiltonian(Trivial, Trivial; t, U, mu)
vals_triv = mapreduce(vcat, eigvals(H_triv)) do (c, v)
return repeat(real.(v), dim(c))
end
sort!(vals_triv)

for (particle_symmetry, spin_symmetry) in implemented_symmetries
if (particle_symmetry, spin_symmetry) == (Trivial, Trivial)
if particle_symmetry == spin_symmetry == Trivial ||
particle_symmetry == spin_symmetry == SU2Irrep
continue
end
H_symm = hubbard_hamiltonian(particle_symmetry, spin_symmetry; t, U, mu, L)
H_symm = hubbard_hamiltonian(particle_symmetry, spin_symmetry; t, U, mu)
vals_symm = mapreduce(vcat, eigvals(H_symm)) do (c, v)
return repeat(real.(v), dim(c))
end
sort!(vals_symm)
@test vals_triv ≈ vals_symm
end

mu = U ./ 2
H_triv = hubbard_hamiltonian(Trivial, Trivial; t, U, mu)
vals_triv = mapreduce(vcat, eigvals(H_triv)) do (c, v)
return repeat(real.(v), dim(c)) .+ sum(U) / 4
end
sort!(vals_triv)

H_symm = hubbard_hamiltonian(SU2Irrep, SU2Irrep; t, U, mu)
vals_symm = mapreduce(vcat, eigvals(H_symm)) do (c, v)
return repeat(real.(v), dim(c))
end
sort!(vals_symm)
@test vals_triv ≈ vals_symm
end

@testset "Exact diagonalisation" begin
for particle_symmetry in [Trivial, U1Irrep, SU2Irrep],
spin_symmetry in [Trivial, U1Irrep, SU2Irrep]

if (particle_symmetry, spin_symmetry) in implemented_symmetries
particle_symmetry == spin_symmetry == SU2Irrep && continue
rng = StableRNG(123)

L = 2
t, U = rand(rng, 5)
mu = 0.0
E⁻ = U / 2 - sqrt((U / 2)^2 + 4 * t^2)
E⁺ = U / 2 + sqrt((U / 2)^2 + 4 * t^2)
H_triv = hubbard_hamiltonian(particle_symmetry, spin_symmetry; t, U, mu, L)
H_triv = hubbard_hamiltonian(
particle_symmetry, spin_symmetry;
t = fill(t, L - 1), U = fill(U, L), mu = fill(mu, L)
)

# Values based on https://arxiv.org/pdf/0807.4878. Introduction to Hubbard Model and Exact Diagonalization
true_eigenvals = sort(
Expand Down
Loading