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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/TensorKitSectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ module TensorKitSectors

# exports
# -------
export Sector, Group, AbstractIrrep
export Irrep
export Sector, Group, AbstractIrrep, AbstractGroupElement
export Irrep, GroupElement

export Nsymbol, Fsymbol, Rsymbol, Asymbol, Bsymbol
export sectorscalartype
export dim, sqrtdim, invsqrtdim, frobeniusschur, twist, fusiontensor, dual
export otimes, deligneproduct, times
export FusionStyle, UniqueFusion, MultipleFusion, SimpleFusion, GenericFusion,
MultiplicityFreeFusion
export BraidingStyle, NoBraiding, SymmetricBraiding, Bosonic, Fermionic, Anyonic
export BraidingStyle, NoBraiding, HasBraiding, SymmetricBraiding, Bosonic, Fermionic, Anyonic
export SectorSet, SectorValues, findindex
export rightone, leftone

export triangle_equation, pentagon_equation, hexagon_equation

export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep
export ZNElement, Z2Element, Z3Element, Z4Element
export ProductSector, TimeReversed
export FermionParity, FermionNumber, FermionSpin
export PlanarTrivial, FibonacciAnyon, IsingAnyon
Expand Down Expand Up @@ -48,6 +49,7 @@ include("sectors.jl")
include("trivial.jl")
include("groups.jl")
include("irreps.jl") # irreps of symmetry groups, with bosonic braiding
include("groupelements.jl") # group elements with cocycles, no braiding
include("product.jl") # direct product of different sectors
include("fermions.jl") # irreps with defined fermionparity and fermionic braiding
include("anyons.jl") # non-group sectors
Expand Down
158 changes: 158 additions & 0 deletions src/groupelements.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Sectors corresponding to the elements of a group, possibly with a nontrivial
# associator given by a 3-cocycle.
#------------------------------------------------------------------------------#
"""
abstract type AbstractGroupElement{G<:Group} <: Sector end

Abstract supertype for sectors which corresponds to group elements of a group `G`.

Actual concrete implementations of those irreps can be obtained as `Element[G]`, or via their
actual name, which generically takes the form `(asciiG)Element`, i.e. the ASCII spelling of
the group name followed by `Element`.

All group elements have [`FusionStyle`](@ref) equal to `UniqueFusion()`.
Furthermore, the [`BraidingStyle`](@ref) is set to `NoBraiding()`, although this can be
overridden by a concrete implementation of `AbstractGroupElement`.

For the fusion structure, a specific `SomeGroupElement<:AbstractGroupElement{SomeGroup}`
should only implement the following methods
```julia
Base.:*(c1::GroupElement, c2::GroupElement) -> GroupElement
Base.one(::Type{GroupElement}) -> GroupElement
Base.inv(c::GroupElement) -> GroupElement
# and optionally
TensorKitSectors.cocycle(c1::GroupElement, c2::GroupElement, c3::GroupElement) -> Number
```
The methods `conj`, `dual`, `⊗`, `Nsymbol`, `Fsymbol`, `dim`, `Asymbol`, `Bsymbol` and
`frobeniusschur` will then be automatically defined. If no `cocycle` method is defined,
the cocycle will be assumed to be trivial, i.e. equal to `1`.

"""
abstract type AbstractGroupElement{G <: Group} <: Sector end
FusionStyle(::Type{<:AbstractGroupElement}) = UniqueFusion()
BraidingStyle(::Type{<:AbstractGroupElement}) = NoBraiding()

cocycle(a::I, b::I, c::I) where {I <: AbstractGroupElement} = 1
⊗(a::I, b::I) where {I <: AbstractGroupElement} = (a * b,)
Base.one(a::AbstractGroupElement) = one(typeof(a))
Base.conj(a::AbstractGroupElement) = inv(a)
Nsymbol(a::I, b::I, c::I) where {I <: AbstractGroupElement} = c == a * b
function Fsymbol(a::I, b::I, c::I, d::I, e::I, f::I) where {I <: AbstractGroupElement}
ω = cocycle(a, b, c)
if e == a * b && d == e * c && f == b * c
return ω
else
return zero(ω)
end
end
dim(c::AbstractGroupElement) = 1
frobeniusschur(c::AbstractGroupElement) = cocycle(c, inv(c), c)
function Asymbol(a::I, b::I, c::I) where {I <: AbstractGroupElement}
A = frobeniusschur(dual(a)) * cocycle(inv(a), a, b)
return c == a * b ? A : zero(A)
end
function Bsymbol(a::I, b::I, c::I) where {I <: AbstractGroupElement}
B = cocycle(a, b, inv(b))
return c == a * b ? B : zero(B)
end

struct ElementTable end
"""
const GroupElement

A constant of a singleton type used as `GroupElement[G]` or `GroupElement[G,ω]` with `G<:Group`
a type of group, to construct or obtain a concrete subtype of `AbstractElement{G}`
that implements the data structure used to represent elements of the group `G`, possibly
with a second argument `ω` that specifies the associated 3-cocycle.
"""
const GroupElement = ElementTable()

function Base.show(io::IO, c::I) where {G <: Group, I <: AbstractGroupElement{G}}
return if get(io, :typeinfo, nothing) !== I
print(io, type_repr(I), "(")
for k in 1:fieldcount(I)
k > 1 && print(io, ", ")
print(io, getfield(c, k))
end
print(io, ")")
else
fieldcount(I) > 1 && print(io, "(")
for k in 1:fieldcount(I)
k > 1 && print(io, ", ")
print(io, getfield(c, k))
end
fieldcount(I) > 1 && print(io, ")")
end
end


# ZNElement: elements of Z_N are labelled by integers mod N; do we ever want N > 64?
"""
struct ZNElement{N, p} <: AbstractGroupElement{ℤ{N}}
ZNElement{N, p}(n::Integer)
GroupElement[ℤ{N}, p](n::Integer)

Represents an element of the group ``ℤ_N`` for some value of `N<64`. (We need 2*(N-1) <= 127 in
order for a ⊗ b to work correctly.) For `N` equals `2`, `3` or `4`, `ℤ{N}` can be replaced
by `ℤ₂`, `ℤ₃`, `ℤ₄`. An arbitrary `Integer` `n` can be provided to the constructor, but only
the value `mod(n, N)` is relevant. The second type parameter `p` should also be specified as
an integer ` 0 <= p < N` and specifies the 3-cocycle, which is then being given by

```julia
cocycle(a, b, c) = cispi(2 * p * a.n * (b.n + c.n - mod(b.n + c.n, N)) / N))
```

If `p` is not specified, it defaults to `0`, i.e. the trivial cocycle.

## Fields
- `n::Int8`: the integer label of the element, modulo `N`.
"""
struct ZNElement{N, p} <: AbstractGroupElement{ℤ{N}}
n::Int8
function ZNElement{N, p}(n::Integer) where {N, p}
@assert N < 64
@assert 0 <= p < N
return new{N, p}(mod(n, N))
end
end
ZNElement{N}(n::Integer) where {N} = ZNElement{N, 0}(n)
Base.getindex(::ElementTable, ::Type{ℤ{N}}, p::Int = 0) where {N} = ZNElement{N, mod(p, N)}
type_repr(::Type{ZNElement{N, p}}) where {N, p} = "GroupElement[ℤ{$N}, $p]"

Base.convert(T::Type{<:ZNElement}, n::Real) = T(n)
const Z2Element{p} = ZNElement{2, p}
const Z3Element{p} = ZNElement{3, p}
const Z4Element{p} = ZNElement{4, p}

Base.one(::Type{Z}) where {Z <: ZNElement} = Z(0)
Base.inv(c::ZNElement) = typeof(c)(-c.n)
Base.:*(c1::ZNElement{N, p}, c2::ZNElement{N, p}) where {N, p} =
ZNElement{N, p}(mod(c1.n + c2.n, N))

cocycle(a::ZNElement{2, 0}, b::ZNElement{2, 0}, c::ZNElement{2, 0}) = 1
function cocycle(a::ZNElement{N, p}, b::ZNElement{N, p}, c::ZNElement{N, p}) where {N, p}
return cispi(2 * p * a.n * (b.n + c.n - mod(b.n + c.n, N)) / N^2)
end

Base.IteratorSize(::Type{SectorValues{<:ZNElement}}) = HasLength()
Base.length(::SectorValues{<:ZNElement{N}}) where {N} = N
function Base.iterate(::SectorValues{ZNElement{N, p}}, i = 0) where {N, p}
return i == N ? nothing : (ZNElement{N, p}(i), i + 1)
end
function Base.getindex(::SectorValues{ZNElement{N, p}}, i::Int) where {N, p}
return 1 <= i <= N ? ZNElement{N, p}(i - 1) : throw(BoundsError(values(ZNElement{N, p}), i))
end
findindex(::SectorValues{ZNElement{N, p}}, c::ZNElement{N, p}) where {N, p} = c.n + 1

Base.hash(c::ZNElement, h::UInt) = hash(c.n, h)
Base.isless(c1::ZNElement{N, p}, c2::ZNElement{N, p}) where {N, p} = isless(c1.n, c2.n)

# Experimental
BraidingStyle(::Type{ZNElement{N, 0}}) where {N} = Bosonic()
Rsymbol(a::ZNElement{N, 0}, b::ZNElement{N, 0}, c::ZNElement{N, 0}) where {N} = ifelse(a * b == c, 1, zero(1))

BraidingStyle(::Type{ZNElement{N, p}}) where {N, p} = Anyonic()
function Rsymbol(a::ZNElement{N, p}, b::ZNElement{N, p}, c::ZNElement{N, p}) where {N, p}
R = cispi(2 * p * a.n * b.n / N^2)
return ifelse(c == a * b, R, zero(R))
end
2 changes: 1 addition & 1 deletion src/irreps.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ structure used to represent irreducible representations of the group `G`.
"""
const Irrep = IrrepTable()

type_repr(::Type{<:AbstractIrrep{G}}) where {G <: Group} = "Irrep[" * type_repr(G) * "]"
type_repr(::Type{IR}) where {G <: Group, IR <: AbstractIrrep{G}} = "Irrep[" * type_repr(G) * "]"
function Base.show(io::IO, c::AbstractIrrep)
I = typeof(c)
return if get(io, :typeinfo, nothing) !== I
Expand Down
10 changes: 5 additions & 5 deletions src/sectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -441,19 +441,19 @@ function hexagon_equation(a::I, b::I, c::I; kwargs...) where {I <: Sector}
for e in ⊗(c, a), f in ⊗(c, b)
for d in intersect(⊗(e, b), ⊗(a, f))
if FusionStyle(I) isa MultiplicityFreeFusion
p1 = Rsymbol(a, c, e) * Fsymbol(a, c, b, d, e, f) * Rsymbol(b, c, f)
p1 = Rsymbol(c, a, e) * Fsymbol(a, c, b, d, e, f) * Rsymbol(c, b, f)
p2 = zero(p1)
for g in ⊗(a, b)
p2 += Fsymbol(c, a, b, d, e, g) * Rsymbol(g, c, d) *
p2 += Fsymbol(c, a, b, d, e, g) * Rsymbol(c, g, d) *
Fsymbol(a, b, c, d, g, f)
end
else
@tensor p1[α, β, μ, ν] := Rsymbol(a, c, e)[α, λ] *
Fsymbol(a, c, b, d, e, f)[λ, β, γ, ν] * Rsymbol(b, c, f)[γ, μ]
@tensor p1[α, β, μ, ν] := Rsymbol(c, a, e)[α, λ] *
Fsymbol(a, c, b, d, e, f)[λ, β, γ, ν] * Rsymbol(c, b, f)[γ, μ]
p2 = zero(p1)
for g in ⊗(a, b)
@tensor p2[α, β, μ, ν] += Fsymbol(c, a, b, d, e, g)[α, β, δ, σ] *
Rsymbol(g, c, d)[σ, ψ] * Fsymbol(a, b, c, d, g, f)[δ, ψ, μ, ν]
Rsymbol(c, g, d)[σ, ψ] * Fsymbol(a, b, c, d, g, f)[δ, ψ, μ, ν]
end
end
isapprox(p1, p2; kwargs...) || return false
Expand Down
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ const sectorlist = (
FermionParity ⊠ SU2Irrep ⊠ SU2Irrep, NewSU2Irrep ⊠ NewSU2Irrep,
NewSU2Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ NewSU2Irrep,
FibonacciAnyon ⊠ FibonacciAnyon ⊠ Z2Irrep,
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{U1Irrep}, TimeReversed{CU1Irrep}, TimeReversed{SU2Irrep},
Expand Down
24 changes: 17 additions & 7 deletions test/sectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@ Istr = TKS.type_repr(I)
@constinferred dim(s[1])
@constinferred frobeniusschur(s[1])
@constinferred Nsymbol(s...)
R = @constinferred Rsymbol(s...)
B = @constinferred Bsymbol(s...)
F = @constinferred Fsymbol(s..., s...)
if FusionStyle(I) === SimpleFusion()
@test typeof(R * F) <: @constinferred sectorscalartype(I)
if BraidingStyle(I) isa HasBraiding
R = @constinferred Rsymbol(s...)
if FusionStyle(I) === SimpleFusion()
@test typeof(R * F) <: @constinferred sectorscalartype(I)
else
@test Base.promote_op(*, eltype(R), eltype(F)) <: @constinferred sectorscalartype(I)
end
else
@test Base.promote_op(*, eltype(R), eltype(F)) <: @constinferred sectorscalartype(I)
if FusionStyle(I) === SimpleFusion()
@test typeof(F) <: @constinferred sectorscalartype(I)
else
@test eltype(F) <: @constinferred sectorscalartype(I)
end
end
it = @constinferred s[1] ⊗ s[2]
@constinferred ⊗(s..., s...)
Expand Down Expand Up @@ -102,8 +110,10 @@ end
@test pentagon_equation(a, b, c, d; atol = 1.0e-12, rtol = 1.0e-12)
end
end
@testset "Sector $Istr: Hexagon equation" begin
for a in smallset(I), b in smallset(I), c in smallset(I)
@test hexagon_equation(a, b, c; atol = 1.0e-12, rtol = 1.0e-12)
if BraidingStyle(I) isa HasBraiding
@testset "Sector $Istr: Hexagon equation" begin
for a in smallset(I), b in smallset(I), c in smallset(I)
@test hexagon_equation(a, b, c; atol = 1.0e-12, rtol = 1.0e-12)
end
end
end
Loading