From 563690930e7b7828cffd5ad82b9f990f11b5d2cf Mon Sep 17 00:00:00 2001 From: BramVancraeynest Date: Mon, 9 Mar 2026 18:47:12 +0100 Subject: [PATCH 1/4] Add quantum Ashkin-Teller model --- docs/src/man/models.md | 1 + src/MPSKitModels.jl | 1 + src/models/hamiltonians.jl | 75 ++++++++++++++++++++++++++++++++++++++ src/utility.jl | 12 ++++++ test/ashkin_teller.jl | 34 +++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 test/ashkin_teller.jl diff --git a/docs/src/man/models.md b/docs/src/man/models.md index 8693d4f..7bdba69 100644 --- a/docs/src/man/models.md +++ b/docs/src/man/models.md @@ -15,6 +15,7 @@ heisenberg_XXZ heisenberg_XYZ bilinear_biquadratic_model tj_model +ashkin_teller hubbard_model bose_hubbard_model quantum_chemistry_hamiltonian diff --git a/src/MPSKitModels.jl b/src/MPSKitModels.jl index 64e8064..c6aa42f 100644 --- a/src/MPSKitModels.jl +++ b/src/MPSKitModels.jl @@ -45,6 +45,7 @@ export heisenberg_XXX, heisenberg_XXZ, heisenberg_XYZ export bilinear_biquadratic_model export hubbard_model, bose_hubbard_model export tj_model +export ashkin_teller export quantum_chemistry_hamiltonian export classical_ising diff --git a/src/models/hamiltonians.jl b/src/models/hamiltonians.jl index 7d36c28..1b79692 100644 --- a/src/models/hamiltonians.jl +++ b/src/models/hamiltonians.jl @@ -461,4 +461,79 @@ function tj_model( end end +#=========================================================================================== + Quantum Ashkin-Teller model +===========================================================================================# +""" + ashkin_teller([T::Type{<:Number} = ComplexF64], + [S = ProductSector{Tuple{Z2Irrep, Z2Irrep}}], + [lattice::AbstractLattice = InfiniteChain(1)]; + h = 1.0, J = 1.0, λ = 1.0) + +MPO for the hamiltonian of the quantum Ashkin-Teller model. The model is +defined on a chain of two qubits per site. Writing Pauli operators on each of these qubits +as ``\\sigma `` and ``\\tau ``, the Hamiltonian reads: +```math +H = -h \\sum_i\\bigg(\\sigma_i^x + \\tau_i^x + \\lambda \\sigma_i^x \\tau_i^x \\bigg) +-J \\sum_{\\langle i,j \\rangle} \\bigg( \\sigma_i^z \\sigma_j^z + \\tau_j^z \\tau_j^z ++\\lambda \\sigma_i^z \\sigma_j^z \\tau_i^z \\tau_j^z \\bigg). + +``` +Currently the Hamiltonian is only supported with ```Z2Irrep ⊠ Z2Irrep``` symmetry. +""" +function ashkin_teller end +function ashkin_teller(lattice::AbstractLattice; kwargs...) + return ashkin_teller(ComplexF64, ProductSector{Tuple{Z2Irrep, Z2Irrep}}, lattice; kwargs...) +end +function ashkin_teller(T::Type{<:Number}, lattice::AbstractLattice; kwargs...) + return ashkin_teller(T, ProductSector{Tuple{Z2Irrep, Z2Irrep}}, lattice; kwargs...) +end +function ashkin_teller( + T::Type{<:Number} = ComplexF64, + S = ProductSector{Tuple{Z2Irrep, Z2Irrep}}, + lattice::AbstractLattice = InfiniteChain(1); + h = 1.0, J = 1.0, λ = 1.0 + ) + + S == ProductSector{Tuple{Z2Irrep, Z2Irrep}} || error("Only implemented with ℤ₂×ℤ₂ symmetry") + + V = Vect[S](c => 1 for c in values(S)) + + # Single site operators + XI = ones(T, V ← V) + block(XI, S(1, 0)) .*= -1 + block(XI, S(1, 1)) .*= -1 + IX = ones(T, V ← V) + block(IX, S(0, 1)) .*= -1 + block(IX, S(1, 1)) .*= -1 + XX = ones(T, V ← V) + block(XX, S(0, 1)) .*= -1 + block(XX, S(1, 0)) .*= -1 + + # Nearest-neighbour terms + ZIZI = zeros(T, V ⊗ V ← V ⊗ V) + IZIZ = zeros(T, V ⊗ V ← V ⊗ V) + ZZZZ = zeros(T, V ⊗ V ← V ⊗ V) + for (s, f) in fusiontrees(ZIZI) + if s.uncoupled == map(x -> flip_charge(x, (1, 0)), f.uncoupled) + ZIZI[s, f] .= 1 + end + if s.uncoupled == map(x -> flip_charge(x, (0, 1)), f.uncoupled) + IZIZ[s, f] .= 1 + end + if s.uncoupled == map(x -> flip_charge(x, (1, 1)), f.uncoupled) + ZZZZ[s, f] .= 1 + end + end + + return @mpoham begin + sum(vertices(lattice)) do i + return -h * (XI{i} + IX{i} + λ * XX{i}) + end + + sum(nearest_neighbours(lattice)) do (i, j) + -J * (ZIZI{i, j} + IZIZ{i, j} + λ * ZZZZ{i, j}) + end + end +end + # TODO: add (hardcore) bosonic t-J model (https://arxiv.org/abs/2409.15424) diff --git a/src/utility.jl b/src/utility.jl index a80fdfa..ad893df 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -57,3 +57,15 @@ function split_twosite(O::AbstractTensorMap{<:Any, <:Any, 2, 2}) @plansor R[a p'; p] := sqrtS[a; 1] * V[1; p p'] return L, R end + +#=========================================================================================== + Other utility +===========================================================================================# + +""" + flip_charge(charge, flip) + + Take the product of charge with flip. + Works only for abelian symmetries. +""" +flip_charge(charge, flip) = only(charge ⊗ sectortype(charge)(flip)) diff --git a/test/ashkin_teller.jl b/test/ashkin_teller.jl new file mode 100644 index 0000000..5490754 --- /dev/null +++ b/test/ashkin_teller.jl @@ -0,0 +1,34 @@ +using Test +using TensorKit +using MPSKit +using MPSKitModels + +# Setup + +gammas = [ + pi / 2, pi / 3, pi / 4, pi / 6, 0, +] +# Exact GS energy density via mapping to spin 1/2 XXZ and Bethe ansatz +# https://www.sciencedirect.com/science/article/pii/0003491688900152 +E0s = [ + -8 / pi, -12 / 4, -4 * (sqrt(2) / pi + 1 / (2 * sqrt(2))), + -4 * (1 / pi + 11 / (12 * sqrt(3))), 2 - 8 * log(2), +] +alg = VUMPS(; maxiter = 100, verbosity = 0) + +S = Z2Irrep ⊠ Z2Irrep +V = Vect[S](c => 1 for c in values(S)) +W = Vect[S](c => 6 for c in values(S)) + +# Test + +@testset "Ashkin-Teller" for (gamma, E0) in zip(gammas, E0s) + + H = ashkin_teller(h = 1, J = 1, λ = cos(gamma)) + Ψ = InfiniteMPS(V, W) + + @test imag(expectation_value(Ψ, H)) ≈ 0 atol = 1.0e-12 + + Ψ0, _ = find_groundstate(Ψ, H, alg) + @test real(expectation_value(Ψ0, H)) ≈ E0 atol = 1.0e-3 +end From b00035ed0d42cc437021f668f26aaa65e0957991 Mon Sep 17 00:00:00 2001 From: BramVancraeynest Date: Tue, 10 Mar 2026 09:50:01 +0100 Subject: [PATCH 2/4] Add Ashkin-Teller to runtests.jl --- test/runtests.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 9fc5e38..41a5576 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -45,6 +45,10 @@ end include("quantum_potts.jl") end +@testset "quantum Ashkin-Teller model" begin + include("ashkin_teller.jl") +end + @testset "classical ising model" begin include("classical_ising.jl") end From 92b3ccc03572220479a1ba7c551a9f3b7b06de88 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 30 Mar 2026 19:06:12 -0400 Subject: [PATCH 3/4] small changes --- src/models/hamiltonians.jl | 76 ++++++++++++++++++-------------------- test/ashkin_teller.jl | 16 ++++---- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/models/hamiltonians.jl b/src/models/hamiltonians.jl index 1b79692..ae26472 100644 --- a/src/models/hamiltonians.jl +++ b/src/models/hamiltonians.jl @@ -465,8 +465,7 @@ end Quantum Ashkin-Teller model ===========================================================================================# """ - ashkin_teller([T::Type{<:Number} = ComplexF64], - [S = ProductSector{Tuple{Z2Irrep, Z2Irrep}}], + ashkin_teller([T::Type{<:Number} = ComplexF64], [S = Trivial], [lattice::AbstractLattice = InfiniteChain(1)]; h = 1.0, J = 1.0, λ = 1.0) @@ -479,61 +478,58 @@ H = -h \\sum_i\\bigg(\\sigma_i^x + \\tau_i^x + \\lambda \\sigma_i^x \\tau_i^x \\ +\\lambda \\sigma_i^z \\sigma_j^z \\tau_i^z \\tau_j^z \\bigg). ``` -Currently the Hamiltonian is only supported with ```Z2Irrep ⊠ Z2Irrep``` symmetry. """ function ashkin_teller end function ashkin_teller(lattice::AbstractLattice; kwargs...) - return ashkin_teller(ComplexF64, ProductSector{Tuple{Z2Irrep, Z2Irrep}}, lattice; kwargs...) + return ashkin_teller(ComplexF64, Trivial, lattice; kwargs...) +end +function ashkin_teller(symmetry::Type{<:Sector}; kwargs...) + return ashkin_teller(ComplexF64, symmetry; kwargs...) end function ashkin_teller(T::Type{<:Number}, lattice::AbstractLattice; kwargs...) - return ashkin_teller(T, ProductSector{Tuple{Z2Irrep, Z2Irrep}}, lattice; kwargs...) + return ashkin_teller(T, Trivial, lattice; kwargs...) end function ashkin_teller( - T::Type{<:Number} = ComplexF64, - S = ProductSector{Tuple{Z2Irrep, Z2Irrep}}, + T::Type{<:Number} = ComplexF64, S::Type{<:Sector} = Trivial, lattice::AbstractLattice = InfiniteChain(1); h = 1.0, J = 1.0, λ = 1.0 ) + S₁, S₂ = _ashin_teller_decompose_symmetry(S) - S == ProductSector{Tuple{Z2Irrep, Z2Irrep}} || error("Only implemented with ℤ₂×ℤ₂ symmetry") + # component tensors + X₁ = σˣ(T, S₁) + Z₁Z₁ = σᶻᶻ(T, S₁) + I₁ = id(T, domain(X₁)) + I₁I₁ = id(T, domain(Z₁Z₁)) - V = Vect[S](c => 1 for c in values(S)) + Z₂Z₂ = σᶻᶻ(T, S₂) + X₂ = σˣ(T, S₂) + I₂ = id(T, domain(X₂)) + I₂I₂ = id(T, domain(Z₂Z₂)) # Single site operators - XI = ones(T, V ← V) - block(XI, S(1, 0)) .*= -1 - block(XI, S(1, 1)) .*= -1 - IX = ones(T, V ← V) - block(IX, S(0, 1)) .*= -1 - block(IX, S(1, 1)) .*= -1 - XX = ones(T, V ← V) - block(XX, S(0, 1)) .*= -1 - block(XX, S(1, 0)) .*= -1 + XI = X₁ ⊠ I₂ + IX = I₁ ⊠ X₂ + XX = X₁ ⊠ X₂ + F = isometry(Int, fuse(domain(XX)) ← domain(XX)) + onsite = F * (-h * (XI + IX + λ * XX)) * F' # Nearest-neighbour terms - ZIZI = zeros(T, V ⊗ V ← V ⊗ V) - IZIZ = zeros(T, V ⊗ V ← V ⊗ V) - ZZZZ = zeros(T, V ⊗ V ← V ⊗ V) - for (s, f) in fusiontrees(ZIZI) - if s.uncoupled == map(x -> flip_charge(x, (1, 0)), f.uncoupled) - ZIZI[s, f] .= 1 - end - if s.uncoupled == map(x -> flip_charge(x, (0, 1)), f.uncoupled) - IZIZ[s, f] .= 1 - end - if s.uncoupled == map(x -> flip_charge(x, (1, 1)), f.uncoupled) - ZZZZ[s, f] .= 1 - end - end - - return @mpoham begin - sum(vertices(lattice)) do i - return -h * (XI{i} + IX{i} + λ * XX{i}) - end + - sum(nearest_neighbours(lattice)) do (i, j) - -J * (ZIZI{i, j} + IZIZ{i, j} + λ * ZZZZ{i, j}) - end + @tensor FF[-1 -2; -3 -5 -4 -6] := F[-1; -3 -4] * F[-2; -5 -6] # note permutation! + ZIZI = FF * (Z₁Z₁ ⊠ I₂I₂) * FF' + IZIZ = FF * (I₁I₁ ⊠ Z₂Z₂) * FF' + ZZZZ = FF * (Z₁Z₁ ⊠ Z₂Z₂) * FF' + twosite = -J * (ZIZI + IZIZ + λ * ZZZZ) + + return @mpoham sum(vertices(lattice)) do i + return onsite{i} + end + sum(nearest_neighbours(lattice)) do (i, j) + return twosite{i, j} end end +_ashin_teller_decompose_symmetry(::Type{Trivial}) = (Trivial, Trivial) +_ashin_teller_decompose_symmetry(::Type{ProductSector{Tuple{A, B}}}) where {A <: Union{Trivial, Z2Irrep}, B <: Union{Trivial, Z2Irrep}} = (A, B) +_ashin_teller_decompose_symmetry(T) = error("Ashkin-Teller model not implemented for symmetry $T") + # TODO: add (hardcore) bosonic t-J model (https://arxiv.org/abs/2409.15424) diff --git a/test/ashkin_teller.jl b/test/ashkin_teller.jl index 5490754..e0ca98f 100644 --- a/test/ashkin_teller.jl +++ b/test/ashkin_teller.jl @@ -16,19 +16,21 @@ E0s = [ ] alg = VUMPS(; maxiter = 100, verbosity = 0) -S = Z2Irrep ⊠ Z2Irrep -V = Vect[S](c => 1 for c in values(S)) -W = Vect[S](c => 6 for c in values(S)) - # Test @testset "Ashkin-Teller" for (gamma, E0) in zip(gammas, E0s) - H = ashkin_teller(h = 1, J = 1, λ = cos(gamma)) - Ψ = InfiniteMPS(V, W) - + Ψ = InfiniteMPS(physicalspace(H), [ComplexSpace(24)]) @test imag(expectation_value(Ψ, H)) ≈ 0 atol = 1.0e-12 + Ψ0, _ = find_groundstate(Ψ, H, alg) + @test real(expectation_value(Ψ0, H)) ≈ E0 atol = 1.0e-3 +end +@testset "Ashkin-Teller (Z2 ⊠ Z2)" for (gamma, E0) in zip(gammas, E0s) + H = ashkin_teller(Z2Irrep ⊠ Z2Irrep; h = 1, J = 1, λ = cos(gamma)) + V = spacetype(H)(c => 6 for c in values(sectortype(H))) + Ψ = InfiniteMPS(physicalspace(H), [V]) + @test imag(expectation_value(Ψ, H)) ≈ 0 atol = 1.0e-12 Ψ0, _ = find_groundstate(Ψ, H, alg) @test real(expectation_value(Ψ0, H)) ≈ E0 atol = 1.0e-3 end From cf30ae9df5e7dd4cf7f100ab2c6a9d70cf7d9bb3 Mon Sep 17 00:00:00 2001 From: BramVancraeynest Date: Tue, 31 Mar 2026 12:00:39 +0200 Subject: [PATCH 4/4] Fix small typo AT docs --- src/models/hamiltonians.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/hamiltonians.jl b/src/models/hamiltonians.jl index ae26472..6d448c9 100644 --- a/src/models/hamiltonians.jl +++ b/src/models/hamiltonians.jl @@ -474,7 +474,7 @@ defined on a chain of two qubits per site. Writing Pauli operators on each of th as ``\\sigma `` and ``\\tau ``, the Hamiltonian reads: ```math H = -h \\sum_i\\bigg(\\sigma_i^x + \\tau_i^x + \\lambda \\sigma_i^x \\tau_i^x \\bigg) --J \\sum_{\\langle i,j \\rangle} \\bigg( \\sigma_i^z \\sigma_j^z + \\tau_j^z \\tau_j^z +-J \\sum_{\\langle i,j \\rangle} \\bigg( \\sigma_i^z \\sigma_j^z + \\tau_i^z \\tau_j^z +\\lambda \\sigma_i^z \\sigma_j^z \\tau_i^z \\tau_j^z \\bigg). ```