diff --git a/src/TensorKitSectors.jl b/src/TensorKitSectors.jl index ebe0e8c..5316f14 100644 --- a/src/TensorKitSectors.jl +++ b/src/TensorKitSectors.jl @@ -12,6 +12,7 @@ export otimes, deligneproduct, times export FusionStyle, UniqueFusion, MultipleFusion, SimpleFusion, GenericFusion, MultiplicityFreeFusion export UnitStyle, SimpleUnit, GenericUnit +export FusionDataStyle, TrivialFusionData, NonTrivialFusionData export BraidingStyle, NoBraiding, HasBraiding, SymmetricBraiding, Bosonic, Fermionic, Anyonic export SectorSet, SectorValues, findindex export unit, rightunit, leftunit, allunits, isunit diff --git a/src/anyons.jl b/src/anyons.jl index 075fdb4..f66a005 100644 --- a/src/anyons.jl +++ b/src/anyons.jl @@ -27,6 +27,7 @@ unit(::Type{PlanarTrivial}) = PlanarTrivial() dual(::PlanarTrivial) = PlanarTrivial() FusionStyle(::Type{PlanarTrivial}) = UniqueFusion() +FusionDataStyle(::Type{PlanarTrivial}) = TrivialFusionData() BraidingStyle(::Type{PlanarTrivial}) = NoBraiding() Base.isreal(::Type{PlanarTrivial}) = true @@ -82,6 +83,7 @@ const _goldenratio = Float64(MathConstants.golden) dim(a::FibonacciAnyon) = isunit(a) ? one(_goldenratio) : _goldenratio FusionStyle(::Type{FibonacciAnyon}) = SimpleFusion() +FusionDataStyle(::Type{FibonacciAnyon}) = NonTrivialFusionData() BraidingStyle(::Type{FibonacciAnyon}) = Anyonic() Base.isreal(::Type{FibonacciAnyon}) = false @@ -198,6 +200,7 @@ dual(s::IsingAnyon) = s dim(a::IsingAnyon) = a.s == :σ ? sqrt(2) : 1.0 FusionStyle(::Type{IsingAnyon}) = SimpleFusion() +FusionDataStyle(::Type{IsingAnyon}) = NonTrivialFusionData() BraidingStyle(::Type{IsingAnyon}) = Anyonic() Base.isreal(::Type{IsingAnyon}) = false diff --git a/src/fermions.jl b/src/fermions.jl index 1a0a2fa..4e453e9 100644 --- a/src/fermions.jl +++ b/src/fermions.jl @@ -35,6 +35,7 @@ dual(f::FermionParity) = f dim(f::FermionParity) = 1 FusionStyle(::Type{FermionParity}) = UniqueFusion() +FusionDataStyle(::Type{FermionParity}) = TrivialFusionData() BraidingStyle(::Type{FermionParity}) = Fermionic() Base.isreal(::Type{FermionParity}) = true diff --git a/src/groupelements.jl b/src/groupelements.jl index 667cea9..e4169f8 100644 --- a/src/groupelements.jl +++ b/src/groupelements.jl @@ -30,6 +30,7 @@ the cocycle will be assumed to be trivial, i.e. equal to `1`. """ abstract type AbstractGroupElement{G <: Group} <: Sector end FusionStyle(::Type{<:AbstractGroupElement}) = UniqueFusion() +FusionDataStyle(::Type{<:AbstractGroupElement}) = NonTrivialFusionData() BraidingStyle(::Type{<:AbstractGroupElement}) = NoBraiding() cocycle(a::I, b::I, c::I) where {I <: AbstractGroupElement} = 1 @@ -149,6 +150,7 @@ 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 +FusionDataStyle(::Type{ZNElement{N, p}}) where {N, p} = p == 0 ? TrivialFusionData() : NonTrivialFusionData() 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)) diff --git a/src/irreps/a4irrep.jl b/src/irreps/a4irrep.jl index 60ffb1f..8166bc3 100644 --- a/src/irreps/a4irrep.jl +++ b/src/irreps/a4irrep.jl @@ -18,6 +18,7 @@ struct A4Irrep <: AbstractIrrep{A₄} end FusionStyle(::Type{A4Irrep}) = GenericFusion() +FusionDataStyle(::Type{A4Irrep}) = NonTrivialFusionData() sectorscalartype(::Type{A4Irrep}) = Float64 unit(::Type{A4Irrep}) = A4Irrep(0) diff --git a/src/irreps/cu1irrep.jl b/src/irreps/cu1irrep.jl index ec16cf5..097c52e 100644 --- a/src/irreps/cu1irrep.jl +++ b/src/irreps/cu1irrep.jl @@ -107,6 +107,7 @@ end dim(c::CU1Irrep) = ifelse(c.j == zero(HalfInt), 1, 2) FusionStyle(::Type{CU1Irrep}) = SimpleFusion() +FusionDataStyle(::Type{CU1Irrep}) = NonTrivialFusionData() sectorscalartype(::Type{CU1Irrep}) = Float64 Base.isreal(::Type{CU1Irrep}) = true diff --git a/src/irreps/dnirrep.jl b/src/irreps/dnirrep.jl index fbbac03..b05ea98 100644 --- a/src/irreps/dnirrep.jl +++ b/src/irreps/dnirrep.jl @@ -44,6 +44,7 @@ function Base.getproperty(a::DNIrrep{N}, sym::Symbol) where {N} end FusionStyle(::Type{DNIrrep{N}}) where {N} = N < 3 ? UniqueFusion() : SimpleFusion() +FusionDataStyle(::Type{DNIrrep{N}}) where {N} = N < 3 ? TrivialFusionData() : NonTrivialFusionData() sectorscalartype(::Type{DNIrrep{N}}) where {N} = Float64 Base.isreal(::Type{DNIrrep{N}}) where {N} = true diff --git a/src/irreps/irreps.jl b/src/irreps/irreps.jl index 6175eb3..db2e5a8 100644 --- a/src/irreps/irreps.jl +++ b/src/irreps/irreps.jl @@ -48,6 +48,7 @@ end const AbelianIrrep{G} = AbstractIrrep{G} where {G <: AbelianGroup} FusionStyle(::Type{<:AbelianIrrep}) = UniqueFusion() +FusionDataStyle(::Type{<:AbelianIrrep}) = TrivialFusionData() Base.isreal(::Type{<:AbelianIrrep}) = true Nsymbol(a::I, b::I, c::I) where {I <: AbelianIrrep} = c == first(a ⊗ b) diff --git a/src/irreps/su2irrep.jl b/src/irreps/su2irrep.jl index d3f7d8c..b8dccf2 100644 --- a/src/irreps/su2irrep.jl +++ b/src/irreps/su2irrep.jl @@ -43,6 +43,7 @@ findindex(::SectorValues{SU2Irrep}, s::SU2Irrep) = twice(s.j) + 1 dim(s::SU2Irrep) = twice(s.j) + 1 FusionStyle(::Type{SU2Irrep}) = SimpleFusion() +FusionDataStyle(::Type{SU2Irrep}) = NonTrivialFusionData() sectorscalartype(::Type{SU2Irrep}) = Float64 Base.isreal(::Type{SU2Irrep}) = true diff --git a/src/multifusion.jl b/src/multifusion.jl index 1672b37..c61adec 100644 --- a/src/multifusion.jl +++ b/src/multifusion.jl @@ -56,6 +56,7 @@ function Base.convert(::Type{IsingAnyon}, a::IsingBimodule) # identify RepZ2 ⊕ end FusionStyle(::Type{IsingBimodule}) = SimpleFusion() # no multiplicities +FusionDataStyle(::Type{IsingBimodule}) = NonTrivialFusionData() BraidingStyle(::Type{IsingBimodule}) = NoBraiding() # because of module categories function Nsymbol(a::IsingBimodule, b::IsingBimodule, c::IsingBimodule) diff --git a/src/product.jl b/src/product.jl index 79ff700..66cb15f 100644 --- a/src/product.jl +++ b/src/product.jl @@ -215,6 +215,9 @@ end function FusionStyle(::Type{<:ProductSector{T}}) where {T <: SectorTuple} return mapreduce(FusionStyle, &, _sectors(T)) end +function FusionDataStyle(::Type{<:ProductSector{T}}) where {T <: SectorTuple} + return mapreduce(FusionDataStyle, &, _sectors(T)) +end function UnitStyle(::Type{<:ProductSector{T}}) where {T <: SectorTuple} return mapreduce(UnitStyle, &, _sectors(T)) end diff --git a/src/sectors.jl b/src/sectors.jl index cfef692..881b0bb 100644 --- a/src/sectors.jl +++ b/src/sectors.jl @@ -317,6 +317,35 @@ Base.:&(::SimpleFusion, ::UniqueFusion) = SimpleFusion() Base.:&(::GenericFusion, ::UniqueFusion) = GenericFusion() Base.:&(::GenericFusion, ::SimpleFusion) = GenericFusion() +# trait to differentiate between trivial and non-trivial F-symbols +""" + abstract type FusionDataStyle + FusionDataStyle(::Sector) + FusionDataStyle(I::Type{<:Sector}) + +Trait to describe the fusion behavior of sectors of type `I`, which can be either +* `TrivialFusionData()`: All F-symbols are trivial (equal to `0` or `1`); +* `NonTrivialFusionData()`: Some F-symbols are non-trivial; + + +This is most important for sectors with `FusionStyle(I) == UniqueFusion()`, where +`NonTrivialFusionData()` requires a proper evaluation of the F-symbols when manipulating +fusion trees, while `TrivialFusionData()` allows to skip all F-symbol evaluations. +""" +abstract type FusionDataStyle end +FusionDataStyle(a::Sector) = FusionDataStyle(typeof(a)) + +struct TrivialFusionData <: FusionDataStyle end +struct NonTrivialFusionData <: FusionDataStyle end + +# combine fusion data properties of tensor products of sectors +Base.:&(f::F, ::F) where {F <: FusionDataStyle} = f +Base.:&(f₁::FusionDataStyle, f₂::FusionDataStyle) = f₂ & f₁ + +Base.:&(::TrivialFusionData, ::TrivialFusionData) = TrivialFusionData() +Base.:&(::TrivialFusionData, ::NonTrivialFusionData) = NonTrivialFusionData() +Base.:&(::NonTrivialFusionData, ::NonTrivialFusionData) = NonTrivialFusionData() + # similar, but for multifusion categories """ abstract type UnitStyle diff --git a/src/timereversed.jl b/src/timereversed.jl index 99423f1..70b1c1b 100644 --- a/src/timereversed.jl +++ b/src/timereversed.jl @@ -18,6 +18,7 @@ struct TimeReversed{I <: Sector} <: Sector end end FusionStyle(::Type{TimeReversed{I}}) where {I <: Sector} = FusionStyle(I) +FusionDataStyle(::Type{TimeReversed{I}}) where {I <: Sector} = FusionDataStyle(I) BraidingStyle(::Type{TimeReversed{I}}) where {I <: Sector} = BraidingStyle(I) function Nsymbol( a::TimeReversed{I}, b::TimeReversed{I}, c::TimeReversed{I} diff --git a/src/trivial.jl b/src/trivial.jl index 642d22f..fa3e549 100644 --- a/src/trivial.jl +++ b/src/trivial.jl @@ -30,6 +30,7 @@ Base.isless(::Trivial, ::Trivial) = false ⊗(::Trivial, ::Trivial) = (Trivial(),) Nsymbol(::Trivial, ::Trivial, ::Trivial) = true FusionStyle(::Type{Trivial}) = UniqueFusion() +FusionDataStyle(::Type{Trivial}) = TrivialFusionData() Fsymbol(::Trivial, ::Trivial, ::Trivial, ::Trivial, ::Trivial, ::Trivial) = 1 fusiontensor(::Trivial, ::Trivial, ::Trivial) = fill(1, (1, 1, 1, 1)) diff --git a/test/multifusion.jl b/test/multifusion.jl index 189ca75..1b8621b 100644 --- a/test/multifusion.jl +++ b/test/multifusion.jl @@ -8,6 +8,7 @@ Istr = TensorKitSectors.type_repr(I) prodsec = I ⊠ Z2Irrep @test UnitStyle(prodsec) isa GenericUnit @test FusionStyle(prodsec) isa SimpleFusion + @test FusionDataStyle(prodsec) isa NonTrivialFusionData @test_throws DomainError unit(prodsec) @test length(allunits(prodsec)) == 2 end diff --git a/test/newsectors.jl b/test/newsectors.jl index 6381627..3f362ab 100644 --- a/test/newsectors.jl +++ b/test/newsectors.jl @@ -12,7 +12,7 @@ using HalfIntegers using WignerSymbols using TensorKitSectors -import TensorKitSectors: FusionStyle, BraidingStyle, Nsymbol, Fsymbol, Rsymbol, dim, +import TensorKitSectors: FusionStyle, BraidingStyle, FusionDataStyle, Nsymbol, Fsymbol, Rsymbol, dim, fusiontensor, ⊗, unit, dual struct NewSU2Irrep <: Sector @@ -35,6 +35,7 @@ Base.IteratorSize(::Type{SectorValues{NewSU2Irrep}}) = Base.IsInfinite() Base.iterate(::SectorValues{NewSU2Irrep}, i = 0) = (NewSU2Irrep(half(i)), i + 1) FusionStyle(::Type{NewSU2Irrep}) = GenericFusion() +FusionDataStyle(::Type{NewSU2Irrep}) = NonTrivialFusionData() BraidingStyle(::Type{NewSU2Irrep}) = Bosonic() Base.isreal(::Type{NewSU2Irrep}) = true diff --git a/test/sectors.jl b/test/sectors.jl index dcf7ef5..043ab77 100644 --- a/test/sectors.jl +++ b/test/sectors.jl @@ -30,6 +30,9 @@ using LinearAlgebra @test eltype(F) <: @testinferred sectorscalartype(I) end end + if FusionStyle(I) != UniqueFusion() + @test FusionDataStyle(I) === NonTrivialFusionData() + end @testinferred(s[1] ⊗ s[2]) @testinferred(⊗(s..., s...)) end