From e533f14fa7a5dc0db2a78a8efdf83eaa3a7bfb44 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 13:02:56 +0100 Subject: [PATCH 01/24] add tj model This is mainly a copy from MPSKitModels, with a renaming of the operators to be consistent with the Hubbard operators as defined here. --- src/TensorKitTensors.jl | 1 + src/tjoperators.jl | 632 ++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 4 + test/tjoperators.jl | 164 +++++++++++ 4 files changed, 801 insertions(+) create mode 100644 src/tjoperators.jl create mode 100644 test/tjoperators.jl diff --git a/src/TensorKitTensors.jl b/src/TensorKitTensors.jl index 064c453..0a5de57 100644 --- a/src/TensorKitTensors.jl +++ b/src/TensorKitTensors.jl @@ -7,5 +7,6 @@ export HubbardOperators include("spinoperators.jl") include("bosonoperators.jl") include("hubbardoperators.jl") +include("tjoperators.jl") end diff --git a/src/tjoperators.jl b/src/tjoperators.jl new file mode 100644 index 0000000..87a6e1c --- /dev/null +++ b/src/tjoperators.jl @@ -0,0 +1,632 @@ +#= Operators that act on t-J-type models +i.e. the local hilbert space consists of + +- usual basis states: + |∅⟩, |↑⟩, |↓⟩ +- slave-fermion basis states (c_σ = h† b_σ; holon h is fermionic, spinon b_σ is bosonic): + |h⟩ = h†|∅⟩, |↑'⟩ = (b↑)†|∅⟩, |↓'⟩ = (b↓)†|∅⟩ +=# +module TJOperators + +using TensorKit + +export tj_space +export c_plus_c_min, u_plus_u_min, d_plus_d_min +export c_min_c_plus, u_min_u_plus, d_min_d_plus +export u_min_d_min, d_min_u_min, c_singlet +export c_num, u_num, d_num, c_num_hole +export S_x, S_y, S_z, S_plus, S_min +export S_plusmin, S_minplus, S_exchange + +export c⁺c⁻, u⁺u⁻, d⁺d⁻, c⁻c⁺, u⁻u⁺, d⁻d⁺, u⁻d⁻, d⁻u⁻ +export nꜛ, nꜜ, nʰ +export Sˣ, Sʸ, Sᶻ, S⁺, S⁻, S⁻⁺, S⁺⁻ +# not exported because namespace: export n + +""" + tj_space(particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the local hilbert space for a t-J-type model with the given particle and spin symmetries. +The possible symmetries are +- Particle number: `Trivial`, `U1Irrep`; +- Spin: `Trivial`, `U1Irrep`, `SU2Irrep`. + +Setting `slave_fermion = true` switches to the slave-fermion basis. +""" +function tj_space(::Type{Trivial}=Trivial, ::Type{Trivial}=Trivial; + slave_fermion::Bool=false) + return slave_fermion ? Vect[FermionParity](0 => 2, 1 => 1) : + Vect[FermionParity](0 => 1, 1 => 2) +end +function tj_space(::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + return if slave_fermion + Vect[FermionParity ⊠ U1Irrep]((1, 0) => 1, (0, 1 // 2) => 1, (0, -1 // 2) => 1) + else + Vect[FermionParity ⊠ U1Irrep]((0, 0) => 1, (1, 1 // 2) => 1, (1, -1 // 2) => 1) + end +end +function tj_space(::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +function tj_space(::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + return if slave_fermion + Vect[FermionParity ⊠ U1Irrep]((1, 0) => 1, (0, 1) => 2) + else + Vect[FermionParity ⊠ U1Irrep]((0, 0) => 1, (1, 1) => 2) + end +end +function tj_space(::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + return if slave_fermion + Vect[FermionParity ⊠ U1Irrep ⊠ U1Irrep]((1, 0, 0) => 1, (0, 1, 1 // 2) => 1, + (0, 1, -1 // 2) => 1) + else + Vect[FermionParity ⊠ U1Irrep ⊠ U1Irrep]((0, 0, 0) => 1, (1, 1, 1 // 2) => 1, + (1, 1, -1 // 2) => 1) + end +end +function tj_space(::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end + +function single_site_operator(T, particle_symmetry::Type{<:Sector}, + spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + V = tj_space(particle_symmetry, spin_symmetry; slave_fermion) + return zeros(T, V ← V) +end + +function two_site_operator(T, particle_symmetry::Type{<:Sector}, + spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + V = tj_space(particle_symmetry, spin_symmetry; slave_fermion) + return zeros(T, V ⊗ V ← V ⊗ V) +end + +""" + u_plus_u_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e†_{1,↑}, e_{2,↑}`` that creates a spin-up electron at the first site and annihilates a spin-up electron at the second. +The only nonzero matrix element corresponds to `|↑0⟩ <-- |0↑⟩`. +""" +function u_plus_u_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_plus_u_min(ComplexF64, P, S; slave_fermion) +end +function u_plus_u_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + #= The extra minus sign in slave-fermion basis: + c†_{1,↑} c_{2,↑} |0↑⟩ + = h_1 b†_{1,↑} h†_2 b_{2,↑} h†_1 b†_{2,↑}|vac⟩ + = -b†_{1,↑} h†_2 h_1 h†_1 b_{2,↑} b†_{2,↑}|vac⟩ + = -b†_{1,↑} h†_2 |vac⟩ + = -|↑0⟩ + =# + t[(I(b), I(h), dual(I(h)), dual(I(b)))][1, 1, 1, 1] = sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1 // 2), I(h, 0), dual(I(h, 0)), dual(I(b, 1 // 2)))] .= sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1), I(h, 0), dual(I(h, 0)), dual(I(b, 1)))][1, 1, 1, 1] = sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1, 1 // 2), I(h, 0, 0), dual(I(h, 0, 0)), dual(I(b, 1, 1 // 2)))] .= sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +const u⁺u⁻ = u_plus_u_min + +""" + d_plus_d_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e†_{1,↓}, e_{2,↓}`` that creates a spin-down electron at the first site and annihilates a spin-down electron at the second. +The only nonzero matrix element corresponds to `|↓0⟩ <-- |0↓⟩`. +""" +function d_plus_d_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_plus_d_min(ComplexF64, P, S; slave_fermion) +end +function d_plus_d_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b), I(h), dual(I(h)), dual(I(b)))][2, 1, 1, 2] = sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, -1 // 2), I(h, 0), dual(I(h, 0)), dual(I(b, -1 // 2)))] .= sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1), I(h, 0), dual(I(h, 0)), dual(I(b, 1)))][2, 1, 1, 2] = sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1, -1 // 2), I(h, 0, 0), dual(I(h, 0, 0)), dual(I(b, 1, -1 // 2)))] .= sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +const d⁺d⁻ = d_plus_d_min + +""" + u_min_u_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the Hermitian conjugate of `u_plus_u_min`, i.e. +``(e†_{1,↑}, e_{2,↑})† = -e_{1,↑}, e†_{2,↑}`` (note the extra minus sign). +It annihilates a spin-up electron at the first site and creates a spin-up electron at the second. +The only nonzero matrix element corresponds to `|0↑⟩ <-- |↑0⟩`. +""" +function u_min_u_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_min_u_plus(ComplexF64, P, S; slave_fermion) +end +function u_min_u_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const u⁻u⁺ = u_min_u_plus + +""" + d_min_d_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the Hermitian conjugate of `d_plus_d_min`, i.e. +``(e†_{1,↓}, e_{2,↓})† = -e_{1,↓}, e†_{2,↓}`` (note the extra minus sign). +It annihilates a spin-down electron at the first site and creates a spin-down electron at the second. +The only nonzero matrix element corresponds to `|0↓⟩ <-- |↓0⟩`. +""" +function d_min_d_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_min_d_plus(ComplexF64, P, S; slave_fermion) +end +function d_min_d_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const d⁻d⁺ = d_min_d_plus + +""" + c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator that creates a particle at the first site and annihilates a particle at the second. +This is the sum of `u_plus_u_min` and `d_plus_d_min`. +""" +function c_plus_c_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_plus_c_min(ComplexF64, P, S; slave_fermion) +end +function c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion) + + d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) +end +const c⁺c⁻ = c_plus_c_min + +""" + c_min_c_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator that annihilates a particle at the first site and creates a particle at the second. +This is the sum of `u_min_u_plus` and `d_min_d_plus`. +""" +function c_min_c_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_min_c_plus(ComplexF64, P, S; slave_fermion) +end +function c_min_c_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(c_plus_c_min(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const c⁻c⁺ = c_min_c_plus + +""" + u_min_d_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e_{1,↑} e_{2,↓}`` that annihilates a spin-up particle at the first site and a spin-down particle at the second site. +The only nonzero matrix element corresponds to `|00⟩ <-- |↑↓⟩`. +""" +function u_min_d_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_min_d_min(ComplexF64, P, S; slave_fermion) +end +function u_min_d_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h), I(h), dual(I(b)), dual(I(b)))][1, 1, 1, 2] = -sgn * 1 + return t +end +function u_min_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h, 0), I(h, 0), dual(I(b, 1 // 2)), dual(I(b, -1 // 2)))] .= -sgn * 1 + return t +end +function u_min_d_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) + throw(ArgumentError("`u_min_d_min` is not symmetric under `U1Irrep` particle symmetry")) +end +const u⁻d⁻ = u_min_d_min + +""" + d_min_u_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e_{1,↓} e_{2,↑}`` that annihilates a spin-down particle at the first site and a spin-up particle at the second site. +The only nonzero matrix element corresponds to `|00⟩ <-- |↓↑⟩`. +""" +function d_min_u_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_min_u_min(ComplexF64, P, S; slave_fermion) +end +function d_min_u_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h), I(h), dual(I(b)), dual(I(b)))][1, 1, 2, 1] = -sgn * 1 + return t +end +function d_min_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h, 0), I(h, 0), dual(I(b, -1 // 2)), dual(I(b, 1 // 2)))] .= -sgn * 1 + return t +end +function d_min_u_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) + throw(ArgumentError("`d_min_u_min` is not symmetric under `U1Irrep` particle symmetry")) +end +const d⁻u⁻ = d_min_u_min +""" + c_singlet(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body singlet operator ``(e_{1,↓} e_{2,↑} - e_{1,↓} e_{2,↑}) / sqrt(2)``. +""" +function c_singlet(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_singlet(ComplexF64, P, S; slave_fermion) +end +function c_singlet(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return (u_min_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) - + d_min_u_min(T, particle_symmetry, spin_symmetry; slave_fermion)) / sqrt(2) +end + +""" + u_num(particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the one-body operator that counts the number of spin-up electrons. +""" +function u_num(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_num(ComplexF64, P, S; slave_fermion) +end +function u_num(T::Type{<:Number}, ::Type{Trivial}, ::Type{Trivial}; + slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b), dual(I(b)))][1, 1] = 1 + return t +end +function u_num(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1 // 2), dual(I(b, 1 // 2)))][1, 1] = 1 + return t +end +function u_num(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`u_num` is not symmetric under `SU2Irrep` spin symmetry")) +end +function u_num(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1), dual(I(b, 1)))][1, 1] = 1 + return t +end +function u_num(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1, 1 // 2), dual(I(b, 1, 1 // 2)))] .= 1 + return t +end +function u_num(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`u_num` is not symmetric under `SU2Irrep` spin symmetry")) +end +const nꜛ = u_num + +""" + d_num(particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the one-body operator that counts the number of spin-down electrons. +""" +function d_num(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_num(ComplexF64, P, S; slave_fermion) +end +function d_num(T::Type{<:Number}, ::Type{Trivial}, ::Type{Trivial}; + slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b), dual(I(b)))][2, 2] = 1 + return t +end +function d_num(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, -1 // 2), dual(I(b, -1 // 2)))][1, 1] = 1 + return t +end +function d_num(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`d_num` is not symmetric under `SU2Irrep` spin symmetry")) +end +function d_num(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1), dual(I(b, 1)))][2, 2] = 1 + return t +end +function d_num(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1, -1 // 2), dual(I(b, 1, -1 // 2)))] .= 1 + return t +end +function d_num(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`d_num` is not symmetric under `SU2Irrep` spin symmetry")) +end +const nꜜ = d_num + +""" + c_num(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the one-body operator that counts the number of particles. +""" +function c_num(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_num(ComplexF64, P, S; slave_fermion) +end +function c_num(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return u_num(T, particle_symmetry, spin_symmetry; slave_fermion) + + d_num(T, particle_symmetry, spin_symmetry; slave_fermion) +end +const n = c_num + +""" + c_num_hole(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the one-body operator that counts the number of holes. +""" +function c_num_hole(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_num_hole(ComplexF64, P, S; slave_fermion) +end +function c_num_hole(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + iden = TensorKit.id(tj_space(particle_symmetry, spin_symmetry; slave_fermion)) + return iden - c_num(T, particle_symmetry, spin_symmetry; slave_fermion) +end +const nʰ = c_num_hole + +""" + S_x(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the one-body spin-1/2 x-operator on the electrons. +""" +function S_x(P::Type{<:Sector}=Trivial, S::Type{<:Sector}=Trivial; + slave_fermion::Bool=false) + return S_x(ComplexF64, P, S; slave_fermion) +end +function S_x(T::Type{<:Number}, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b), dual(I(b)))][1, 2] = 0.5 + t[(I(b), dual(I(b)))][2, 1] = 0.5 + return t +end +function S_x(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1), dual(I(b, 1)))][1, 2] = 0.5 + t[(I(b, 1), dual(I(b, 1)))][2, 1] = 0.5 + return t +end +const Sˣ = S_x + +""" + S_y(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the one-body spin-1/2 x-operator on the electrons (only defined for `Trivial` symmetry). +""" +function S_y(P::Type{<:Sector}=Trivial, S::Type{<:Sector}=Trivial; + slave_fermion::Bool=false) + return S_y(ComplexF64, P, S; slave_fermion) +end +function S_y(T::Type{<:Number}, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b), dual(I(b)))][1, 2] = -0.5im + t[(I(b), dual(I(b)))][2, 1] = 0.5im + return t +end +function S_y(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1), dual(I(b, 1)))][1, 2] = -0.5im + t[(I(b, 1), dual(I(b, 1)))][2, 1] = 0.5im + return t +end +const Sʸ = S_y + +""" + S_z(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the one-body spin-1/2 z-operator on the electrons. +""" +function S_z(P::Type{<:Sector}=Trivial, S::Type{<:Sector}=Trivial; + slave_fermion::Bool=false) + return S_z(ComplexF64, P, S; slave_fermion) +end +function S_z(T::Type{<:Number}, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b), dual(I(b)))][1, 1] = 0.5 + t[(I(b), dual(I(b)))][2, 2] = -0.5 + return t +end +function S_z(T::Type{<:Number}, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1 // 2), dual(I(b, 1 // 2)))] .= 0.5 + t[(I(b, -1 // 2), dual(I(b, -1 // 2)))] .= -0.5 + return t +end +function S_z(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1), dual(I(b, 1)))][1, 1] = 0.5 + t[(I(b, 1), dual(I(b, 1)))][2, 2] = -0.5 + return t +end +function S_z(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1, 1 // 2), dual(I(b, 1, 1 // 2)))] .= 0.5 + t[(I(b, 1, -1 // 2), dual(I(b, 1, -1 // 2)))] .= -0.5 + return t +end +const Sᶻ = S_z + +""" + S_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the spin-plus operator. +""" +function S_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return S_plus(ComplexF64, P, S; slave_fermion) +end +function S_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return S_x(T, particle_symmetry, spin_symmetry; slave_fermion) + + 1im * S_y(T, particle_symmetry, spin_symmetry; slave_fermion) +end +const S⁺ = S_plus + +""" + S_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + +Return the spin-minus operator. +""" +function S_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return S_min(ComplexF64, P, S; slave_fermion) +end +function S_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return S_x(T, particle_symmetry, spin_symmetry; slave_fermion) - + 1im * S_y(T, particle_symmetry, spin_symmetry; slave_fermion) +end +const S⁻ = S_min + +""" + S_plusmin(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator S⁺S⁻. +The only nonzero matrix element corresponds to `|↑↓⟩ <-- |↓↑⟩`. +""" +function S_plusmin(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return S_plusmin(ComplexF64, P, S; slave_fermion) +end +function S_plusmin(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b), I(b), dual(I(b)), dual(I(b)))][1, 2, 2, 1] = 1 + return t +end +function S_plusmin(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1 // 2), I(b, -1 // 2), dual(I(b, -1 // 2)), dual(I(b, 1 // 2)))] .= 1 + return t +end +function S_plusmin(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1), I(b, 1), dual(I(b, 1)), dual(I(b, 1)))][1, 2, 2, 1] = 1 + return t +end +function S_plusmin(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + b = slave_fermion ? 0 : 1 + t[(I(b, 1, 1 // 2), I(b, 1, -1 // 2), dual(I(b, 1, -1 // 2)), dual(I(b, 1, 1 // 2)))] .= 1 + return t +end +const S⁺⁻ = S_plusmin + +""" + S_minplus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator S⁻S⁺. +The only nonzero matrix element corresponds to `|↓↑⟩ <-- |↑↓⟩`. +""" +function S_minplus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return S_minplus(ComplexF64, P, S; slave_fermion) +end +function S_minplus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(S_plusmin(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const S⁻⁺ = S_minplus + +""" + S_exchange(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the spin exchange operator S⋅S. +""" +function S_exchange(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return S_exchange(ComplexF64, P, S; slave_fermion) +end +function S_exchange(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + Sz = S_z(T, particle_symmetry, spin_symmetry; slave_fermion) + return (1 / 2) * (S_plusmin(T, particle_symmetry, spin_symmetry; slave_fermion) + + + S_minplus(T, particle_symmetry, spin_symmetry; slave_fermion)) + + Sz ⊗ Sz +end + +end diff --git a/test/runtests.jl b/test/runtests.jl index 7795177..0fdbff3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,10 @@ using SafeTestsets include("hubbardoperators.jl") end +@time @safetestset "tj operators" begin + include("tjoperators.jl") +end + @time @safetestset "spin operators" begin include("spinoperators.jl") end diff --git a/test/tjoperators.jl b/test/tjoperators.jl new file mode 100644 index 0000000..c898e17 --- /dev/null +++ b/test/tjoperators.jl @@ -0,0 +1,164 @@ +using TensorKit +using LinearAlgebra: tr, eigvals +using Test +include("testsetup.jl") +using .TensorKitTensorsTestSetup +using TensorKitTensors.TJOperators +using StableRNGs + +implemented_symmetries = [(Trivial, Trivial), (Trivial, U1Irrep), + (U1Irrep, Trivial), (U1Irrep, U1Irrep)] + +@testset "Compare symmetric with trivial tensors" begin + for particle_symmetry in [Trivial, U1Irrep], + spin_symmetry in [Trivial, U1Irrep, SU2Irrep] + + if (particle_symmetry, spin_symmetry) in implemented_symmetries + space = tj_space(particle_symmetry, spin_symmetry) + + O = c_plus_c_min(ComplexF64, particle_symmetry, spin_symmetry) + O_triv = c_plus_c_min(ComplexF64, Trivial, Trivial) + test_operator(O, O_triv) + + O = c_num(ComplexF64, particle_symmetry, spin_symmetry) + O_triv = c_num(ComplexF64, Trivial, Trivial) + test_operator(O, O_triv) + + else + @test_broken c_plus_c_min(ComplexF64, particle_symmetry, spin_symmetry) + @test_broken c_num(ComplexF64, particle_symmetry, spin_symmetry) + end + end +end + +@testset "basic properties" begin + for slave_fermion in (false, true) + for particle_symmetry in (Trivial, U1Irrep, SU2Irrep), + spin_symmetry in (Trivial, U1Irrep, SU2Irrep) + + if (particle_symmetry, spin_symmetry) in implemented_symmetries + # test hermiticity + @test c_plus_c_min(particle_symmetry, spin_symmetry; slave_fermion)' ≈ + c_min_c_plus(particle_symmetry, spin_symmetry; slave_fermion) + if spin_symmetry !== SU2Irrep + @test d_plus_d_min(particle_symmetry, spin_symmetry; slave_fermion)' ≈ + d_min_d_plus(particle_symmetry, spin_symmetry; slave_fermion) + @test u_plus_u_min(particle_symmetry, spin_symmetry; slave_fermion)' ≈ + u_min_u_plus(particle_symmetry, spin_symmetry; slave_fermion) + @test d_plus_d_min(particle_symmetry, spin_symmetry; slave_fermion)' ≈ + d_min_d_plus(particle_symmetry, spin_symmetry; slave_fermion) + @test u_plus_u_min(particle_symmetry, spin_symmetry; slave_fermion)' ≈ + u_min_u_plus(particle_symmetry, spin_symmetry; slave_fermion) + end + + # test number operator + if spin_symmetry !== SU2Irrep + @test c_num(particle_symmetry, spin_symmetry; slave_fermion) ≈ + u_num(particle_symmetry, spin_symmetry; slave_fermion) + + d_num(particle_symmetry, spin_symmetry; slave_fermion) + @test u_num(particle_symmetry, spin_symmetry; slave_fermion) * + d_num(particle_symmetry, spin_symmetry; slave_fermion) ≈ + d_num(particle_symmetry, spin_symmetry; slave_fermion) * + u_num(particle_symmetry, spin_symmetry; slave_fermion) + end + + # test spin operator + if spin_symmetry == Trivial + ε = zeros(ComplexF64, 3, 3, 3) + for i in 1:3 + ε[mod1(i, 3), mod1(i + 1, 3), mod1(i + 2, 3)] = 1 + ε[mod1(i, 3), mod1(i - 1, 3), mod1(i - 2, 3)] = -1 + end + Svec = [S_x(particle_symmetry, spin_symmetry; slave_fermion), + S_y(particle_symmetry, spin_symmetry; slave_fermion), + S_z(particle_symmetry, spin_symmetry; slave_fermion)] + # Hermiticity + for s in Svec + @test s' ≈ s + end + # operators should be normalized + S = 1 / 2 + @test sum(tr(Svec[i]^2) for i in 1:3) / (2S + 1) ≈ S * (S + 1) + # test S_plus and S_min + @test S_plusmin(particle_symmetry, spin_symmetry; slave_fermion) ≈ + S_plus(particle_symmetry, spin_symmetry; slave_fermion) ⊗ + S_min(particle_symmetry, spin_symmetry; slave_fermion) + # commutation relations + for i in 1:3, j in 1:3 + @test Svec[i] * Svec[j] - Svec[j] * Svec[i] ≈ + sum(im * ε[i, j, k] * Svec[k] for k in 1:3) + end + end + else + @test_broken c_plus_c_min(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken c_min_c_plus(particle_symmetry, spin_symmetry; slave_fermion) + end + end + end +end + +function hubbard_hamiltonian(particle_symmetry, spin_symmetry; t, U, mu, L) + hopping = t * (c_plus_c_min(particle_symmetry, spin_symmetry) + + c_min_c_plus(particle_symmetry, spin_symmetry)) + chemical_potential = mu * c_num(particle_symmetry, spin_symmetry) + I = id(tj_space(particle_symmetry, spin_symmetry)) + H = sum(1:(L - 1)) do i + return reduce(⊗, insert!(collect(Any, fill(I, L - 2)), i, hopping)) + end + + sum(1:L) do i + return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, chemical_potential)) + end + return H +end + +function tjhamiltonian(particle_symmetry, spin_symmetry; t, J, mu, L, slave_fermion) + num = c_num(particle_symmetry, spin_symmetry; slave_fermion) + hop_heis = (-t) * (c_plus_c_min(particle_symmetry, spin_symmetry; slave_fermion) + + c_min_c_plus(particle_symmetry, spin_symmetry; slave_fermion)) + + J * + (S_exchange(particle_symmetry, spin_symmetry; slave_fermion) - + (1 / 4) * (num ⊗ num)) + chemical_potential = (-mu) * num + I = id(tj_space(particle_symmetry, spin_symmetry; slave_fermion)) + H = sum(1:(L - 1)) do i + return reduce(⊗, insert!(collect(Any, fill(I, L - 2)), i, hop_heis)) + end + sum(1:L) do i + return reduce(⊗, insert!(collect(Any, fill(I, L - 1)), i, chemical_potential)) + end + return H +end + +@testset "spectrum" begin + L = 4 + t = randn() + J = randn() + mu = randn() + + for slave_fermion in (false, true) + H_triv = tjhamiltonian(Trivial, Trivial; t, J, mu, L, slave_fermion) + vals_triv = mapreduce(vcat, eigvals(H_triv)) do (c, v) + return repeat(real.(v), dim(c)) + end + sort!(vals_triv) + + for particle_symmetry in (Trivial, U1Irrep), + spin_symmetry in (Trivial, U1Irrep, SU2Irrep) + + if (particle_symmetry, spin_symmetry) in implemented_symmetries + if (particle_symmetry, spin_symmetry) == (Trivial, Trivial) + continue + end + H_symm = tjhamiltonian(particle_symmetry, spin_symmetry; t, J, mu, L, + slave_fermion) + 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 + else + @test_broken tjhamiltonian(particle_symmetry, spin_symmetry; t, J, mu, L, + slave_fermion) + end + end + end +end From cd3b30b2c0cbe926e76122c242041177345297d3 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 13:45:21 +0100 Subject: [PATCH 02/24] increase patch coverage --- test/tjoperators.jl | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/test/tjoperators.jl b/test/tjoperators.jl index c898e17..b2d7458 100644 --- a/test/tjoperators.jl +++ b/test/tjoperators.jl @@ -33,8 +33,8 @@ end @testset "basic properties" begin for slave_fermion in (false, true) - for particle_symmetry in (Trivial, U1Irrep, SU2Irrep), - spin_symmetry in (Trivial, U1Irrep, SU2Irrep) + for particle_symmetry in [Trivial, U1Irrep], + spin_symmetry in [Trivial, U1Irrep, SU2Irrep] if (particle_symmetry, spin_symmetry) in implemented_symmetries # test hermiticity @@ -49,6 +49,15 @@ end d_min_d_plus(particle_symmetry, spin_symmetry; slave_fermion) @test u_plus_u_min(particle_symmetry, spin_symmetry; slave_fermion)' ≈ u_min_u_plus(particle_symmetry, spin_symmetry; slave_fermion) + else + @test_broken d_plus_d_min(particle_symmetry, spin_symmetry; + slave_fermion) + @test_broken d_min_d_plus(particle_symmetry, spin_symmetry; + slave_fermion) + @test_broken u_plus_u_min(particle_symmetry, spin_symmetry; + slave_fermion) + @test_broken u_min_u_plus(particle_symmetry, spin_symmetry; + slave_fermion) end # test number operator @@ -60,6 +69,10 @@ end d_num(particle_symmetry, spin_symmetry; slave_fermion) ≈ d_num(particle_symmetry, spin_symmetry; slave_fermion) * u_num(particle_symmetry, spin_symmetry; slave_fermion) + else + @test_broken c_num(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken u_num(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken d_num(particle_symmetry, spin_symmetry; slave_fermion) end # test spin operator @@ -90,8 +103,13 @@ end end end else - @test_broken c_plus_c_min(particle_symmetry, spin_symmetry; slave_fermion) - @test_broken c_min_c_plus(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken d_plus_d_min(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken d_min_d_plus(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken u_plus_u_min(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken u_min_u_plus(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken c_num(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken u_num(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken d_num(particle_symmetry, spin_symmetry; slave_fermion) end end end From 4dab025ed0b2ff466122411c456af79464f5e0e1 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 13:50:15 +0100 Subject: [PATCH 03/24] add tj model to automatic docs --- docs/src/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/index.md b/docs/src/index.md index 9e0d66a..1dd2420 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -10,5 +10,5 @@ Documentation for [TensorKitTensors](https://github.com/QuantumKitHub/TensorKitT ``` ```@autodocs -Modules = [TensorKitTensors, TensorKitTensors.SpinOperators, TensorKitTensors.BosonOperators, TensorKitTensors.HubbardOperators] +Modules = [TensorKitTensors, TensorKitTensors.SpinOperators, TensorKitTensors.BosonOperators, TensorKitTensors.HubbardOperators, TensorKitTensors.TJOperators] ``` From 34782077445d1ef6376f201d492e0e8e4051edd2 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 14:22:15 +0100 Subject: [PATCH 04/24] increase patch coverage a bit more --- test/tjoperators.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/tjoperators.jl b/test/tjoperators.jl index b2d7458..1830887 100644 --- a/test/tjoperators.jl +++ b/test/tjoperators.jl @@ -69,6 +69,9 @@ end d_num(particle_symmetry, spin_symmetry; slave_fermion) ≈ d_num(particle_symmetry, spin_symmetry; slave_fermion) * u_num(particle_symmetry, spin_symmetry; slave_fermion) + @test TensorKit.id(tj_space(particle_symmetry, spin_symmetry; slave_fermion)) ≈ + c_num_hole(particle_symmetry, spin_symmetry; slave_fermion) + + c_num(particle_symmetry, spin_symmetry; slave_fermion) else @test_broken c_num(particle_symmetry, spin_symmetry; slave_fermion) @test_broken u_num(particle_symmetry, spin_symmetry; slave_fermion) @@ -76,6 +79,12 @@ end end # test spin operator + if particle_symmetry == Trivial + @test c_singlet(particle_symmetry, spin_symmetry; slave_fermion) ≈ + (u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) - + d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion)) / sqrt(2) + end + if spin_symmetry == Trivial ε = zeros(ComplexF64, 3, 3, 3) for i in 1:3 From e4d164cf252b549df77429e2a8f75b4dd881b5b3 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 14:28:35 +0100 Subject: [PATCH 05/24] fix formatting --- test/tjoperators.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/tjoperators.jl b/test/tjoperators.jl index 1830887..88c9bd4 100644 --- a/test/tjoperators.jl +++ b/test/tjoperators.jl @@ -69,8 +69,9 @@ end d_num(particle_symmetry, spin_symmetry; slave_fermion) ≈ d_num(particle_symmetry, spin_symmetry; slave_fermion) * u_num(particle_symmetry, spin_symmetry; slave_fermion) - @test TensorKit.id(tj_space(particle_symmetry, spin_symmetry; slave_fermion)) ≈ - c_num_hole(particle_symmetry, spin_symmetry; slave_fermion) + + @test TensorKit.id(tj_space(particle_symmetry, spin_symmetry; + slave_fermion)) ≈ + c_num_hole(particle_symmetry, spin_symmetry; slave_fermion) + c_num(particle_symmetry, spin_symmetry; slave_fermion) else @test_broken c_num(particle_symmetry, spin_symmetry; slave_fermion) @@ -81,8 +82,9 @@ end # test spin operator if particle_symmetry == Trivial @test c_singlet(particle_symmetry, spin_symmetry; slave_fermion) ≈ - (u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) - - d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion)) / sqrt(2) + (u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) - + d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion)) / + sqrt(2) end if spin_symmetry == Trivial From 53a81dbc6c9f0d1b362f38f3ec015588884a9b92 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 16:02:12 +0100 Subject: [PATCH 06/24] export TJOperators --- src/TensorKitTensors.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TensorKitTensors.jl b/src/TensorKitTensors.jl index 0a5de57..a0ca6d1 100644 --- a/src/TensorKitTensors.jl +++ b/src/TensorKitTensors.jl @@ -3,6 +3,7 @@ module TensorKitTensors export SpinOperators export BosonOperators export HubbardOperators +export TJOperators include("spinoperators.jl") include("bosonoperators.jl") From e64fcdc468253b91e8be504a71c039909cc6476f Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 17 Mar 2025 16:34:16 +0100 Subject: [PATCH 07/24] change order and fix docs --- src/tjoperators.jl | 540 +++++++++++++++++++++++---------------------- 1 file changed, 274 insertions(+), 266 deletions(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 87a6e1c..59e77ba 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -11,16 +11,19 @@ module TJOperators using TensorKit export tj_space -export c_plus_c_min, u_plus_u_min, d_plus_d_min -export c_min_c_plus, u_min_u_plus, d_min_d_plus -export u_min_d_min, d_min_u_min, c_singlet export c_num, u_num, d_num, c_num_hole 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 +export u_min_d_min, d_min_u_min +export c_plus_c_min, c_min_c_plus, c_singlet export S_plusmin, S_minplus, S_exchange -export c⁺c⁻, u⁺u⁻, d⁺d⁻, c⁻c⁺, u⁻u⁺, d⁻d⁺, u⁻d⁻, d⁻u⁻ export nꜛ, nꜜ, nʰ -export Sˣ, Sʸ, Sᶻ, S⁺, S⁻, S⁻⁺, S⁺⁻ +export Sˣ, Sʸ, Sᶻ, S⁺, S⁻ +export u⁺u⁻, d⁺d⁻, u⁻u⁺, d⁻d⁺, u⁻d⁻, d⁻u⁻ +export c⁺c⁻, c⁻c⁺ +export S⁻⁺, S⁺⁻ # not exported because namespace: export n """ @@ -68,254 +71,19 @@ function tj_space(::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) return error("Not implemented") end +# Single-site operators +# --------------------- function single_site_operator(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) V = tj_space(particle_symmetry, spin_symmetry; slave_fermion) return zeros(T, V ← V) end -function two_site_operator(T, particle_symmetry::Type{<:Sector}, - spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) - V = tj_space(particle_symmetry, spin_symmetry; slave_fermion) - return zeros(T, V ⊗ V ← V ⊗ V) -end - -""" - u_plus_u_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body operator ``e†_{1,↑}, e_{2,↑}`` that creates a spin-up electron at the first site and annihilates a spin-up electron at the second. -The only nonzero matrix element corresponds to `|↑0⟩ <-- |0↑⟩`. -""" -function u_plus_u_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return u_plus_u_min(ComplexF64, P, S; slave_fermion) -end -function u_plus_u_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, Trivial; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - #= The extra minus sign in slave-fermion basis: - c†_{1,↑} c_{2,↑} |0↑⟩ - = h_1 b†_{1,↑} h†_2 b_{2,↑} h†_1 b†_{2,↑}|vac⟩ - = -b†_{1,↑} h†_2 h_1 h†_1 b_{2,↑} b†_{2,↑}|vac⟩ - = -b†_{1,↑} h†_2 |vac⟩ - = -|↑0⟩ - =# - t[(I(b), I(h), dual(I(h)), dual(I(b)))][1, 1, 1, 1] = sgn * 1 - return t -end -function u_plus_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b, 1 // 2), I(h, 0), dual(I(h, 0)), dual(I(b, 1 // 2)))] .= sgn * 1 - return t -end -function u_plus_u_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") -end -function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) - t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b, 1), I(h, 0), dual(I(h, 0)), dual(I(b, 1)))][1, 1, 1, 1] = sgn * 1 - return t -end -function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) - t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b, 1, 1 // 2), I(h, 0, 0), dual(I(h, 0, 0)), dual(I(b, 1, 1 // 2)))] .= sgn * 1 - return t -end -function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") -end -const u⁺u⁻ = u_plus_u_min - -""" - d_plus_d_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body operator ``e†_{1,↓}, e_{2,↓}`` that creates a spin-down electron at the first site and annihilates a spin-down electron at the second. -The only nonzero matrix element corresponds to `|↓0⟩ <-- |0↓⟩`. -""" -function d_plus_d_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return d_plus_d_min(ComplexF64, P, S; slave_fermion) -end -function d_plus_d_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, Trivial; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b), I(h), dual(I(h)), dual(I(b)))][2, 1, 1, 2] = sgn * 1 - return t -end -function d_plus_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b, -1 // 2), I(h, 0), dual(I(h, 0)), dual(I(b, -1 // 2)))] .= sgn * 1 - return t -end -function d_plus_d_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") -end -function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) - t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b, 1), I(h, 0), dual(I(h, 0)), dual(I(b, 1)))][2, 1, 1, 2] = sgn * 1 - return t -end -function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) - t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(b, 1, -1 // 2), I(h, 0, 0), dual(I(h, 0, 0)), dual(I(b, 1, -1 // 2)))] .= sgn * 1 - return t -end -function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") -end -const d⁺d⁻ = d_plus_d_min - -""" - u_min_u_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the Hermitian conjugate of `u_plus_u_min`, i.e. -``(e†_{1,↑}, e_{2,↑})† = -e_{1,↑}, e†_{2,↑}`` (note the extra minus sign). -It annihilates a spin-up electron at the first site and creates a spin-up electron at the second. -The only nonzero matrix element corresponds to `|0↑⟩ <-- |↑0⟩`. -""" -function u_min_u_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return u_min_u_plus(ComplexF64, P, S; slave_fermion) -end -function u_min_u_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; - slave_fermion::Bool=false) - return copy(adjoint(u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion))) -end -const u⁻u⁺ = u_min_u_plus - -""" - d_min_d_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the Hermitian conjugate of `d_plus_d_min`, i.e. -``(e†_{1,↓}, e_{2,↓})† = -e_{1,↓}, e†_{2,↓}`` (note the extra minus sign). -It annihilates a spin-down electron at the first site and creates a spin-down electron at the second. -The only nonzero matrix element corresponds to `|0↓⟩ <-- |↓0⟩`. -""" -function d_min_d_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return d_min_d_plus(ComplexF64, P, S; slave_fermion) -end -function d_min_d_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; - slave_fermion::Bool=false) - return copy(adjoint(d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion))) -end -const d⁻d⁺ = d_min_d_plus - -""" - c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body operator that creates a particle at the first site and annihilates a particle at the second. -This is the sum of `u_plus_u_min` and `d_plus_d_min`. -""" -function c_plus_c_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return c_plus_c_min(ComplexF64, P, S; slave_fermion) -end -function c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; - slave_fermion::Bool=false) - return u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion) + - d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) -end -const c⁺c⁻ = c_plus_c_min - -""" - c_min_c_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body operator that annihilates a particle at the first site and creates a particle at the second. -This is the sum of `u_min_u_plus` and `d_min_d_plus`. -""" -function c_min_c_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return c_min_c_plus(ComplexF64, P, S; slave_fermion) -end -function c_min_c_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; - slave_fermion::Bool=false) - return copy(adjoint(c_plus_c_min(T, particle_symmetry, spin_symmetry; slave_fermion))) -end -const c⁻c⁺ = c_min_c_plus - -""" - u_min_d_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body operator ``e_{1,↑} e_{2,↓}`` that annihilates a spin-up particle at the first site and a spin-down particle at the second site. -The only nonzero matrix element corresponds to `|00⟩ <-- |↑↓⟩`. -""" -function u_min_d_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return u_min_d_min(ComplexF64, P, S; slave_fermion) -end -function u_min_d_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, Trivial; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(h), I(h), dual(I(b)), dual(I(b)))][1, 1, 1, 2] = -sgn * 1 - return t -end -function u_min_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(h, 0), I(h, 0), dual(I(b, 1 // 2)), dual(I(b, -1 // 2)))] .= -sgn * 1 - return t -end -function u_min_d_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) - throw(ArgumentError("`u_min_d_min` is not symmetric under `U1Irrep` particle symmetry")) -end -const u⁻d⁻ = u_min_d_min - -""" - d_min_u_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body operator ``e_{1,↓} e_{2,↑}`` that annihilates a spin-down particle at the first site and a spin-up particle at the second site. -The only nonzero matrix element corresponds to `|00⟩ <-- |↓↑⟩`. -""" -function d_min_u_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return d_min_u_min(ComplexF64, P, S; slave_fermion) -end -function d_min_u_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, Trivial; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(h), I(h), dual(I(b)), dual(I(b)))][1, 1, 2, 1] = -sgn * 1 - return t -end -function d_min_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) - t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) - I = sectortype(t) - (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) - t[(I(h, 0), I(h, 0), dual(I(b, -1 // 2)), dual(I(b, 1 // 2)))] .= -sgn * 1 - return t -end -function d_min_u_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) - throw(ArgumentError("`d_min_u_min` is not symmetric under `U1Irrep` particle symmetry")) -end -const d⁻u⁻ = d_min_u_min -""" - c_singlet(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) - -Return the two-body singlet operator ``(e_{1,↓} e_{2,↑} - e_{1,↓} e_{2,↑}) / sqrt(2)``. -""" -function c_singlet(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) - return c_singlet(ComplexF64, P, S; slave_fermion) -end -function c_singlet(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; - slave_fermion::Bool=false) - return (u_min_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) - - d_min_u_min(T, particle_symmetry, spin_symmetry; slave_fermion)) / sqrt(2) -end - -""" +@doc """ u_num(particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) Return the one-body operator that counts the number of spin-up electrons. -""" +""" u_num function u_num(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return u_num(ComplexF64, P, S; slave_fermion) end @@ -356,11 +124,11 @@ function u_num(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) end const nꜛ = u_num -""" +@doc """ d_num(particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the one-body operator that counts the number of spin-down electrons. -""" +""" d_num function d_num(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return d_num(ComplexF64, P, S; slave_fermion) end @@ -401,11 +169,11 @@ function d_num(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) end const nꜜ = d_num -""" +@doc """ c_num(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the one-body operator that counts the number of particles. -""" +""" c_num function c_num(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return c_num(ComplexF64, P, S; slave_fermion) end @@ -416,11 +184,11 @@ function c_num(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Secto end const n = c_num -""" +@doc """ c_num_hole(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the one-body operator that counts the number of holes. -""" +""" c_num_hole function c_num_hole(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return c_num_hole(ComplexF64, P, S; slave_fermion) end @@ -431,11 +199,11 @@ function c_num_hole(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<: end const nʰ = c_num_hole -""" +@doc """ S_x(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the one-body spin-1/2 x-operator on the electrons. -""" +""" S_x function S_x(P::Type{<:Sector}=Trivial, S::Type{<:Sector}=Trivial; slave_fermion::Bool=false) return S_x(ComplexF64, P, S; slave_fermion) @@ -458,11 +226,11 @@ function S_x(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion: end const Sˣ = S_x -""" +@doc """ S_y(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the one-body spin-1/2 x-operator on the electrons (only defined for `Trivial` symmetry). -""" +""" S_y function S_y(P::Type{<:Sector}=Trivial, S::Type{<:Sector}=Trivial; slave_fermion::Bool=false) return S_y(ComplexF64, P, S; slave_fermion) @@ -485,11 +253,11 @@ function S_y(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion: end const Sʸ = S_y -""" +@doc """ S_z(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the one-body spin-1/2 z-operator on the electrons. -""" +""" S_z function S_z(P::Type{<:Sector}=Trivial, S::Type{<:Sector}=Trivial; slave_fermion::Bool=false) return S_z(ComplexF64, P, S; slave_fermion) @@ -528,11 +296,11 @@ function S_z(T::Type{<:Number}, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion: end const Sᶻ = S_z -""" +@doc """ S_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the spin-plus operator. -""" +""" S_plus function S_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return S_plus(ComplexF64, P, S; slave_fermion) end @@ -543,11 +311,11 @@ function S_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sect end const S⁺ = S_plus -""" +@doc """ S_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) Return the spin-minus operator. -""" +""" S_min function S_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return S_min(ComplexF64, P, S; slave_fermion) end @@ -558,12 +326,252 @@ function S_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Secto end const S⁻ = S_min -""" +# Two site operators +# ------------------ +function two_site_operator(T, particle_symmetry::Type{<:Sector}, + spin_symmetry::Type{<:Sector}; slave_fermion::Bool=false) + V = tj_space(particle_symmetry, spin_symmetry; slave_fermion) + return zeros(T, V ⊗ V ← V ⊗ V) +end + +@doc """ + u_plus_u_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e†_{1,↑}, e_{2,↑}`` that creates a spin-up electron at the first site and annihilates a spin-up electron at the second. +The only nonzero matrix element corresponds to `|↑0⟩ <-- |0↑⟩`. +""" u_plus_u_min +function u_plus_u_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_plus_u_min(ComplexF64, P, S; slave_fermion) +end +function u_plus_u_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + #= The extra minus sign in slave-fermion basis: + c†_{1,↑} c_{2,↑} |0↑⟩ + = h_1 b†_{1,↑} h†_2 b_{2,↑} h†_1 b†_{2,↑}|vac⟩ + = -b†_{1,↑} h†_2 h_1 h†_1 b_{2,↑} b†_{2,↑}|vac⟩ + = -b†_{1,↑} h†_2 |vac⟩ + = -|↑0⟩ + =# + t[(I(b), I(h), dual(I(h)), dual(I(b)))][1, 1, 1, 1] = sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1 // 2), I(h, 0), dual(I(h, 0)), dual(I(b, 1 // 2)))] .= sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1), I(h, 0), dual(I(h, 0)), dual(I(b, 1)))][1, 1, 1, 1] = sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1, 1 // 2), I(h, 0, 0), dual(I(h, 0, 0)), dual(I(b, 1, 1 // 2)))] .= sgn * 1 + return t +end +function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +const u⁺u⁻ = u_plus_u_min + +@doc """ + d_plus_d_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e†_{1,↓}, e_{2,↓}`` that creates a spin-down electron at the first site and annihilates a spin-down electron at the second. +The only nonzero matrix element corresponds to `|↓0⟩ <-- |0↓⟩`. +""" d_plus_d_min +function d_plus_d_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_plus_d_min(ComplexF64, P, S; slave_fermion) +end +function d_plus_d_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b), I(h), dual(I(h)), dual(I(b)))][2, 1, 1, 2] = sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, -1 // 2), I(h, 0), dual(I(h, 0)), dual(I(b, -1 // 2)))] .= sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1), I(h, 0), dual(I(h, 0)), dual(I(b, 1)))][2, 1, 1, 2] = sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(b, 1, -1 // 2), I(h, 0, 0), dual(I(h, 0, 0)), dual(I(b, 1, -1 // 2)))] .= sgn * 1 + return t +end +function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + return error("Not implemented") +end +const d⁺d⁻ = d_plus_d_min + +@doc """ + u_min_u_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the Hermitian conjugate of `u_plus_u_min`, i.e. +``(e†_{1,↑}, e_{2,↑})† = -e_{1,↑}, e†_{2,↑}`` (note the extra minus sign). +It annihilates a spin-up electron at the first site and creates a spin-up electron at the second. +The only nonzero matrix element corresponds to `|0↑⟩ <-- |↑0⟩`. +""" u_min_u_plus +function u_min_u_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_min_u_plus(ComplexF64, P, S; slave_fermion) +end +function u_min_u_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const u⁻u⁺ = u_min_u_plus + +@doc """ + d_min_d_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the Hermitian conjugate of `d_plus_d_min`, i.e. +``(e†_{1,↓}, e_{2,↓})† = -e_{1,↓}, e†_{2,↓}`` (note the extra minus sign). +It annihilates a spin-down electron at the first site and creates a spin-down electron at the second. +The only nonzero matrix element corresponds to `|0↓⟩ <-- |↓0⟩`. +""" d_min_d_plus +function d_min_d_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_min_d_plus(ComplexF64, P, S; slave_fermion) +end +function d_min_d_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const d⁻d⁺ = d_min_d_plus + +@doc """ + u_min_d_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e_{1,↑} e_{2,↓}`` that annihilates a spin-up particle at the first site and a spin-down particle at the second site. +The only nonzero matrix element corresponds to `|00⟩ <-- |↑↓⟩`. +""" u_min_d_min +function u_min_d_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return u_min_d_min(ComplexF64, P, S; slave_fermion) +end +function u_min_d_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h), I(h), dual(I(b)), dual(I(b)))][1, 1, 1, 2] = -sgn * 1 + return t +end +function u_min_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h, 0), I(h, 0), dual(I(b, 1 // 2)), dual(I(b, -1 // 2)))] .= -sgn * 1 + return t +end +function u_min_d_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) + throw(ArgumentError("`u_min_d_min` is not symmetric under `U1Irrep` particle symmetry")) +end +const u⁻d⁻ = u_min_d_min + +@doc """ + d_min_u_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator ``e_{1,↓} e_{2,↑}`` that annihilates a spin-down particle at the first site and a spin-up particle at the second site. +The only nonzero matrix element corresponds to `|00⟩ <-- |↓↑⟩`. +""" d_min_u_min +function d_min_u_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return d_min_u_min(ComplexF64, P, S; slave_fermion) +end +function d_min_u_min(T, ::Type{Trivial}, ::Type{Trivial}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, Trivial; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h), I(h), dual(I(b)), dual(I(b)))][1, 1, 2, 1] = -sgn * 1 + return t +end +function d_min_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, U1Irrep; slave_fermion) + I = sectortype(t) + (h, b, sgn) = slave_fermion ? (1, 0, -1) : (0, 1, 1) + t[(I(h, 0), I(h, 0), dual(I(b, -1 // 2)), dual(I(b, 1 // 2)))] .= -sgn * 1 + return t +end +function d_min_u_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) + throw(ArgumentError("`d_min_u_min` is not symmetric under `U1Irrep` particle symmetry")) +end +const d⁻u⁻ = d_min_u_min + +@doc """ + c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator that creates a particle at the first site and annihilates a particle at the second. +This is the sum of `u_plus_u_min` and `d_plus_d_min`. +""" c_plus_c_min +function c_plus_c_min(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_plus_c_min(ComplexF64, P, S; slave_fermion) +end +function c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion) + + d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) +end +const c⁺c⁻ = c_plus_c_min + +@doc """ + c_min_c_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body operator that annihilates a particle at the first site and creates a particle at the second. +This is the sum of `u_min_u_plus` and `d_min_d_plus`. +""" c_min_c_plus +function c_min_c_plus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_min_c_plus(ComplexF64, P, S; slave_fermion) +end +function c_min_c_plus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return copy(adjoint(c_plus_c_min(T, particle_symmetry, spin_symmetry; slave_fermion))) +end +const c⁻c⁺ = c_min_c_plus + +@doc """ + c_singlet(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) + +Return the two-body singlet operator ``(e_{1,↓} e_{2,↑} - e_{1,↓} e_{2,↑}) / sqrt(2)``. +""" c_singlet +function c_singlet(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) + return c_singlet(ComplexF64, P, S; slave_fermion) +end +function c_singlet(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; + slave_fermion::Bool=false) + return (u_min_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) - + d_min_u_min(T, particle_symmetry, spin_symmetry; slave_fermion)) / sqrt(2) +end + +@doc """ S_plusmin(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) Return the two-body operator S⁺S⁻. The only nonzero matrix element corresponds to `|↑↓⟩ <-- |↓↑⟩`. -""" +""" S_plusmin function S_plusmin(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return S_plusmin(ComplexF64, P, S; slave_fermion) end @@ -597,12 +605,12 @@ function S_plusmin(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=fals end const S⁺⁻ = S_plusmin -""" +@doc """ S_minplus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) Return the two-body operator S⁻S⁺. The only nonzero matrix element corresponds to `|↓↑⟩ <-- |↑↓⟩`. -""" +""" S_minplus function S_minplus(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return S_minplus(ComplexF64, P, S; slave_fermion) end @@ -612,11 +620,11 @@ function S_minplus(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:S end const S⁻⁺ = S_minplus -""" +@doc """ S_exchange(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) Return the spin exchange operator S⋅S. -""" +""" S_exchange function S_exchange(P::Type{<:Sector}, S::Type{<:Sector}; slave_fermion::Bool=false) return S_exchange(ComplexF64, P, S; slave_fermion) end From ce7b1080e7431c5e60f0597fb9e206ad3954792c Mon Sep 17 00:00:00 2001 From: Sander De Meyer <74001142+sanderdemeyer@users.noreply.github.com> Date: Wed, 19 Mar 2025 13:52:40 +0100 Subject: [PATCH 08/24] Update src/tjoperators.jl add 'n' to exports Co-authored-by: Lukas Devos --- src/tjoperators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 59e77ba..879e2c9 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -19,7 +19,7 @@ export u_min_d_min, d_min_u_min export c_plus_c_min, c_min_c_plus, c_singlet export S_plusmin, S_minplus, S_exchange -export nꜛ, nꜜ, nʰ +export nꜛ, nꜜ, nʰ, n export Sˣ, Sʸ, Sᶻ, S⁺, S⁻ export u⁺u⁻, d⁺d⁻, u⁻u⁺, d⁻d⁺, u⁻d⁻, d⁻u⁻ export c⁺c⁻, c⁻c⁺ From eb0758ab0891fd08ced4d73dab5f44af939ef78c Mon Sep 17 00:00:00 2001 From: Sander De Meyer <74001142+sanderdemeyer@users.noreply.github.com> Date: Wed, 19 Mar 2025 13:53:02 +0100 Subject: [PATCH 09/24] Update src/tjoperators.jl remove comment Co-authored-by: Lukas Devos --- src/tjoperators.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 879e2c9..91a29c4 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -24,7 +24,6 @@ export Sˣ, Sʸ, Sᶻ, S⁺, S⁻ export u⁺u⁻, d⁺d⁻, u⁻u⁺, d⁻d⁺, u⁻d⁻, d⁻u⁻ export c⁺c⁻, c⁻c⁺ export S⁻⁺, S⁺⁻ -# not exported because namespace: export n """ tj_space(particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Sector}; slave_fermion::Bool = false) From aad7f7bf61c9b6a10e1481b4a104cb2e571774d2 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Wed, 19 Mar 2025 14:05:25 +0100 Subject: [PATCH 10/24] move and slightly modify docstring --- src/tjoperators.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 91a29c4..51d0871 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -1,10 +1,5 @@ #= Operators that act on t-J-type models i.e. the local hilbert space consists of - -- usual basis states: - |∅⟩, |↑⟩, |↓⟩ -- slave-fermion basis states (c_σ = h† b_σ; holon h is fermionic, spinon b_σ is bosonic): - |h⟩ = h†|∅⟩, |↑'⟩ = (b↑)†|∅⟩, |↓'⟩ = (b↓)†|∅⟩ =# module TJOperators @@ -34,6 +29,11 @@ The possible symmetries are - Spin: `Trivial`, `U1Irrep`, `SU2Irrep`. Setting `slave_fermion = true` switches to the slave-fermion basis. + +- basis states for `slave_fermion = false`: + |∅⟩, |↑⟩, |↓⟩ +- basis states for `slave_fermion = true`: (c_σ = h† b_σ; holon h is fermionic, spinon b_σ is bosonic): + |h⟩ = h†|∅⟩, |↑'⟩ = (b↑)†|∅⟩, |↓'⟩ = (b↓)†|∅⟩ """ function tj_space(::Type{Trivial}=Trivial, ::Type{Trivial}=Trivial; slave_fermion::Bool=false) From 8ff0e40fea8e638785e8419c199f548d50ae008f Mon Sep 17 00:00:00 2001 From: Sander De Meyer <74001142+sanderdemeyer@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:43:10 +0100 Subject: [PATCH 11/24] Update src/tjoperators.jl Co-authored-by: Yue Zhengyuan --- src/tjoperators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 51d0871..7cac1db 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -31,7 +31,7 @@ The possible symmetries are Setting `slave_fermion = true` switches to the slave-fermion basis. - basis states for `slave_fermion = false`: - |∅⟩, |↑⟩, |↓⟩ + |0⟩ = |vac⟩ (vacuum), |↑⟩ = (c↑)†|vac⟩, |↓⟩ = (c↓)†|vac⟩ - basis states for `slave_fermion = true`: (c_σ = h† b_σ; holon h is fermionic, spinon b_σ is bosonic): |h⟩ = h†|∅⟩, |↑'⟩ = (b↑)†|∅⟩, |↓'⟩ = (b↓)†|∅⟩ """ From 2654871f755ee5f56d358b36960fb7407b6c79bc Mon Sep 17 00:00:00 2001 From: Sander De Meyer <74001142+sanderdemeyer@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:43:21 +0100 Subject: [PATCH 12/24] Update src/tjoperators.jl Co-authored-by: Yue Zhengyuan --- src/tjoperators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 7cac1db..16166e6 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -33,7 +33,7 @@ Setting `slave_fermion = true` switches to the slave-fermion basis. - basis states for `slave_fermion = false`: |0⟩ = |vac⟩ (vacuum), |↑⟩ = (c↑)†|vac⟩, |↓⟩ = (c↓)†|vac⟩ - basis states for `slave_fermion = true`: (c_σ = h† b_σ; holon h is fermionic, spinon b_σ is bosonic): - |h⟩ = h†|∅⟩, |↑'⟩ = (b↑)†|∅⟩, |↓'⟩ = (b↓)†|∅⟩ + |0⟩ = h†|vac⟩, |↑⟩ = (b↑)†|vac⟩, |↓⟩ = (b↓)†|vac⟩ """ function tj_space(::Type{Trivial}=Trivial, ::Type{Trivial}=Trivial; slave_fermion::Bool=false) From 64bb613dc6c20b5e08ec465fb54681230e4b01c8 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Wed, 26 Mar 2025 17:15:23 +0100 Subject: [PATCH 13/24] add SU2 spin symmetry for tj model --- src/tjoperators.jl | 124 +++++++++++++++++++++++++++++++++++++++++--- test/tjoperators.jl | 32 ++++++------ 2 files changed, 135 insertions(+), 21 deletions(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index 16166e6..df123c4 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -48,7 +48,8 @@ function tj_space(::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=false) end end function tj_space(::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") + return slave_fermion ? Vect[FermionParity ⊠ SU2Irrep]((1, 0) => 1, (0, 1 // 2) => 1) : + Vect[FermionParity ⊠ SU2Irrep]((0, 0) => 1, (1, 1 // 2) => 1) end function tj_space(::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) return if slave_fermion @@ -67,7 +68,11 @@ function tj_space(::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=false) end end function tj_space(::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") + return if slave_fermion + Vect[FermionParity ⊠ U1Irrep ⊠ SU2Irrep]((1, 0, 0) => 1, (0, 1, 1 // 2) => 1) + else + Vect[FermionParity ⊠ U1Irrep ⊠ SU2Irrep]((0, 0, 0) => 1, (1, 1, 1 // 2) => 1) + end end # Single-site operators @@ -181,6 +186,30 @@ function c_num(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<:Secto return u_num(T, particle_symmetry, spin_symmetry; slave_fermion) + d_num(T, particle_symmetry, spin_symmetry; slave_fermion) end +function c_num(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, Trivial, SU2Irrep; slave_fermion) + I = sectortype(t) + if slave_fermion + block(t, I(0, 1 // 2))[1, 1] = 1 + # block(t, I(0, 1 // 2))[2, 2] = 1 + else + block(t, I(1, 1 // 2))[1, 1] = 1 + # block(t, I(1, 1 // 2))[2, 2] = 1 + end + return t +end +function c_num(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + t = single_site_operator(T, U1Irrep, SU2Irrep; slave_fermion) + I = sectortype(t) + if slave_fermion + block(t, I(0, 1, 1 // 2))[1, 1] = 1 + # block(t, I(0, 1, 1 // 2))[2, 2] = 1 + else + block(t, I(1, 1, 1 // 2))[1, 1] = 1 + # block(t, I(1, 1, 1 // 2))[2, 2] = 1 + end + return t +end const n = c_num @doc """ @@ -364,7 +393,7 @@ function u_plus_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=f return t end function u_plus_u_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") + throw(ArgumentError("`u_min_u_min` is not symmetric under `U1Irrep` particle symmetry")) end function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) @@ -381,7 +410,7 @@ function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=f return t end function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") + throw(ArgumentError("`u_plus_u_min` is not symmetric under `SU2Irrep` spin symmetry")) end const u⁺u⁻ = u_plus_u_min @@ -409,7 +438,7 @@ function d_plus_d_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=f return t end function d_plus_d_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") + throw(ArgumentError("`d_plus_d_min` is not symmetric under `SU2Irrep` spin symmetry")) end function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) @@ -426,7 +455,7 @@ function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{U1Irrep}; slave_fermion::Bool=f return t end function d_plus_d_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - return error("Not implemented") + throw(ArgumentError("`d_plus_d_min` is not symmetric under `SU2Irrep` spin symmetry")) end const d⁺d⁻ = d_plus_d_min @@ -490,6 +519,12 @@ end function u_min_d_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) throw(ArgumentError("`u_min_d_min` is not symmetric under `U1Irrep` particle symmetry")) end +function u_min_d_min(T, ::Type{<:Sector}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`u_min_d_min` is not symmetric under `SU2Irrep` spin symmetry")) +end +function u_min_d_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`u_min_d_min` is not symmetric under `U1Irrep` particle symmetry or under `SU2Irrep` spin symmetry")) +end const u⁻d⁻ = u_min_d_min @doc """ @@ -518,6 +553,12 @@ end function d_min_u_min(T, ::Type{U1Irrep}, ::Type{<:Sector}; slave_fermion::Bool=false) throw(ArgumentError("`d_min_u_min` is not symmetric under `U1Irrep` particle symmetry")) end +function d_min_u_min(T, ::Type{<:Sector}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`d_min_u_min` is not symmetric under `SU2Irrep` spin symmetry")) +end +function d_min_u_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + throw(ArgumentError("`d_min_u_min` is not symmetric under `U1Irrep` particle symmetry or under `SU2Irrep` particle symmetry")) +end const d⁻u⁻ = d_min_u_min @doc """ @@ -534,6 +575,55 @@ function c_plus_c_min(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{ return u_plus_u_min(T, particle_symmetry, spin_symmetry; slave_fermion) + d_plus_d_min(T, particle_symmetry, spin_symmetry; slave_fermion) end +function c_plus_c_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, SU2Irrep; slave_fermion) + I = sectortype(t) + if slave_fermion + f1 = only(fusiontrees((I(1, 0), I(0, 1 // 2)), I(1, 1 // 2))) + f2 = only(fusiontrees((I(0, 1 // 2), I(1, 0)), I(1, 1 // 2))) + t[f1, f2][1, 1, 1, 1] = 1 + # t[f1, f2][1, 2, 2, 1] = 1 + # f3 = only(fusiontrees((I(0, 1 // 2), I(1, 0)), I(1, 1 // 2))) + # f4 = only(fusiontrees((I(1, 0), I(0, 1 // 2)), I(1, 1 // 2))) + # t[f3, f4][1, 1, 1, 1] = -1 + # t[f3, f4][2, 1, 1, 2] = -1 + else + f1 = only(fusiontrees((I(0, 0), I(1, 1 // 2)), I(1, 1 // 2))) + f2 = only(fusiontrees((I(1, 1 // 2), I(0, 0)), I(1, 1 // 2))) + t[f1, f2][1, 1, 1, 1] = 1 + # t[f1, f2][1, 2, 2, 1] = 1 + # f3 = only(fusiontrees((I(1, 1 // 2), I(0, 0)), I(1, 1 // 2))) + # f4 = only(fusiontrees((I(0, 0), I(1, 1 // 2)), I(1, 1 // 2))) + # t[f3, f4][1, 1, 1, 1] = -1 + # t[f3, f4][2, 1, 1, 2] = -1 + end + return t +end +function c_plus_c_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, SU2Irrep; slave_fermion) + I = sectortype(t) + if slave_fermion + f1 = only(fusiontrees((I(1, 0, 0), I(0, 1, 1 // 2)), I(1, 1, 1 // 2))) + f2 = only(fusiontrees((I(0, 1, 1 // 2), I(1, 0, 0)), I(1, 1, 1 // 2))) + t[f1, f2][1, 1, 1, 1] = 1 + # t[f1, f2][1, 2, 2, 1] = 1 + # f3 = only(fusiontrees((I(0, 1, 1 // 2), I(1, 0, 0)), I(1, 1, 1 // 2))) + # f4 = only(fusiontrees((I(1, 0, 0), I(0, 1, 1 // 2)), I(1, 1, 1 // 2))) + # t[f3, f4][1, 1, 1, 1] = -1 + # t[f3, f4][2, 1, 1, 2] = -1 + else + f1 = only(fusiontrees((I(0, 0, 0), I(1, 1, 1 // 2)), I(1, 1, 1 // 2))) + f2 = only(fusiontrees((I(1, 1, 1 // 2), I(0, 0, 0)), I(1, 1, 1 // 2))) + t[f1, f2][1, 1, 1, 1] = 1 + # t[f1, f2][1, 2, 2, 1] = 1 + # f3 = only(fusiontrees((I(1, 1, 1 // 2), I(0, 0, 0)), I(1, 1, 1 // 2))) + # f4 = only(fusiontrees((I(0, 0, 0), I(1, 1, 1 // 2)), I(1, 1, 1 // 2))) + # t[f3, f4][1, 1, 1, 1] = -1 + # t[f3, f4][2, 1, 1, 2] = -1 + end + return t +end + const c⁺c⁻ = c_plus_c_min @doc """ @@ -635,5 +725,27 @@ function S_exchange(T, particle_symmetry::Type{<:Sector}, spin_symmetry::Type{<: S_minplus(T, particle_symmetry, spin_symmetry; slave_fermion)) + Sz ⊗ Sz end +function S_exchange(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, Trivial, SU2Irrep; slave_fermion) + + for (s, f) in fusiontrees(t) + l3 = f.uncoupled[1][2].j + l4 = f.uncoupled[2][2].j + k = f.coupled[2].j + t[s, f] .= (k * (k + 1) - l3 * (l3 + 1) - l4 * (l4 + 1)) / 2 + end + return t +end +function S_exchange(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) + t = two_site_operator(T, U1Irrep, SU2Irrep; slave_fermion) + + for (s, f) in fusiontrees(t) + l3 = f.uncoupled[1][3].j + l4 = f.uncoupled[2][3].j + k = f.coupled[3].j + t[s, f] .= (k * (k + 1) - l3 * (l3 + 1) - l4 * (l4 + 1)) / 2 + end + return t +end end diff --git a/test/tjoperators.jl b/test/tjoperators.jl index 88c9bd4..33b5325 100644 --- a/test/tjoperators.jl +++ b/test/tjoperators.jl @@ -6,27 +6,30 @@ using .TensorKitTensorsTestSetup using TensorKitTensors.TJOperators using StableRNGs -implemented_symmetries = [(Trivial, Trivial), (Trivial, U1Irrep), - (U1Irrep, Trivial), (U1Irrep, U1Irrep)] +implemented_symmetries = [(Trivial, Trivial), (Trivial, U1Irrep), (Trivial, SU2Irrep), + (U1Irrep, Trivial), (U1Irrep, U1Irrep), (U1Irrep, SU2Irrep)] @testset "Compare symmetric with trivial tensors" begin for particle_symmetry in [Trivial, U1Irrep], spin_symmetry in [Trivial, U1Irrep, SU2Irrep] - if (particle_symmetry, spin_symmetry) in implemented_symmetries - space = tj_space(particle_symmetry, spin_symmetry) + for slave_fermion in (false, true) + if (particle_symmetry, spin_symmetry) in implemented_symmetries + space = tj_space(particle_symmetry, spin_symmetry; slave_fermion) - O = c_plus_c_min(ComplexF64, particle_symmetry, spin_symmetry) - O_triv = c_plus_c_min(ComplexF64, Trivial, Trivial) - test_operator(O, O_triv) + O = c_plus_c_min(ComplexF64, particle_symmetry, spin_symmetry; + slave_fermion) + O_triv = c_plus_c_min(ComplexF64, Trivial, Trivial; slave_fermion) + test_operator(O, O_triv) - O = c_num(ComplexF64, particle_symmetry, spin_symmetry) - O_triv = c_num(ComplexF64, Trivial, Trivial) - test_operator(O, O_triv) + O = c_num(ComplexF64, particle_symmetry, spin_symmetry; slave_fermion) + O_triv = c_num(ComplexF64, Trivial, Trivial; slave_fermion) + test_operator(O, O_triv) - else - @test_broken c_plus_c_min(ComplexF64, particle_symmetry, spin_symmetry) - @test_broken c_num(ComplexF64, particle_symmetry, spin_symmetry) + else + @test_broken c_plus_c_min(ComplexF64, particle_symmetry, spin_symmetry) + @test_broken c_num(ComplexF64, particle_symmetry, spin_symmetry) + end end end end @@ -74,13 +77,12 @@ end c_num_hole(particle_symmetry, spin_symmetry; slave_fermion) + c_num(particle_symmetry, spin_symmetry; slave_fermion) else - @test_broken c_num(particle_symmetry, spin_symmetry; slave_fermion) @test_broken u_num(particle_symmetry, spin_symmetry; slave_fermion) @test_broken d_num(particle_symmetry, spin_symmetry; slave_fermion) end # test spin operator - if particle_symmetry == Trivial + if particle_symmetry == Trivial && spin_symmetry !== SU2Irrep @test c_singlet(particle_symmetry, spin_symmetry; slave_fermion) ≈ (u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) - d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion)) / From 7263d8f27630c817ec647a6a84953abf2ad405d8 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Wed, 26 Mar 2025 17:34:46 +0100 Subject: [PATCH 14/24] increase patch coverage --- test/tjoperators.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tjoperators.jl b/test/tjoperators.jl index 33b5325..a416c1c 100644 --- a/test/tjoperators.jl +++ b/test/tjoperators.jl @@ -87,6 +87,10 @@ end (u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) - d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion)) / sqrt(2) + else + @test_broken c_singlet(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion) end if spin_symmetry == Trivial From 0d9d98831675ac8aff061b5707bc21bf1a8aa9cb Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Wed, 26 Mar 2025 17:35:45 +0100 Subject: [PATCH 15/24] fix formatting --- test/tjoperators.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/tjoperators.jl b/test/tjoperators.jl index a416c1c..32e8c97 100644 --- a/test/tjoperators.jl +++ b/test/tjoperators.jl @@ -89,8 +89,10 @@ end sqrt(2) else @test_broken c_singlet(particle_symmetry, spin_symmetry; slave_fermion) - @test_broken u_min_d_min(particle_symmetry, spin_symmetry; slave_fermion) - @test_broken d_min_u_min(particle_symmetry, spin_symmetry; slave_fermion) + @test_broken u_min_d_min(particle_symmetry, spin_symmetry; + slave_fermion) + @test_broken d_min_u_min(particle_symmetry, spin_symmetry; + slave_fermion) end if spin_symmetry == Trivial From 69901f21a5cf8c62f5514a7be19202a9206e8419 Mon Sep 17 00:00:00 2001 From: Sander De Meyer <74001142+sanderdemeyer@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:19:00 +0100 Subject: [PATCH 16/24] Update src/tjoperators.jl Co-authored-by: Lukas Devos --- src/tjoperators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index df123c4..4ffd63b 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -393,7 +393,7 @@ function u_plus_u_min(T, ::Type{Trivial}, ::Type{U1Irrep}; slave_fermion::Bool=f return t end function u_plus_u_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) - throw(ArgumentError("`u_min_u_min` is not symmetric under `U1Irrep` particle symmetry")) + throw(ArgumentError("`u_plus_u_min` is not symmetric under `SU2Irrep` spin symmetry")) end function u_plus_u_min(T, ::Type{U1Irrep}, ::Type{Trivial}; slave_fermion::Bool=false) t = two_site_operator(T, U1Irrep, Trivial; slave_fermion) From 4247eb540dc9583087d234ee0e9440bbd8fdc0dd Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Thu, 27 Mar 2025 17:21:27 +0100 Subject: [PATCH 17/24] remove comments --- src/tjoperators.jl | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/tjoperators.jl b/src/tjoperators.jl index df123c4..aafd9c1 100644 --- a/src/tjoperators.jl +++ b/src/tjoperators.jl @@ -191,10 +191,8 @@ function c_num(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool=false) I = sectortype(t) if slave_fermion block(t, I(0, 1 // 2))[1, 1] = 1 - # block(t, I(0, 1 // 2))[2, 2] = 1 else block(t, I(1, 1 // 2))[1, 1] = 1 - # block(t, I(1, 1 // 2))[2, 2] = 1 end return t end @@ -203,10 +201,8 @@ function c_num(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool=false) I = sectortype(t) if slave_fermion block(t, I(0, 1, 1 // 2))[1, 1] = 1 - # block(t, I(0, 1, 1 // 2))[2, 2] = 1 else block(t, I(1, 1, 1 // 2))[1, 1] = 1 - # block(t, I(1, 1, 1 // 2))[2, 2] = 1 end return t end @@ -582,20 +578,10 @@ function c_plus_c_min(T, ::Type{Trivial}, ::Type{SU2Irrep}; slave_fermion::Bool= f1 = only(fusiontrees((I(1, 0), I(0, 1 // 2)), I(1, 1 // 2))) f2 = only(fusiontrees((I(0, 1 // 2), I(1, 0)), I(1, 1 // 2))) t[f1, f2][1, 1, 1, 1] = 1 - # t[f1, f2][1, 2, 2, 1] = 1 - # f3 = only(fusiontrees((I(0, 1 // 2), I(1, 0)), I(1, 1 // 2))) - # f4 = only(fusiontrees((I(1, 0), I(0, 1 // 2)), I(1, 1 // 2))) - # t[f3, f4][1, 1, 1, 1] = -1 - # t[f3, f4][2, 1, 1, 2] = -1 else f1 = only(fusiontrees((I(0, 0), I(1, 1 // 2)), I(1, 1 // 2))) f2 = only(fusiontrees((I(1, 1 // 2), I(0, 0)), I(1, 1 // 2))) t[f1, f2][1, 1, 1, 1] = 1 - # t[f1, f2][1, 2, 2, 1] = 1 - # f3 = only(fusiontrees((I(1, 1 // 2), I(0, 0)), I(1, 1 // 2))) - # f4 = only(fusiontrees((I(0, 0), I(1, 1 // 2)), I(1, 1 // 2))) - # t[f3, f4][1, 1, 1, 1] = -1 - # t[f3, f4][2, 1, 1, 2] = -1 end return t end @@ -606,20 +592,10 @@ function c_plus_c_min(T, ::Type{U1Irrep}, ::Type{SU2Irrep}; slave_fermion::Bool= f1 = only(fusiontrees((I(1, 0, 0), I(0, 1, 1 // 2)), I(1, 1, 1 // 2))) f2 = only(fusiontrees((I(0, 1, 1 // 2), I(1, 0, 0)), I(1, 1, 1 // 2))) t[f1, f2][1, 1, 1, 1] = 1 - # t[f1, f2][1, 2, 2, 1] = 1 - # f3 = only(fusiontrees((I(0, 1, 1 // 2), I(1, 0, 0)), I(1, 1, 1 // 2))) - # f4 = only(fusiontrees((I(1, 0, 0), I(0, 1, 1 // 2)), I(1, 1, 1 // 2))) - # t[f3, f4][1, 1, 1, 1] = -1 - # t[f3, f4][2, 1, 1, 2] = -1 else f1 = only(fusiontrees((I(0, 0, 0), I(1, 1, 1 // 2)), I(1, 1, 1 // 2))) f2 = only(fusiontrees((I(1, 1, 1 // 2), I(0, 0, 0)), I(1, 1, 1 // 2))) t[f1, f2][1, 1, 1, 1] = 1 - # t[f1, f2][1, 2, 2, 1] = 1 - # f3 = only(fusiontrees((I(1, 1, 1 // 2), I(0, 0, 0)), I(1, 1, 1 // 2))) - # f4 = only(fusiontrees((I(0, 0, 0), I(1, 1, 1 // 2)), I(1, 1, 1 // 2))) - # t[f3, f4][1, 1, 1, 1] = -1 - # t[f3, f4][2, 1, 1, 2] = -1 end return t end From 9cf1e1045cb0e80396c6f9f62b55a7a7179aaa9a Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 28 Mar 2025 11:09:22 +0100 Subject: [PATCH 18/24] add spinless fermions Again mainly a copy from MPSKitModels --- src/TensorKitTensors.jl | 5 +++ src/fermionoperators.jl | 80 ++++++++++++++++++++++++++++++++++++++++ src/utility.jl | 53 ++++++++++++++++++++++++++ test/fermionoperators.jl | 52 ++++++++++++++++++++++++++ test/runtests.jl | 16 +++++--- 5 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 src/fermionoperators.jl create mode 100644 src/utility.jl create mode 100644 test/fermionoperators.jl diff --git a/src/TensorKitTensors.jl b/src/TensorKitTensors.jl index a0ca6d1..79e88ce 100644 --- a/src/TensorKitTensors.jl +++ b/src/TensorKitTensors.jl @@ -4,10 +4,15 @@ export SpinOperators export BosonOperators export HubbardOperators export TJOperators +export FermionOperators +export contract_onesite, contract_twosite, split_twosite + +include("utility.jl") include("spinoperators.jl") include("bosonoperators.jl") include("hubbardoperators.jl") include("tjoperators.jl") +include("fermionoperators.jl") end diff --git a/src/fermionoperators.jl b/src/fermionoperators.jl new file mode 100644 index 0000000..a019e72 --- /dev/null +++ b/src/fermionoperators.jl @@ -0,0 +1,80 @@ +#=========================================================================================== + Spinless fermions +===========================================================================================# + +module FermionOperators + +using TensorKit + +export c_plus, c_min +export c_plusmin, c_minplus, c_plusplus, c_minmin +export c_number +export c⁺, c⁻ +export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ + +""" + c_plus([elt::Type{<:Number}=ComplexF64]; side=:L) + c⁺([elt::Type{<:Number}=ComplexF64]; side=:L) + +Fermionic creation operator. +""" +function c_plus(elt::Type{<:Number}=ComplexF64; side=:L) + vspace = Vect[fℤ₂](1 => 1) + if side === :L + pspace = Vect[fℤ₂](0 => 1, 1 => 1) + c⁺ = zeros(elt, pspace ← pspace ⊗ vspace) + block(c⁺, fℤ₂(1)) .= one(elt) + elseif side === :R + C = c_plus(elt; side=:L) + F = isomorphism(storagetype(C), vspace, flip(vspace)) + @planar c⁺[-1 -2; -3] := C[-2; 1 2] * τ[1 2; 3 -3] * F[3; -1] + else + throw(ArgumentError("invalid side `:$side`, expected `:L` or `:R`")) + end + return c⁺ +end +const c⁺ = c_plus + +""" + c_min([elt::Type{<:Number}=ComplexF64]; side=:L) + c⁻([elt::Type{<:Number}=ComplexF64]; side=:L) + +Fermionic annihilation operator. +""" +function c_min(elt::Type{<:Number}=ComplexF64; side=:L) + if side === :L + C = c_plus(elt; side=:L)' + F = isomorphism(flip(space(C, 2)), space(C, 2)) + @planar c⁻[-1; -2 -3] := C[-1 1; -2] * F[-3; 1] + elseif side === :R + c⁻ = permute(c_plus(elt; side=:L)', ((2, 1), (3,))) + else + throw(ArgumentError("invalid side `:$side`, expected `:L` or `:R`")) + end + return c⁻ +end + +const c⁻ = c_min + +c_plusmin(elt=ComplexF64) = contract_twosite(c⁺(elt; side=:L), c⁻(elt; side=:R)) +const c⁺c⁻ = c_plusmin +c_minplus(elt=ComplexF64) = contract_twosite(c⁻(elt; side=:L), c⁺(elt; side=:R)) +const c⁻c⁺ = c_minplus +c_plusplus(elt=ComplexF64) = contract_twosite(c⁺(elt; side=:L), c⁺(elt; side=:R)) +const c⁺c⁺ = c_plusplus +c_minmin(elt=ComplexF64) = contract_twosite(c⁻(elt; side=:L), c⁻(elt; side=:R)) +const c⁻c⁻ = c_minmin + +""" + c_number([elt::Type{<:Number}=ComplexF64]) + +Fermionic number operator. +""" +function c_number(elt::Type{<:Number}=ComplexF64) + pspace = Vect[fℤ₂](0 => 1, 1 => 1) + n = zeros(elt, pspace ← pspace) + block(n, fℤ₂(1)) .= one(elt) + return n +end + +end diff --git a/src/utility.jl b/src/utility.jl new file mode 100644 index 0000000..011e49e --- /dev/null +++ b/src/utility.jl @@ -0,0 +1,53 @@ +using TensorKit + +# check all elements are equal -> only defined in 1.8+ +@static if !isdefined(Base, :allequal) + allequal(itr) = isempty(itr) ? true : all(isequal(first(itr)), itr) +end + +#=========================================================================================== + Contractions +===========================================================================================# + +""" + contract_onesite(L, R) + +contract two single-site operators inta a single-site operator. +""" +function contract_onesite(L::AbstractTensorMap{<:Number,<:Any,1,2}, + R::AbstractTensorMap{<:Number,<:Any,2,1}) + @plansor H[-1; -2] := L[-1; 1 2] * τ[1 2; 3 4] * R[3 4; -2] + return H +end +function contract_onesite(L::AbstractTensorMap{<:Number,<:Any,1,1}, + R::AbstractTensorMap{<:Number,<:Any,1,1}) + return L * R +end + +""" + contract_twosite(L, R) + +contract two single-site operators into a two-site operator. +""" +function contract_twosite(L::AbstractTensorMap{<:Number,<:Any,1,2}, + R::AbstractTensorMap{<:Number,<:Any,2,1}) + @plansor H[-1 -2; -3 -4] := L[-1; -3 1] * R[1 -2; -4] + return H +end +function contract_twosite(L::AbstractTensorMap{<:Any,<:Any,1,1}, + R::AbstractTensorMap{<:Any,<:Any,1,1}) + return L ⊗ R +end + +""" + split_twosite(O) + +Split a two-site operator into two single-site operators with a connecting auxiliary leg. +""" +function split_twosite(O::AbstractTensorMap{<:Any,<:Any,2,2}) + U, S, V, = tsvd(O, ((3, 1), (4, 2)); trunc=truncbelow(eps(real(scalartype(O))))) + sqrtS = sqrt(S) + @plansor L[p'; p a] := U[p p'; 1] * sqrtS[1; a] + @plansor R[a p'; p] := sqrtS[a; 1] * V[1; p p'] + return L, R +end diff --git a/test/fermionoperators.jl b/test/fermionoperators.jl new file mode 100644 index 0000000..a53d02b --- /dev/null +++ b/test/fermionoperators.jl @@ -0,0 +1,52 @@ +using TensorKit +using Test +include("testsetup.jl") +using TensorKitTensors +using .TensorKitTensorsTestSetup +using TensorKitTensors.FermionOperators +using StableRNGs + +# anticommutation relations +# {cᵢ†, cⱼ†} = 0 = {cᵢ, cⱼ} +# {cᵢ, cⱼ†} = δᵢⱼ + +@testset "simple fermions" begin + cc = contract_twosite(c⁻(; side=:L), c⁻(; side=:R)) + cc⁺ = contract_twosite(c⁻(; side=:L), c⁺(; side=:R)) + c⁺c = contract_twosite(c⁺(; side=:L), c⁻(; side=:R)) + c⁺c⁺ = contract_twosite(c⁺(; side=:L), c⁺(; side=:R)) + + @test cc ≈ -permute(cc, ((2, 1), (4, 3))) + @test c⁺c⁺ ≈ -permute(c⁺c⁺, ((2, 1), (4, 3))) + + # the following doesn't hold + # I don't think I can get all of these to hold simultaneously? + # @test cc⁺ ≈ -permute(c⁺c, (2, 1), (4, 3)) + + @test cc⁺' ≈ c⁺c + @test cc' ≈ c⁺c⁺ + @test (c⁺c + cc⁺)' ≈ cc⁺ + c⁺c + @test (c⁺c - cc⁺)' ≈ cc⁺ - c⁺c + + @test c_number() ≈ contract_onesite(c⁺(; side=:L), c⁻(; side=:R)) +end + +@testset "Exact Diagonalization" begin + rng = StableRNG(123) + + L = 2 + t, V, mu = rand(rng, 3) + pspace = Vect[fℤ₂](0 => 1, 1 => 1) + + cc⁺ = contract_twosite(c⁻(; side=:L), c⁺(; side=:R)) + c⁺c = contract_twosite(c⁺(; side=:L), c⁻(; side=:R)) + + H = -t * (cc⁺ + c⁺c) + + V * ((c_number() - 0.5 * id(pspace)) ⊗ (c_number() - 0.5 * id(pspace))) - + 0.5 * mu * (c_number() ⊗ id(pspace) + id(pspace) ⊗ c_number()) + # Values based on https://arxiv.org/abs/1610.05003v1. Half-Chain Entanglement Entropy in the One-Dimensional Spinless Fermion Model + true_eigenvalues = sort([V / 4, V / 4 - mu, -V / 4 - mu / 2 + t, -V / 4 - mu / 2 - t]) + + eigenvals = get_lowest_eigenvalues(H, -1; L) + @test eigenvals ≈ true_eigenvalues +end diff --git a/test/runtests.jl b/test/runtests.jl index 0fdbff3..06d4729 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,13 @@ using SafeTestsets +@time @safetestset "spin operators" begin + include("spinoperators.jl") +end + +@time @safetestset "boson operators" begin + include("bosonoperators.jl") +end + @time @safetestset "Hubbard operators" begin include("hubbardoperators.jl") end @@ -8,12 +16,8 @@ end include("tjoperators.jl") end -@time @safetestset "spin operators" begin - include("spinoperators.jl") -end - -@time @safetestset "boson operators" begin - include("bosonoperators.jl") +@time @safetestset "spinless fermion operators" begin + include("fermionoperators.jl") end @time @safetestset "Aqua" begin From 8f569b8264a5bf4f53c78399ed29a3bd0b01826e Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 28 Mar 2025 13:16:40 +0100 Subject: [PATCH 19/24] add get_lowest_eigenvalues --- test/testsetup.jl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/testsetup.jl b/test/testsetup.jl index 3f93623..b1799ce 100644 --- a/test/testsetup.jl +++ b/test/testsetup.jl @@ -1,6 +1,6 @@ module TensorKitTensorsTestSetup -export test_operator, operator_sum +export test_operator, operator_sum, get_lowest_eigenvalues using Test using TensorKit @@ -29,4 +29,16 @@ function test_operator(O1::AbstractTensorMap, O2::AbstractTensorMap; L::Int=4, isapproxkwargs...) end +function get_lowest_eigenvalues(O1::AbstractTensorMap, n::Int; L::Int=4) + H = operator_sum(O1; L) + eigenvals = mapreduce(vcat, eigvals(H)) do (c, vals) + return repeat(vals, dim(c)) + end + sort!(eigenvals; by=real) + if n == -1 + return eigenvals + end + return eigenvals[1:n] +end + end From 4cfc3451ceb3b86fe88f3c22602822e6d3fc74f8 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 28 Mar 2025 14:16:40 +0100 Subject: [PATCH 20/24] changed definition of SF Only allow for contracted tensors. Also changed some naming conventions to match the ones in this package --- src/fermionoperators.jl | 98 ++++++++++++++++++---------------------- test/fermionoperators.jl | 29 +++++------- 2 files changed, 54 insertions(+), 73 deletions(-) diff --git a/src/fermionoperators.jl b/src/fermionoperators.jl index a019e72..2c6d25d 100644 --- a/src/fermionoperators.jl +++ b/src/fermionoperators.jl @@ -7,74 +7,62 @@ module FermionOperators using TensorKit export c_plus, c_min -export c_plusmin, c_minplus, c_plusplus, c_minmin -export c_number +export c_plus_c_min, c_min_c_plus, c_plus_c_plus, c_min_c_min +export c_num export c⁺, c⁻ export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ +export n """ - c_plus([elt::Type{<:Number}=ComplexF64]; side=:L) - c⁺([elt::Type{<:Number}=ComplexF64]; side=:L) + c_num([elt::Type{<:Number}=ComplexF64]) -Fermionic creation operator. +Fermionic number operator. """ -function c_plus(elt::Type{<:Number}=ComplexF64; side=:L) - vspace = Vect[fℤ₂](1 => 1) - if side === :L - pspace = Vect[fℤ₂](0 => 1, 1 => 1) - c⁺ = zeros(elt, pspace ← pspace ⊗ vspace) - block(c⁺, fℤ₂(1)) .= one(elt) - elseif side === :R - C = c_plus(elt; side=:L) - F = isomorphism(storagetype(C), vspace, flip(vspace)) - @planar c⁺[-1 -2; -3] := C[-2; 1 2] * τ[1 2; 3 -3] * F[3; -1] - else - throw(ArgumentError("invalid side `:$side`, expected `:L` or `:R`")) - end - return c⁺ +function c_num(T::Type{<:Number}=ComplexF64) + pspace = Vect[fℤ₂](0 => 1, 1 => 1) + n = zeros(T, pspace ← pspace) + block(n, fℤ₂(1)) .= one(T) + return n end -const c⁺ = c_plus +const n = c_num -""" - c_min([elt::Type{<:Number}=ComplexF64]; side=:L) - c⁻([elt::Type{<:Number}=ComplexF64]; side=:L) - -Fermionic annihilation operator. -""" -function c_min(elt::Type{<:Number}=ComplexF64; side=:L) - if side === :L - C = c_plus(elt; side=:L)' - F = isomorphism(flip(space(C, 2)), space(C, 2)) - @planar c⁻[-1; -2 -3] := C[-1 1; -2] * F[-3; 1] - elseif side === :R - c⁻ = permute(c_plus(elt; side=:L)', ((2, 1), (3,))) - else - throw(ArgumentError("invalid side `:$side`, expected `:L` or `:R`")) - end - return c⁻ +# Two site operators +# ------------------ +function two_site_operator(T::Type{<:Number}=ComplexF64) + V = Vect[fℤ₂](0 => 1, 1 => 1) + return zeros(T, V ⊗ V ← V ⊗ V) end -const c⁻ = c_min +function c_plus_c_min(T=ComplexF64) + t = two_site_operator(T) + I = sectortype(t) + t[(I(1), I(0), dual(I(0)), dual(I(1)))] .= 1 + return t +end +const c⁺c⁻ = c_plus_c_min -c_plusmin(elt=ComplexF64) = contract_twosite(c⁺(elt; side=:L), c⁻(elt; side=:R)) -const c⁺c⁻ = c_plusmin -c_minplus(elt=ComplexF64) = contract_twosite(c⁻(elt; side=:L), c⁺(elt; side=:R)) -const c⁻c⁺ = c_minplus -c_plusplus(elt=ComplexF64) = contract_twosite(c⁺(elt; side=:L), c⁺(elt; side=:R)) -const c⁺c⁺ = c_plusplus -c_minmin(elt=ComplexF64) = contract_twosite(c⁻(elt; side=:L), c⁻(elt; side=:R)) -const c⁻c⁻ = c_minmin +function c_min_c_plus(T=ComplexF64) + t = two_site_operator(T) + I = sectortype(t) + t[(I(0), I(1), dual(I(1)), dual(I(0)))] .= 1 + return t +end +const c⁻c⁺ = c_min_c_plus -""" - c_number([elt::Type{<:Number}=ComplexF64]) +function c_plus_c_plus(T=ComplexF64) + t = two_site_operator(T) + I = sectortype(t) + t[(I(1), I(1), dual(I(0)), dual(I(0)))] .= 1 + return t +end +const c⁺c⁺ = c_plus_c_plus -Fermionic number operator. -""" -function c_number(elt::Type{<:Number}=ComplexF64) - pspace = Vect[fℤ₂](0 => 1, 1 => 1) - n = zeros(elt, pspace ← pspace) - block(n, fℤ₂(1)) .= one(elt) - return n +function c_min_c_min(T=ComplexF64) + t = two_site_operator(T) + I = sectortype(t) + t[(I(0), I(0), dual(I(1)), dual(I(1)))] .= 1 + return t end +const c⁻c⁻ = c_min_c_min end diff --git a/test/fermionoperators.jl b/test/fermionoperators.jl index a53d02b..840762b 100644 --- a/test/fermionoperators.jl +++ b/test/fermionoperators.jl @@ -11,24 +11,20 @@ using StableRNGs # {cᵢ, cⱼ†} = δᵢⱼ @testset "simple fermions" begin - cc = contract_twosite(c⁻(; side=:L), c⁻(; side=:R)) - cc⁺ = contract_twosite(c⁻(; side=:L), c⁺(; side=:R)) - c⁺c = contract_twosite(c⁺(; side=:L), c⁻(; side=:R)) - c⁺c⁺ = contract_twosite(c⁺(; side=:L), c⁺(; side=:R)) - - @test cc ≈ -permute(cc, ((2, 1), (4, 3))) - @test c⁺c⁺ ≈ -permute(c⁺c⁺, ((2, 1), (4, 3))) + @test c⁻c⁻() ≈ -permute(c⁻c⁻(), ((2, 1), (4, 3))) + @test c⁺c⁺() ≈ -permute(c⁺c⁺(), ((2, 1), (4, 3))) # the following doesn't hold # I don't think I can get all of these to hold simultaneously? # @test cc⁺ ≈ -permute(c⁺c, (2, 1), (4, 3)) - @test cc⁺' ≈ c⁺c - @test cc' ≈ c⁺c⁺ - @test (c⁺c + cc⁺)' ≈ cc⁺ + c⁺c - @test (c⁺c - cc⁺)' ≈ cc⁺ - c⁺c + @test c⁻c⁺()' ≈ c⁺c⁻() + @test c⁻c⁻()' ≈ c⁺c⁺() + @test (c⁺c⁻() + c⁻c⁺())' ≈ c⁻c⁺() + c⁺c⁻() + @test (c⁺c⁻() - c⁻c⁺())' ≈ c⁻c⁺() - c⁺c⁻() - @test c_number() ≈ contract_onesite(c⁺(; side=:L), c⁻(; side=:R)) + @plansor c_number[-1; -2] := c⁺c⁻()[-1 1; 3 2] * τ[3 2; -2 1] + @test c_number ≈ c_num() end @testset "Exact Diagonalization" begin @@ -38,12 +34,9 @@ end t, V, mu = rand(rng, 3) pspace = Vect[fℤ₂](0 => 1, 1 => 1) - cc⁺ = contract_twosite(c⁻(; side=:L), c⁺(; side=:R)) - c⁺c = contract_twosite(c⁺(; side=:L), c⁻(; side=:R)) - - H = -t * (cc⁺ + c⁺c) + - V * ((c_number() - 0.5 * id(pspace)) ⊗ (c_number() - 0.5 * id(pspace))) - - 0.5 * mu * (c_number() ⊗ id(pspace) + id(pspace) ⊗ c_number()) + H = -t * (c⁻c⁺() + c⁺c⁻()) + + V * ((n() - 0.5 * id(pspace)) ⊗ (n() - 0.5 * id(pspace))) - + 0.5 * mu * (n() ⊗ id(pspace) + id(pspace) ⊗ n()) # Values based on https://arxiv.org/abs/1610.05003v1. Half-Chain Entanglement Entropy in the One-Dimensional Spinless Fermion Model true_eigenvalues = sort([V / 4, V / 4 - mu, -V / 4 - mu / 2 + t, -V / 4 - mu / 2 - t]) From c2509badf12e77fd2840b014c2119f96dd196690 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 28 Mar 2025 14:25:31 +0100 Subject: [PATCH 21/24] fix docs --- docs/src/index.md | 2 +- src/fermionoperators.jl | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 1dd2420..e536830 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -10,5 +10,5 @@ Documentation for [TensorKitTensors](https://github.com/QuantumKitHub/TensorKitT ``` ```@autodocs -Modules = [TensorKitTensors, TensorKitTensors.SpinOperators, TensorKitTensors.BosonOperators, TensorKitTensors.HubbardOperators, TensorKitTensors.TJOperators] +Modules = [TensorKitTensors, TensorKitTensors.SpinOperators, TensorKitTensors.BosonOperators, TensorKitTensors.HubbardOperators, TensorKitTensors.TJOperators, TensorKitTensors.FermionOperators] ``` diff --git a/src/fermionoperators.jl b/src/fermionoperators.jl index 2c6d25d..40def47 100644 --- a/src/fermionoperators.jl +++ b/src/fermionoperators.jl @@ -13,11 +13,12 @@ export c⁺, c⁻ export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ export n -""" +@doc """ c_num([elt::Type{<:Number}=ComplexF64]) + n([elt::Type{<:Number}=ComplexF64]) -Fermionic number operator. -""" +Return the one-body operator that counts the nunber of particles. +""" c_num function c_num(T::Type{<:Number}=ComplexF64) pspace = Vect[fℤ₂](0 => 1, 1 => 1) n = zeros(T, pspace ← pspace) @@ -33,7 +34,13 @@ function two_site_operator(T::Type{<:Number}=ComplexF64) return zeros(T, V ⊗ V ← V ⊗ V) end -function c_plus_c_min(T=ComplexF64) +@doc """ + c_plus_c_min([elt::Type{<:Number}=ComplexF64]) + c⁺c⁻([elt::Type{<:Number}=ComplexF64]) + +Return the two-body operator that creates a particle at the first site and annihilates a particle at the second. +""" c_plus_c_min +function c_plus_c_min(T::Type{<:Number}=ComplexF64) t = two_site_operator(T) I = sectortype(t) t[(I(1), I(0), dual(I(0)), dual(I(1)))] .= 1 @@ -41,7 +48,13 @@ function c_plus_c_min(T=ComplexF64) end const c⁺c⁻ = c_plus_c_min -function c_min_c_plus(T=ComplexF64) +@doc """ + c_min_c_plus([elt::Type{<:Number}=ComplexF64]) + c⁻c⁺([elt::Type{<:Number}=ComplexF64]) + +Return the two-body operator that annihilates a particle at the first site and creates a particle at the second. +""" c_min_c_plus +function c_min_c_plus(T::Type{<:Number}=ComplexF64) t = two_site_operator(T) I = sectortype(t) t[(I(0), I(1), dual(I(1)), dual(I(0)))] .= 1 @@ -49,7 +62,13 @@ function c_min_c_plus(T=ComplexF64) end const c⁻c⁺ = c_min_c_plus -function c_plus_c_plus(T=ComplexF64) +@doc """ + c_plus_c_plus([elt::Type{<:Number}=ComplexF64]) + c⁺c⁺([elt::Type{<:Number}=ComplexF64]) + +Return the two-body operator that creates a particle at the first and at the second site. +""" c_plus_c_plus +function c_plus_c_plus(T::Type{<:Number}=ComplexF64) t = two_site_operator(T) I = sectortype(t) t[(I(1), I(1), dual(I(0)), dual(I(0)))] .= 1 @@ -57,7 +76,13 @@ function c_plus_c_plus(T=ComplexF64) end const c⁺c⁺ = c_plus_c_plus -function c_min_c_min(T=ComplexF64) +@doc """ + c_min_c_min([elt::Type{<:Number}=ComplexF64]) + c⁻c⁻([elt::Type{<:Number}=ComplexF64]) + +Return the two-body operator that annihilates a particle at the first and at the second site. +""" c_min_c_min +function c_min_c_min(T::Type{<:Number}=ComplexF64) t = two_site_operator(T) I = sectortype(t) t[(I(0), I(0), dual(I(1)), dual(I(1)))] .= 1 From 46049cfcea8eecc0176054ef983e42cd2867f17e Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Fri, 28 Mar 2025 14:28:59 +0100 Subject: [PATCH 22/24] fix exports --- src/fermionoperators.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/fermionoperators.jl b/src/fermionoperators.jl index 40def47..998b8a8 100644 --- a/src/fermionoperators.jl +++ b/src/fermionoperators.jl @@ -6,12 +6,10 @@ module FermionOperators using TensorKit -export c_plus, c_min -export c_plus_c_min, c_min_c_plus, c_plus_c_plus, c_min_c_min export c_num -export c⁺, c⁻ -export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ +export c_plus_c_min, c_min_c_plus, c_plus_c_plus, c_min_c_min export n +export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ @doc """ c_num([elt::Type{<:Number}=ComplexF64]) From 5e0896c2542d9181549a04d0115983ec543f2904 Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Mon, 31 Mar 2025 16:27:21 +0200 Subject: [PATCH 23/24] remove utility file --- src/TensorKitTensors.jl | 3 --- src/utility.jl | 53 ----------------------------------------- 2 files changed, 56 deletions(-) delete mode 100644 src/utility.jl diff --git a/src/TensorKitTensors.jl b/src/TensorKitTensors.jl index 79e88ce..60c1744 100644 --- a/src/TensorKitTensors.jl +++ b/src/TensorKitTensors.jl @@ -6,9 +6,6 @@ export HubbardOperators export TJOperators export FermionOperators -export contract_onesite, contract_twosite, split_twosite - -include("utility.jl") include("spinoperators.jl") include("bosonoperators.jl") include("hubbardoperators.jl") diff --git a/src/utility.jl b/src/utility.jl deleted file mode 100644 index 011e49e..0000000 --- a/src/utility.jl +++ /dev/null @@ -1,53 +0,0 @@ -using TensorKit - -# check all elements are equal -> only defined in 1.8+ -@static if !isdefined(Base, :allequal) - allequal(itr) = isempty(itr) ? true : all(isequal(first(itr)), itr) -end - -#=========================================================================================== - Contractions -===========================================================================================# - -""" - contract_onesite(L, R) - -contract two single-site operators inta a single-site operator. -""" -function contract_onesite(L::AbstractTensorMap{<:Number,<:Any,1,2}, - R::AbstractTensorMap{<:Number,<:Any,2,1}) - @plansor H[-1; -2] := L[-1; 1 2] * τ[1 2; 3 4] * R[3 4; -2] - return H -end -function contract_onesite(L::AbstractTensorMap{<:Number,<:Any,1,1}, - R::AbstractTensorMap{<:Number,<:Any,1,1}) - return L * R -end - -""" - contract_twosite(L, R) - -contract two single-site operators into a two-site operator. -""" -function contract_twosite(L::AbstractTensorMap{<:Number,<:Any,1,2}, - R::AbstractTensorMap{<:Number,<:Any,2,1}) - @plansor H[-1 -2; -3 -4] := L[-1; -3 1] * R[1 -2; -4] - return H -end -function contract_twosite(L::AbstractTensorMap{<:Any,<:Any,1,1}, - R::AbstractTensorMap{<:Any,<:Any,1,1}) - return L ⊗ R -end - -""" - split_twosite(O) - -Split a two-site operator into two single-site operators with a connecting auxiliary leg. -""" -function split_twosite(O::AbstractTensorMap{<:Any,<:Any,2,2}) - U, S, V, = tsvd(O, ((3, 1), (4, 2)); trunc=truncbelow(eps(real(scalartype(O))))) - sqrtS = sqrt(S) - @plansor L[p'; p a] := U[p p'; 1] * sqrtS[1; a] - @plansor R[a p'; p] := sqrtS[a; 1] * V[1; p p'] - return L, R -end From 35ed6858334e76baad998a93372913f7d20be7bf Mon Sep 17 00:00:00 2001 From: sanderdemeyer Date: Tue, 1 Apr 2025 11:37:36 +0200 Subject: [PATCH 24/24] update SF for consistency consistency with other files --- src/fermionoperators.jl | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/fermionoperators.jl b/src/fermionoperators.jl index 998b8a8..707fef6 100644 --- a/src/fermionoperators.jl +++ b/src/fermionoperators.jl @@ -6,11 +6,28 @@ module FermionOperators using TensorKit +export fermion_space export c_num export c_plus_c_min, c_min_c_plus, c_plus_c_plus, c_min_c_min export n export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ +""" + fermion_space() + +Return the local hilbert space for a model of spinless fermions. +""" +function fermion_space() + return Vect[fℤ₂](0 => 1, 1 => 1) +end + +# Single-site operators +# --------------------- +function single_site_operator(T) + V = fermion_space() + return zeros(T, V ← V) +end + @doc """ c_num([elt::Type{<:Number}=ComplexF64]) n([elt::Type{<:Number}=ComplexF64]) @@ -18,17 +35,16 @@ export c⁺c⁻, c⁻c⁺, c⁺c⁺, c⁻c⁻ Return the one-body operator that counts the nunber of particles. """ c_num function c_num(T::Type{<:Number}=ComplexF64) - pspace = Vect[fℤ₂](0 => 1, 1 => 1) - n = zeros(T, pspace ← pspace) - block(n, fℤ₂(1)) .= one(T) - return n + t = single_site_operator(T) + block(t, fℤ₂(1)) .= one(T) + return t end const n = c_num # Two site operators # ------------------ function two_site_operator(T::Type{<:Number}=ComplexF64) - V = Vect[fℤ₂](0 => 1, 1 => 1) + V = fermion_space() return zeros(T, V ⊗ V ← V ⊗ V) end