Skip to content

Commit 7c03f1a

Browse files
lkdvosmtfishman
andauthored
Sectors refactor (#8)
Co-authored-by: Matt Fishman <[email protected]>
1 parent a0e5ade commit 7c03f1a

29 files changed

+706
-961
lines changed

Project.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "GradedArrays"
22
uuid = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2"
33
authors = ["ITensor developers <[email protected]> and contributors"]
4-
version = "0.4.26"
4+
version = "0.5.0"
55

66
[deps]
77
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -15,9 +15,16 @@ MatrixAlgebraKit = "6c742aac-3347-4629-af66-fc926824e5e4"
1515
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1616
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
1717
TensorAlgebra = "68bd88dc-f39d-4e12-b2ca-f046b68fcc6a"
18+
TensorKitSectors = "13a9c161-d5da-41f0-bcbd-e1a08ae0647f"
1819
TensorProducts = "decf83d6-1968-43f4-96dc-fdb3fe15fc6d"
1920
TypeParameterAccessors = "7e5a90cf-f82e-492e-a09b-e3e26432c138"
2021

22+
[weakdeps]
23+
SUNRepresentations = "1a50b95c-7aac-476d-a9ce-2bfc675fc617"
24+
25+
[extensions]
26+
GradedArraysSUNRepresentationsExt = "SUNRepresentations"
27+
2128
[compat]
2229
ArrayLayouts = "1"
2330
BlockArrays = "1.6"
@@ -28,8 +35,10 @@ HalfIntegers = "1.6"
2835
LinearAlgebra = "1.10"
2936
MatrixAlgebraKit = "0.2, 0.3, 0.4, 0.5"
3037
Random = "1.10"
38+
SUNRepresentations = "0.3"
3139
SplitApplyCombine = "1.2.3"
3240
TensorAlgebra = "0.3.2, 0.4"
41+
TensorKitSectors = "0.1, 0.2"
3342
TensorProducts = "0.1.3"
3443
TypeParameterAccessors = "0.4"
3544
julia = "1.10"

docs/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
55

66
[compat]
77
Documenter = "1"
8-
GradedArrays = "0.4"
8+
GradedArrays = "0.5"
99
Literate = "2"

examples/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
GradedArrays = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2"
33

44
[compat]
5-
GradedArrays = "0.4"
5+
GradedArrays = "0.5"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module GradedArraysSUNRepresentationsExt
2+
3+
using SUNRepresentations: SUNIrrep
4+
using GradedArrays: GradedArrays, SectorRange
5+
6+
function GradedArrays.SectorRange{SUNIrrep{N}}::NTuple{M,Int}) where {N,M}
7+
M + 1 == N || throw(ArgumentError("Length of λ must be N-1 for SU(N) irreps"))
8+
return SectorRange(SUNIrrep((λ..., 0)))
9+
end
10+
GradedArrays.sector_label(c::SUNIrrep) = Base.front(c.I)
11+
12+
end

src/GradedArrays.jl

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
module GradedArrays
22

33
include("gradedunitrange_interface.jl")
4-
include("symmetry_style.jl")
54

5+
include("abstractsector.jl")
66
include("sectorunitrange.jl")
77
include("gradedunitrange.jl")
88

9-
include("abstractsector.jl")
10-
include("sector_definitions/fib.jl")
11-
include("sector_definitions/ising.jl")
12-
include("sector_definitions/o2.jl")
13-
include("sector_definitions/trivial.jl")
14-
include("sector_definitions/su.jl")
15-
include("sector_definitions/su2k.jl")
16-
include("sector_definitions/u1.jl")
17-
include("sector_definitions/zn.jl")
189
include("namedtuple_operations.jl")
1910
include("sector_product.jl")
2011

@@ -23,10 +14,8 @@ include("gradedarray.jl")
2314
include("tensoralgebra.jl")
2415
include("factorizations.jl")
2516

26-
export SU2,
27-
U1,
28-
Z,
29-
dag,
17+
export TrivialSector, Z, Z2, U1, O2, SU2, Fib, Ising
18+
export dag,
3019
dual,
3120
flip,
3221
gradedrange,

src/abstractsector.jl

Lines changed: 176 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,211 @@
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
43
using TensorProducts: TensorProducts,
4+
import TensorKitSectors as TKS
55

6-
abstract type AbstractSector end
6+
"""
7+
SectorRange(sector::TKS.Sector)
78
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
1114
end
1215

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
1446

1547
# ================================= Sectors interface ====================================
48+
1649
trivial(x) = trivial(typeof(x))
1750
function trivial(axis_type::Type{<:AbstractUnitRange})
1851
return gradedrange([trivial(sector_type(axis_type)) => 1]) # always returns nondual
1952
end
2053
function trivial(type::Type)
2154
return error("`trivial` not defined for type $(type).")
2255
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
2868
end
2969

3070
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)
3673

37-
quantum_dimension(::AbelianStyle, ::AbstractSector) = 1
74+
to_sector(x::TKS.Sector) = SectorRange(x)
3875

3976
# 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))
4379

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))
4982
end
5083

84+
dual(c::TKS.Sector) = TKS.dual(c)
85+
dual(r1::SectorRange) = typeof(r1)(dual(label(r1)))
86+
5187
# =============================== 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
5588

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)
6091

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
6596

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+
)
68123
end
69124

70125
# ============================= TensorProducts interface =====--==========================
71-
TensorProducts.tensor_product(s::AbstractSector) = s
72126

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
75192
end
76193

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" : "τ"
79204

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)
82210
end
211+
sector_label(c::TKS.IsingAnyon) = Symbol(c.s) == :I ? "1" : String(c.s)

src/factorizations.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ function fluxify(A, Aaxes, charge; side::Symbol=:domain)
5858
istrivial(charge) && return TensorAlgebra.unmatricize(A, (Aaxes[1],), (Aaxes[2],))
5959

6060
if side === :domain
61-
A′ = TensorAlgebra.unmatricize(A, (Aaxes[1],), (Aaxes[2], to_gradedrange(charge)))
61+
A′ = TensorAlgebra.unmatricize(A, (Aaxes[1],), (Aaxes[2], to_gradedrange(dual(charge))))
6262
else
63-
A′ = TensorAlgebra.unmatricize(A, (to_gradedrange(charge), Aaxes[1]), (Aaxes[2],))
63+
A′ = TensorAlgebra.unmatricize(A, (to_gradedrange(dual(charge)), Aaxes[1]), (Aaxes[2],))
6464
end
6565

6666
A″ = similar(A′, Aaxes)

src/fusion.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,22 @@ end
2828

2929
# allow to fuse a Sector with a GradedUnitRange
3030
function TensorProducts.tensor_product(
31-
s::Union{AbstractSector,SectorUnitRange}, g::AbstractGradedUnitRange
31+
s::Union{SectorRange,SectorUnitRange}, g::AbstractGradedUnitRange
3232
)
3333
return to_gradedrange(s) g
3434
end
3535

3636
function TensorProducts.tensor_product(
37-
g::AbstractGradedUnitRange, s::Union{AbstractSector,SectorUnitRange}
37+
g::AbstractGradedUnitRange, s::Union{SectorRange,SectorUnitRange}
3838
)
3939
return g to_gradedrange(s)
4040
end
4141

42-
function TensorProducts.tensor_product(sr::SectorUnitRange, s::AbstractSector)
42+
function TensorProducts.tensor_product(sr::SectorUnitRange, s::SectorRange)
4343
return sr sectorrange(s, 1)
4444
end
4545

46-
function TensorProducts.tensor_product(s::AbstractSector, sr::SectorUnitRange)
46+
function TensorProducts.tensor_product(s::SectorRange, sr::SectorUnitRange)
4747
return sectorrange(s, 1) sr
4848
end
4949

0 commit comments

Comments
 (0)