|
1 | | -# This file defines the abstract type AbstractSector |
2 | | -# all fusion categories (Z{2}, SU2, Ising...) are subtypes of AbstractSector |
3 | | - |
| 1 | +# This file defines the interface for type Sector |
| 2 | +# all fusion categories (Z{2}, SU2, Ising...) are subtypes of Sector |
4 | 3 | using TensorProducts: TensorProducts, ⊗ |
| 4 | +import TensorKitSectors as TKS |
5 | 5 |
|
6 | | -abstract type AbstractSector end |
| 6 | +""" |
| 7 | + SectorRange(sector::TKS.Sector) |
7 | 8 |
|
8 | | -# =================================== Base interface ===================================== |
9 | | -function Base.isless(c1::C, c2::C) where {C<:AbstractSector} |
10 | | - return isless(sector_label(c1), sector_label(c2)) |
| 9 | +Unit range with elements of type `Int` that additionally stores a sector to denote the grading. |
| 10 | +Equivalent to `Base.OneTo(length(sector))`. |
| 11 | +""" |
| 12 | +struct SectorRange{I<:TKS.Sector} <: AbstractUnitRange{Int} |
| 13 | + label::I |
11 | 14 | end |
12 | 15 |
|
13 | | -Base.length(s::AbstractSector) = quantum_dimension(s) |
| 16 | +label(r::SectorRange) = r.label |
| 17 | +sector_type(I::Type{<:SectorRange}) = I |
| 18 | + |
| 19 | +# =================================== Base interface ===================================== |
| 20 | + |
| 21 | +Base.length(r::SectorRange) = quantum_dimension(r) |
| 22 | + |
| 23 | +Base.isless(r1::SectorRange, r2::SectorRange) = isless(label(r1), label(r2)) |
| 24 | +Base.isless(r1::SectorRange, r2::TKS.Sector) = isless(label(r1), r2) |
| 25 | +Base.isless(r1::TKS.Sector, r2::SectorRange) = isless(r1, label(r2)) |
| 26 | + |
| 27 | +Base.isequal(r1::SectorRange, r2::SectorRange) = isequal(label(r1), label(r2)) |
| 28 | +Base.:(==)(r1::SectorRange, r2::SectorRange) = label(r1) == label(r2) |
| 29 | +Base.:(==)(r1::SectorRange, r2::TKS.Sector) = label(r1) == r2 |
| 30 | +Base.:(==)(r1::TKS.Sector, r2::SectorRange) = r1 == label(r2) |
| 31 | + |
| 32 | +Base.hash(r::SectorRange, h::UInt) = hash(label(r), h) |
| 33 | + |
| 34 | +Base.OneTo(r::SectorRange) = Base.OneTo(length(r)) |
| 35 | +Base.first(r::SectorRange) = first(Base.OneTo(r)) |
| 36 | +Base.last(r::SectorRange) = last(Base.OneTo(r)) |
| 37 | + |
| 38 | +function Base.show(io::IO, r::SectorRange{I}) where {I} |
| 39 | + show(io, typeof(r)) |
| 40 | + print(io, '(') |
| 41 | + l = sector_label(r) |
| 42 | + isnothing(l) || show(io, l) |
| 43 | + print(io, ')') |
| 44 | + return nothing |
| 45 | +end |
14 | 46 |
|
15 | 47 | # ================================= Sectors interface ==================================== |
| 48 | + |
16 | 49 | trivial(x) = trivial(typeof(x)) |
17 | 50 | function trivial(axis_type::Type{<:AbstractUnitRange}) |
18 | 51 | return gradedrange([trivial(sector_type(axis_type)) => 1]) # always returns nondual |
19 | 52 | end |
20 | 53 | function trivial(type::Type) |
21 | 54 | return error("`trivial` not defined for type $(type).") |
22 | 55 | end |
23 | | - |
24 | | -istrivial(c::AbstractSector) = (c == trivial(c)) |
25 | | - |
26 | | -function sector_label(c::AbstractSector) |
27 | | - return error("method `sector_label` not defined for type $(typeof(c))") |
| 56 | +trivial(::Type{SectorRange{I}}) where {I} = SectorRange{I}(one(I)) |
| 57 | +trivial(::Type{I}) where {I<:TKS.Sector} = one(I) |
| 58 | + |
| 59 | +istrivial(r::SectorRange) = isone(label(r)) |
| 60 | +istrivial(r) = (r == trivial(r)) |
| 61 | + |
| 62 | +sector_label(r::SectorRange) = sector_label(label(r)) |
| 63 | +function sector_label(c::TKS.Sector) |
| 64 | + return map(fieldnames(typeof(c))) do f |
| 65 | + return getfield(c, f) |
| 66 | + end |
| 67 | + return c |
28 | 68 | end |
29 | 69 |
|
30 | 70 | quantum_dimension(g::AbstractUnitRange) = length(g) |
31 | | -quantum_dimension(s::AbstractSector) = quantum_dimension(SymmetryStyle(s), s) |
32 | | - |
33 | | -function quantum_dimension(::NotAbelianStyle, c::AbstractSector) |
34 | | - return error("method `quantum_dimension` not defined for type $(typeof(c))") |
35 | | -end |
| 71 | +quantum_dimension(r::SectorRange) = quantum_dimension(label(r)) |
| 72 | +quantum_dimension(s::TKS.Sector) = TKS.dim(s) |
36 | 73 |
|
37 | | -quantum_dimension(::AbelianStyle, ::AbstractSector) = 1 |
| 74 | +to_sector(x::TKS.Sector) = SectorRange(x) |
38 | 75 |
|
39 | 76 | # convert to range |
40 | | -to_gradedrange(c::AbstractSector) = gradedrange([c => 1]) |
41 | | -to_gradedrange(sr::SectorUnitRange) = mortar_axis([sr]) |
42 | | -to_gradedrange(g::AbstractGradedUnitRange) = g |
| 77 | +to_gradedrange(c::SectorRange) = gradedrange([c => 1]) |
| 78 | +to_gradedrange(c::TKS.Sector) = to_gradedrange(SectorRange(c)) |
43 | 79 |
|
44 | | -function nsymbol(s1::AbstractSector, s2::AbstractSector, s3::AbstractSector) |
45 | | - full_space = to_gradedrange(s1 ⊗ s2) |
46 | | - i = findfirst(==(s3), sectors(full_space)) |
47 | | - isnothing(i) && return 0 |
48 | | - return sector_multiplicities(full_space)[i] |
| 80 | +function nsymbol(s1::SectorRange, s2::SectorRange, s3::SectorRange) |
| 81 | + return TKS.Nsymbol(label(s1), label(s2), label(s3)) |
49 | 82 | end |
50 | 83 |
|
| 84 | +dual(c::TKS.Sector) = TKS.dual(c) |
| 85 | +dual(r1::SectorRange) = typeof(r1)(dual(label(r1))) |
| 86 | + |
51 | 87 | # =============================== Fusion rule interface ================================== |
52 | | -function fusion_rule(c1::AbstractSector, c2::AbstractSector) |
53 | | - return fusion_rule(combine_styles(SymmetryStyle(c1), SymmetryStyle(c2)), c1, c2) |
54 | | -end |
55 | 88 |
|
56 | | -function fusion_rule(::NotAbelianStyle, c1::C, c2::C) where {C<:AbstractSector} |
57 | | - sector_degen_pairs = label_fusion_rule(C, sector_label(c1), sector_label(c2)) |
58 | | - return gradedrange(sector_degen_pairs) |
59 | | -end |
| 89 | +TKS.FusionStyle(::Type{SectorRange{I}}) where {I} = TKS.FusionStyle(I) |
| 90 | +TKS.BraidingStyle(::Type{SectorRange{I}}) where {I} = TKS.BraidingStyle(I) |
60 | 91 |
|
61 | | -# abelian case: return Sector |
62 | | -function fusion_rule(::AbelianStyle, c1::C, c2::C) where {C<:AbstractSector} |
63 | | - return only(sectors(fusion_rule(NotAbelianStyle(), c1, c2))) |
64 | | -end |
| 92 | +abstract type SymmetryStyle end |
| 93 | + |
| 94 | +struct AbelianStyle <: SymmetryStyle end |
| 95 | +struct NotAbelianStyle <: SymmetryStyle end |
65 | 96 |
|
66 | | -function label_fusion_rule(sector_type::Type{<:AbstractSector}, l1, l2) |
67 | | - return [abelian_label_fusion_rule(sector_type, l1, l2) => 1] |
| 97 | +SymmetryStyle(x) = SymmetryStyle(typeof(x)) |
| 98 | + |
| 99 | +# default SymmetryStyle to AbelianStyle |
| 100 | +# allows for abelian-like slicing style for GradedUnitRange: assume length(::label) = 1 |
| 101 | +# and preserve labels in any slicing operation |
| 102 | +SymmetryStyle(T::Type) = AbelianStyle() |
| 103 | +function SymmetryStyle(::Type{T}) where {T<:SectorRange} |
| 104 | + if TKS.FusionStyle(T) === TKS.UniqueFusion() && TKS.BraidingStyle(T) === TKS.Bosonic() |
| 105 | + return AbelianStyle() |
| 106 | + else |
| 107 | + return NotAbelianStyle() |
| 108 | + end |
| 109 | +end |
| 110 | +SymmetryStyle(G::Type{<:AbstractUnitRange}) = SymmetryStyle(sector_type(G)) |
| 111 | + |
| 112 | +combine_styles(::AbelianStyle, ::AbelianStyle) = AbelianStyle() |
| 113 | +combine_styles(::SymmetryStyle, ::SymmetryStyle) = NotAbelianStyle() |
| 114 | + |
| 115 | +function fusion_rule(r1::SectorRange, r2::SectorRange) |
| 116 | + a = label(r1) |
| 117 | + b = label(r2) |
| 118 | + fstyle = TKS.FusionStyle(typeof(r1)) & TKS.FusionStyle(typeof(r2)) |
| 119 | + fstyle === TKS.UniqueFusion() && return SectorRange(only(TKS.otimes(a, b))) |
| 120 | + return gradedrange( |
| 121 | + vec([SectorRange(c) => TKS.Nsymbol(a, b, c) for c in TKS.otimes(a, b)]) |
| 122 | + ) |
68 | 123 | end |
69 | 124 |
|
70 | 125 | # ============================= TensorProducts interface =====--========================== |
71 | | -TensorProducts.tensor_product(s::AbstractSector) = s |
72 | 126 |
|
73 | | -function TensorProducts.tensor_product(c1::AbstractSector, c2::AbstractSector) |
74 | | - return fusion_rule(c1, c2) |
| 127 | +TensorProducts.tensor_product(s::SectorRange) = s |
| 128 | +TensorProducts.tensor_product(c1::SectorRange, c2::SectorRange) = fusion_rule(c1, c2) |
| 129 | +function TensorProducts.tensor_product(c1::TKS.Sector, c2::TKS.Sector) |
| 130 | + return tensor_product(to_sector(c1), to_sector(c2)) |
| 131 | +end |
| 132 | +function TensorProducts.tensor_product(c1::SectorRange, c2::TKS.Sector) |
| 133 | + return tensor_product(c1, to_sector(c2)) |
| 134 | +end |
| 135 | +function TensorProducts.tensor_product(c1::TKS.Sector, c2::SectorRange) |
| 136 | + return tensor_product(to_sector(c1), c2) |
| 137 | +end |
| 138 | + |
| 139 | +# ===================================== Sectors =========================================== |
| 140 | + |
| 141 | +const TrivialSector = SectorRange{TKS.Trivial} |
| 142 | +TrivialSector() = TrivialSector(TKS.Trivial()) |
| 143 | +sector_label(::TKS.Trivial) = nothing |
| 144 | +function fusion_rule(::TrivialSector, r::SectorRange) |
| 145 | + return TKS.FusionStyle(label(r)) === TKS.UniqueFusion() ? r : to_gradedrange(r) |
| 146 | +end |
| 147 | +function fusion_rule(r::SectorRange, ::TrivialSector) |
| 148 | + return TKS.FusionStyle(label(r)) === TKS.UniqueFusion() ? r : to_gradedrange(r) |
| 149 | +end |
| 150 | +fusion_rule(r::TrivialSector, ::TrivialSector) = r |
| 151 | + |
| 152 | +Base.:(==)(::TrivialSector, ::TrivialSector) = true |
| 153 | +Base.:(==)(::TrivialSector, r::SectorRange) = isone(label(r)) |
| 154 | +Base.:(==)(r::SectorRange, ::TrivialSector) = isone(label(r)) |
| 155 | +Base.isless(::TrivialSector, ::TrivialSector) = false |
| 156 | +Base.isless(::TrivialSector, r::SectorRange) = trivial(r) < r |
| 157 | +Base.isless(r::SectorRange, ::TrivialSector) = r < trivial(r) |
| 158 | + |
| 159 | +# use promotion to handle trivial sectors in tensor products |
| 160 | +Base.promote_rule(::Type{TrivialSector}, ::Type{T}) where {T<:SectorRange} = T |
| 161 | +Base.convert(::Type{T}, ::TrivialSector) where {T<:SectorRange} = trivial(T) |
| 162 | + |
| 163 | +const Z{N} = SectorRange{TKS.ZNIrrep{N}} |
| 164 | +sector_label(c::TKS.ZNIrrep) = c.n |
| 165 | +modulus(::Z{N}) where {N} = N |
| 166 | +const Z2 = Z{2} |
| 167 | + |
| 168 | +const U1 = SectorRange{TKS.U1Irrep} |
| 169 | +sector_label(c::TKS.U1Irrep) = c.charge |
| 170 | +Base.isless(r1::U1, r2::U1) = isless(sector_label(r1), sector_label(r2)) |
| 171 | + |
| 172 | +const O2 = SectorRange{TKS.CU1Irrep} |
| 173 | +function O2(l::Real) |
| 174 | + j = max(l, zero(l)) |
| 175 | + s = if l == 0 |
| 176 | + 0 |
| 177 | + elseif l == -1 |
| 178 | + 1 |
| 179 | + else |
| 180 | + 2 |
| 181 | + end |
| 182 | + return O2(TKS.CU1Irrep(j, s)) |
| 183 | +end |
| 184 | +function sector_label(c::TKS.CU1Irrep) |
| 185 | + return if c.s == 0 |
| 186 | + c.j |
| 187 | + elseif c.s == 1 |
| 188 | + oftype(c.j, -1) |
| 189 | + else |
| 190 | + c.j |
| 191 | + end |
75 | 192 | end |
76 | 193 |
|
77 | | -# ================================ GradedUnitRanges interface ================================== |
78 | | -sector_type(S::Type{<:AbstractSector}) = S |
| 194 | +const SU2 = SectorRange{TKS.SU2Irrep} |
| 195 | +sector_label(c::TKS.SU2Irrep) = c.j |
| 196 | + |
| 197 | +const Fib = SectorRange{TKS.FibonacciAnyon} |
| 198 | +function Fib(s::AbstractString) |
| 199 | + s == "1" && return Fib(:I) |
| 200 | + s == "τ" && return Fib(:τ) |
| 201 | + throw(ArgumentError("Unrecognized input `$s`")) |
| 202 | +end |
| 203 | +sector_label(c::TKS.FibonacciAnyon) = c.isone ? "1" : "τ" |
79 | 204 |
|
80 | | -function findfirstblock(g::AbstractGradedUnitRange, s::AbstractSector) |
81 | | - return findfirstblock_sector(g::AbstractGradedUnitRange, s) |
| 205 | +const Ising = SectorRange{TKS.IsingAnyon} |
| 206 | +function Ising(s::AbstractString) |
| 207 | + s in ("1", "σ", "ψ") || throw(ArgumentError("Unrecognized input `$s`")) |
| 208 | + sym = s == "1" ? :I : Symbol(s) |
| 209 | + return Ising(sym) |
82 | 210 | end |
| 211 | +sector_label(c::TKS.IsingAnyon) = Symbol(c.s) == :I ? "1" : String(c.s) |
0 commit comments