diff --git a/src/TensorKitSectors.jl b/src/TensorKitSectors.jl index 1c8c251..ebe0e8c 100644 --- a/src/TensorKitSectors.jl +++ b/src/TensorKitSectors.jl @@ -21,6 +21,7 @@ export triangle_equation, pentagon_equation, hexagon_equation export Trivial export Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, LargeZNIrrep, U1Irrep export D3Irrep, D4Irrep, DNIrrep, CU1Irrep +export A4Irrep export SU2Irrep export ZNElement, Z2Element, Z3Element, Z4Element export ProductSector, TimeReversed @@ -35,6 +36,7 @@ export charge, modulus # --------------- export ⊠, ⊗, × export Cyclic, ℤ, ℤ₂, ℤ₃, ℤ₄, U₁, SU, SU₂, Dihedral, D₃, D₄, CU₁ +export Alternating, A₄ export fℤ₂, fU₁, fSU₂ # public diff --git a/src/groups.jl b/src/groups.jl index f1db7eb..899dfdf 100644 --- a/src/groups.jl +++ b/src/groups.jl @@ -38,6 +38,14 @@ group. """ abstract type Dihedral{N} <: Group end +""" + abstract type Alternating{N} <: Group + +Type to represent the alternating group of order `N!/2`, which is the group +of even permutations on `N` elements. +""" +abstract type Alternating{N} <: Group end + """ abstract type U₁ <: AbelianGroup @@ -72,6 +80,7 @@ const ℤ₃ = ℤ{3} const ℤ₄ = ℤ{4} const D₃ = Dihedral{3} const D₄ = Dihedral{4} +const A₄ = Alternating{4} const SU₂ = SU{2} type_repr(::Type{ℤ₂}) = "ℤ₂" @@ -80,6 +89,7 @@ type_repr(::Type{ℤ₄}) = "ℤ₄" type_repr(::Type{ℤ{N}}) where {N} = "ℤ{$N}" type_repr(::Type{D₃}) = "D₃" type_repr(::Type{D₄}) = "D₄" +type_repr(::Type{A₄}) = "A₄" type_repr(::Type{SU₂}) = "SU₂" type_repr(::Type{U₁}) = "U₁" type_repr(::Type{CU₁}) = "CU₁" diff --git a/src/irreps/a4irrep.jl b/src/irreps/a4irrep.jl new file mode 100644 index 0000000..60ffb1f --- /dev/null +++ b/src/irreps/a4irrep.jl @@ -0,0 +1,164 @@ +""" + struct A4Irrep <: AbstractIrrep{A₄} + A4Irrep(n::Integer) + Irrep[A₄](n::Integer) + +Represents irreps of the alternating group ``A₄``. + +## Fields + +- `n::Int8`: the label of the irrep, corresponding to ``1``, ``1′``, ``1″`` and ``3``. +""" +struct A4Irrep <: AbstractIrrep{A₄} + n::Int8 + function A4Irrep(n::Integer) + 0 ≤ n < 4 || throw(ArgumentError("A4Irrep only has irrep labels in `0:3`")) + return new(n) + end +end + +FusionStyle(::Type{A4Irrep}) = GenericFusion() +sectorscalartype(::Type{A4Irrep}) = Float64 + +unit(::Type{A4Irrep}) = A4Irrep(0) +dual(a::A4Irrep) = a.n == 3 ? a : A4Irrep((3 - a.n) % 3) + +Base.hash(a::A4Irrep, h::UInt) = hash(a.n, h) +Base.convert(::Type{A4Irrep}, n::Integer) = A4Irrep(n) + +Base.getindex(::IrrepTable, ::Type{A₄}) = A4Irrep + + +# Sector iterator +# --------------- +Base.isless(a::A4Irrep, b::A4Irrep) = isless(a.n, b.n) +Base.IteratorSize(::Type{SectorValues{A4Irrep}}) = Base.HasLength() +Base.length(::SectorValues{A4Irrep}) = 4 + +Base.iterate(v::SectorValues{A4Irrep}, i = 1) = i > length(v) ? nothing : (v[i], i + 1) + +@inline function Base.getindex(v::SectorValues{A4Irrep}, i::Int) + @boundscheck 1 <= i <= length(v) || throw(BoundsError(v, i)) + return A4Irrep(i - 1) +end + +findindex(::SectorValues{A4Irrep}, a::A4Irrep) = a.n + 1 + +# Product iterator +# ---------------- + +const A4IrrepProdIterator = SectorProductIterator{A4Irrep} +⊗(a::A4Irrep, b::A4Irrep) = SectorProductIterator((a <= b ? (a, b) : (b, a))...) + +Base.IteratorSize(::Type{A4IrrepProdIterator}) = Base.HasLength() +Base.length(x::A4IrrepProdIterator) = (x.a == x.b == A4Irrep(3)) ? 4 : 1 + +function Base.iterate(p::A4IrrepProdIterator, state::Int = 1) + a, b = p.a, p.b + if state == 1 + a.n == b.n == 3 && return (A4Irrep(state - 1), state + 1) # 3 ⊗ 3 + b.n == 3 && return (b, 5) # x ⊗ 3 = 3 + return (A4Irrep((a.n + b.n) % 3), 5) # 1d irreps ≡ Z3 + elseif state < 5 # a == b == 3 + return (A4Irrep(state - 1), state + 1) + else + return nothing + end +end + +# Topological data +# ---------------- +dim(a::A4Irrep) = a.n == 3 ? 3 : 1 + +function Nsymbol(a::A4Irrep, b::A4Irrep, c::A4Irrep) + # 3d irreps + a.n == b.n == 3 && return 1 + (c.n == 3) + (a.n == 3 || b.n == 3) && return Int(c.n == 3) + # 1d irreps + return Int((a.n + b.n) % 3 == c.n) +end + + +function Fsymbol(a::I, b::I, c::I, d::I, e::I, f::I) where {I <: A4Irrep} + T = sectorscalartype(I) + Nabe = Nsymbol(a, b, e) + Necd = Nsymbol(e, c, d) + Nbcf = Nsymbol(b, c, f) + Nafd = Nsymbol(a, f, d) + + Nabe > 0 && Necd > 0 && Nbcf > 0 && Nafd > 0 || + return zeros(T, Nabe, Necd, Nbcf, Nafd) + + # fallback through fusiontensor for A4Irrep + A = fusiontensor(a, b, e) + B = fusiontensor(e, c, d)[:, :, 1, :] + C = fusiontensor(b, c, f) + D = fusiontensor(a, f, d)[:, :, 1, :] + @tensor F[-1, -2, -3, -4] := conj(D[1, 5, -4]) * conj(C[2, 4, 5, -3]) * + A[1, 2, 3, -1] * B[3, 4, -2] + + return F +end + +# bosonic +function Rsymbol(a::I, b::I, c::I) where {I <: A4Irrep} + Nabc = Nsymbol(a, b, c) + R = zeros(sectorscalartype(I), Nabc, Nabc) + Nabc == 0 && return R + if a == b == c == A4Irrep(3) + R[1, 1] = -1 + R[2, 2] = 1 + else + R[1, 1] = 1 + end + return R +end + +# choice of basis: https://journals.aps.org/rmp/pdf/10.1103/RevModPhys.82.2701 +# triplet is a real representation -> can make all representation matrices real +function fusiontensor(a::I, b::I, c::I) where {I <: A4Irrep} + T = sectorscalartype(I) + Nabc = Nsymbol(a, b, c) + C = zeros(T, dim(a), dim(b), dim(c), Nabc) + isempty(C) && return C + + ω = cis(2π / 3) + + if a.n == b.n == 3 # 3 ⊗ 3 + if c.n != 3 # singlets + invsqrt3 = 1 / sqrt(3.0) + for i in 1:3 + j = 4 - mod1(i - c.n - 1, 3) + C[i, j, 1, 1] = invsqrt3 + end + else # triplet: eq 38 in above reference + s2 = 1 / sqrt(2.0) + s6 = 1 / sqrt(6.0) + + # antisymmetric channel + C[:, :, 1, 1] .= [0 0 0; 0 0 s2; 0 -s2 0] + C[:, :, 2, 1] .= [0 s2 0; -s2 0 0; 0 0 0] + C[:, :, 3, 1] .= [0 0 -s2; 0 0 0; s2 0 0] + + # symmetric channel + C[:, :, 1, 2] .= [-2 * s6 0 0; 0 0 s6; 0 s6 0] + C[:, :, 2, 2] .= [0 s6 0; s6 0 0; 0 0 -2 * s6] + C[:, :, 3, 2] .= [0 0 s6; 0 -2 * s6 0; s6 0 0] + end + else + if a.n != 3 && b.n != 3 # 1d x 1d + C[1, 1, 1] = one(T) + elseif a.n == 3 && b.n != 3 # 3 x 1d + for i in 1:3 + j = mod1(i - b.n, 3) + C[j, 1, i, 1] = one(T) + end + else # 1d x 3: reshape of 3 x 1d + for i in 1:3 + j = mod1(i - a.n, 3) + C[1, j, i, 1] = one(T) + end + end + end + return C +end diff --git a/src/irreps/irreps.jl b/src/irreps/irreps.jl index 56e3dbd..6175eb3 100644 --- a/src/irreps/irreps.jl +++ b/src/irreps/irreps.jl @@ -68,3 +68,4 @@ include("u1irrep.jl") include("dnirrep.jl") include("cu1irrep.jl") include("su2irrep.jl") +include("a4irrep.jl") diff --git a/test/runtests.jl b/test/runtests.jl index cc0dfd4..8852584 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,20 +9,21 @@ const sectorlist = ( Trivial, PlanarTrivial, Z2Irrep, Z3Irrep, Z4Irrep, Irrep[ℤ{200}], U1Irrep, DNIrrep{3}, DNIrrep{4}, DNIrrep{5}, CU1Irrep, - SU2Irrep, NewSU2Irrep, + A4Irrep, SU2Irrep, NewSU2Irrep, FibonacciAnyon, IsingAnyon, FermionParity, FermionParity ⊠ FermionParity, Z3Irrep ⊠ Z4Irrep, FermionParity ⊠ U1Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ SU2Irrep, NewSU2Irrep ⊠ NewSU2Irrep, NewSU2Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ NewSU2Irrep, FibonacciAnyon ⊠ FibonacciAnyon ⊠ Z2Irrep, + A4Irrep ⊠ Z2Irrep, A4Irrep ⊠ SU2Irrep, Z2Element{0}, Z2Element{1}, Z3Element{0}, Z3Element{1}, Z3Element{2}, Z4Element{0}, Z4Element{1}, Z4Element{2}, Z3Element{1} ⊠ SU2Irrep, FibonacciAnyon ⊠ Z4Element{3}, TimeReversed{Z2Irrep}, - TimeReversed{Z3Irrep}, TimeReversed{Z4Irrep}, + TimeReversed{Z3Irrep}, TimeReversed{Z4Irrep}, TimeReversed{A4Irrep}, TimeReversed{U1Irrep}, TimeReversed{CU1Irrep}, TimeReversed{SU2Irrep}, TimeReversed{FibonacciAnyon}, TimeReversed{IsingAnyon}, TimeReversed{FermionParity}, @@ -46,6 +47,28 @@ using .SectorTestSuite end end +@testset "Intertwiner relation for A4Irrep" begin + ω = cis(2π / 3) + T3 = [1 0 0; 0 ω 0; 0 0 ω^2] + T(a::Int8) = (a == 3) ? T3 : hcat(ω^(a)) + S3 = 1 / 3 * [-1 2 2; 2 -1 2; 2 2 -1] + S(a::Int8) = (a == 3) ? S3 : hcat(1) + for a in smallset(A4Irrep), b in smallset(A4Irrep) + for c in ⊗(a, b) + C = fusiontensor(a, b, c) + Ta, Tb, Tc = T(a.n), T(b.n), T(c.n) + Sa, Sb, Sc = S(a.n), S(b.n), S(c.n) + for μ in 1:Nsymbol(a, b, c) + Cmat = reshape(view(C, :, :, :, μ), (dim(a) * dim(b), dim(c))) + L_T = Cmat' * kron(Ta, Tb) * Cmat + L_S = Cmat' * kron(Sa, Sb) * Cmat + @test isapprox(L_T, Tc; atol = 1.0e-12) + @test isapprox(L_S, Sc; atol = 1.0e-12) + end + end + end +end + @testset "Deligne product" begin sectorlist′ = (Trivial, sectorlist...) for I1 in sectorlist′, I2 in sectorlist′