11# Deligne tensor product of different sectors: ⊠
22# ------------------------------------------------------------------------------#
33const SectorTuple = Tuple{Vararg{Sector}}
4+ const SectorNamedTuple = NamedTuple{<: Any , <: SectorTuple }
5+ const AnySectorTuple = Union{SectorTuple, SectorNamedTuple}
46
57"""
6- ProductSector{T<: SectorTuple}
8+ ProductSector{T <: Union{ SectorTuple, SectorNamedTuple }
79
8- Represents the Deligne tensor product of sectors. The type parameter `T` is a tuple of the
9- component sectors. The recommended way to construct a `ProductSector` is using the
10+ Represents the Deligne tensor product of sectors. The type parameter `T` is a (named) tuple
11+ of the component sectors. The recommended way to construct a `ProductSector` is using the
1012[`deligneproduct`](@ref) (`⊠`) operator on the components.
13+
14+ It is also possible to use names for the different factors in the product, either by passing
15+ a `NamedTuple` of `Sector`s to the `ProductSector` constructor, or by making use of the
16+ [`@NamedProductSector`](@ref) macro.
1117"""
12- struct ProductSector{T <: SectorTuple } <: Sector
18+ struct ProductSector{T <: AnySectorTuple } <: Sector
1319 sectors:: T
20+ ProductSector {T} (x:: T ) where {T} = new {T} (x)
1421end
1522
16- Base. Tuple (a:: ProductSector ) = a. sectors
23+ ProductSector (x:: AnySectorTuple ) = ProductSector {typeof(x)} (x)
24+ ProductSector (x, y, z... ) = ProductSector ((x, y, z... ))
25+ ProductSector (x:: Sector ) = ProductSector ((x,))
26+
27+ ProductSector {T} (x) where {T} = ProductSector {T} (convert (T, x))
28+ ProductSector {T} (x, y... ) where {T} = ProductSector {T} ((x, y... ))
29+ ProductSector {T} (x:: Sector ) where {T} = ProductSector {T} ((x,))
30+
31+ const TupleProductSector{T <: SectorTuple } = ProductSector{T}
32+ const NamedProductSector{T <: SectorTuple } = ProductSector{<: NamedTuple{<:Any, T} }
33+
34+ TupleProductSector (s:: NamedProductSector ) = ProductSector (values (s. sectors))
35+
36+ Base. Tuple (a:: ProductSector ) = Tuple (a. sectors)
1737
1838Base. getindex (s:: ProductSector , i:: Int ) = getindex (s. sectors, i)
1939Base. length (s:: ProductSector ) = length (s. sectors)
2040Base. iterate (s:: ProductSector , args... ) = iterate (s. sectors, args... )
2141Base. indexed_iterate (s:: ProductSector , args... ) = Base. indexed_iterate (s. sectors, args... )
2242
43+ Base. @constprop :aggressive function Base. getproperty (s:: NamedProductSector , f:: Symbol )
44+ return f === :sectors ? getfield (s, f) : getproperty (s. sectors, f)
45+ end
46+ Base. propertynames (s:: NamedProductSector ) = (:sectors , propertynames (fieldtype (typeof (s), :sectors ))... )
47+
48+ _sectors (:: Type{NamedTuple{K, V}} ) where {K, V} = _sectors (V)
2349_sectors (:: Type{Tuple{}} ) = ()
2450Base. @pure function _sectors (:: Type{T} ) where {T <: SectorTuple }
2551 return (Base. tuple_type_head (T), _sectors (Base. tuple_type_tail (T))... )
2652end
2753
28- function Base. IteratorSize (:: Type{SectorValues{ProductSector{T}}} ) where {T <: SectorTuple }
54+ function Base. IteratorSize (:: Type{SectorValues{ProductSector{T}}} ) where {T}
2955 return Base. IteratorSize (Base. Iterators. product (map (values, _sectors (T))... ))
3056end
31- function Base. size (:: SectorValues{ProductSector{T}} ) where {T <: SectorTuple }
57+ function Base. size (:: SectorValues{ProductSector{T}} ) where {T}
3258 return map (s -> length (values (s)), _sectors (T))
3359end
3460Base. length (P:: SectorValues{<:ProductSector} ) = * (size (P)... )
3561
3662function _length (iter:: SectorValues{I} ) where {I <: Sector }
3763 return Base. IteratorSize (iter) === Base. IsInfinite () ? typemax (Int) : length (iter)
3864end
39- function _size (:: SectorValues{ProductSector{T}} ) where {T <: SectorTuple }
65+ function _size (:: SectorValues{ProductSector{T}} ) where {T}
4066 return map (s -> _length (values (s)), _sectors (T))
4167end
42- function Base. getindex (P:: SectorValues{ProductSector{T}} , i:: Int ) where {T <: SectorTuple }
68+ function Base. getindex (P:: SectorValues{ProductSector{T}} , i:: Int ) where {T}
4369 I = manhattan_to_multidimensional_index (i, _size (P))
4470 return ProductSector {T} (getindex .(values .(_sectors (T)), I))
4571end
46- function findindex (
47- P:: SectorValues{ProductSector{T}} ,
48- c:: ProductSector{T}
49- ) where {T <: SectorTuple }
50- return to_manhattan_index (findindex .(values .(_sectors (T)), c. sectors), _size (P))
72+ function findindex (P:: SectorValues{ProductSector{T}} , c:: ProductSector{T} ) where {T}
73+ return to_manhattan_index (findindex .(values .(_sectors (T)), Tuple (c. sectors)), _size (P))
5174end
5275
53- function Base. iterate (P:: SectorValues{ProductSector{T}} , i = 1 ) where {T <: SectorTuple }
76+ function Base. iterate (P:: SectorValues{<: ProductSector} , i = 1 )
5477 Base. IteratorSize (P) != Base. IsInfinite () && i > length (P) && return nothing
5578 return getindex (P, i), i + 1
5679end
5780
58- ProductSector {T} (args... ) where {T <: SectorTuple } = ProductSector {T} (args)
5981function Base. convert (:: Type{ProductSector{T}} , t:: Tuple ) where {T <: SectorTuple }
6082 return ProductSector {T} (convert (T, t))
6183end
84+ function Base. convert (P:: Type{<:NamedProductSector{T}} , t:: Tuple ) where {T <: SectorTuple }
85+ return P (convert (T, t))
86+ end
6287
6388function unit (:: Type{T} ) where {T <: ProductSector }
6489 UnitStyle (T) === GenericUnit () && throw_genericunit_error (T)
@@ -83,12 +108,13 @@ function Nsymbol(a::P, b::P, c::P) where {P <: ProductSector}
83108end
84109
85110_firstsector (x:: ProductSector ) = x. sectors[1 ]
86- _tailsector (x:: ProductSector ) = ProductSector (Base. tail (x. sectors))
111+ _tailsector (x:: ProductSector ) = ProductSector (Base. tail (Tuple ( x. sectors) ))
87112
88113function Fsymbol (a:: P , b:: P , c:: P , d:: P , e:: P , f:: P ) where {P <: ProductSector }
89114 heads = map (_firstsector, (a, b, c, d, e, f))
90- tails = map (_tailsector, (a, b, c, d, e, f))
91115 F₁ = Fsymbol (heads... )
116+ length (a) == 1 && return F₁
117+ tails = map (_tailsector, (a, b, c, d, e, f))
92118 F₂ = Fsymbol (tails... )
93119 if F₁ isa Number && F₂ isa Number
94120 return F₁ * F₂
@@ -110,16 +136,12 @@ function Fsymbol(a::P, b::P, c::P, d::P, e::P, f::P) where {P <: ProductSector}
110136 return _kron (F₁, F₂)
111137 end
112138end
113- function Fsymbol (
114- a:: P , b:: P , c:: P , d:: P , e:: P , f:: P
115- ) where {P <: ProductSector{<:Tuple{Sector}} }
116- return Fsymbol (map (_firstsector, (a, b, c, d, e, f))... )
117- end
118139
119140function Rsymbol (a:: P , b:: P , c:: P ) where {P <: ProductSector }
120141 heads = map (_firstsector, (a, b, c))
121- tails = map (_tailsector, (a, b, c))
122142 R₁ = Rsymbol (heads... )
143+ length (a) == 1 && return R₁
144+ tails = map (_tailsector, (a, b, c))
123145 R₂ = Rsymbol (tails... )
124146 if R₁ isa Number && R₂ isa Number
125147 R₁ * R₂
@@ -137,14 +159,12 @@ function Rsymbol(a::P, b::P, c::P) where {P <: ProductSector}
137159 return _kron (R₁, R₂)
138160 end
139161end
140- function Rsymbol (a:: P , b:: P , c:: P ) where {P <: ProductSector{<:Tuple{Sector}} }
141- return Rsymbol (map (_firstsector, (a, b, c))... )
142- end
143162
144163function Bsymbol (a:: P , b:: P , c:: P ) where {P <: ProductSector }
145164 heads = map (_firstsector, (a, b, c))
146- tails = map (_tailsector, (a, b, c))
147165 B₁ = Bsymbol (heads... )
166+ length (a) == 1 && return B₁
167+ tails = map (_tailsector, (a, b, c))
148168 B₂ = Bsymbol (tails... )
149169 if B₁ isa Number && B₂ isa Number
150170 B₁ * B₂
@@ -162,14 +182,12 @@ function Bsymbol(a::P, b::P, c::P) where {P <: ProductSector}
162182 return _kron (B₁, B₂)
163183 end
164184end
165- function Bsymbol (a:: P , b:: P , c:: P ) where {P <: ProductSector{<:Tuple{Sector}} }
166- return Bsymbol (map (_firstsector, (a, b, c))... )
167- end
168185
169186function Asymbol (a:: P , b:: P , c:: P ) where {P <: ProductSector }
170187 heads = map (_firstsector, (a, b, c))
171- tails = map (_tailsector, (a, b, c))
172188 A₁ = Asymbol (heads... )
189+ length (a) == 1 && return A₁
190+ tails = map (_tailsector, (a, b, c))
173191 A₂ = Asymbol (tails... )
174192 if A₁ isa Number && A₂ isa Number
175193 A₁ * A₂
@@ -187,46 +205,41 @@ function Asymbol(a::P, b::P, c::P) where {P <: ProductSector}
187205 return _kron (A₁, A₂)
188206 end
189207end
190- function Asymbol (a:: P , b:: P , c:: P ) where {P <: ProductSector{<:Tuple{Sector}} }
191- return Asymbol (map (_firstsector, (a, b, c))... )
192- end
193208
194209frobenius_schur_phase (p:: ProductSector ) = prod (frobenius_schur_phase, p. sectors)
195210frobenius_schur_indicator (p:: ProductSector ) = prod (frobenius_schur_indicator, p. sectors)
196211
197212function fusiontensor (a:: P , b:: P , c:: P ) where {P <: ProductSector }
198- return _kron (
199- fusiontensor (map (_firstsector, (a, b, c))... ),
200- fusiontensor (map (_tailsector, (a, b, c))... )
201- )
202- end
203-
204- function fusiontensor (a:: P , b:: P , c:: P ) where {P <: ProductSector{<:Tuple{Sector}} }
205- return fusiontensor (map (_firstsector, (a, b, c))... )
213+ heads = map (_firstsector, (a, b, c))
214+ C₁ = fusiontensor (heads... )
215+ length (a) == 1 && return C₁
216+ tails = map (_tailsector, (a, b, c))
217+ C₂ = fusiontensor (tails... )
218+ return _kron (C₁, C₂)
206219end
207220
208- function FusionStyle (:: Type{<:ProductSector{T}} ) where {T <: SectorTuple }
221+ function FusionStyle (:: Type{<:ProductSector{T}} ) where {T}
209222 return mapreduce (FusionStyle, & , _sectors (T))
210223end
211- function UnitStyle (:: Type{<:ProductSector{T}} ) where {T <: SectorTuple }
224+ function UnitStyle (:: Type{<:ProductSector{T}} ) where {T}
212225 return mapreduce (UnitStyle, & , _sectors (T))
213226end
214- function BraidingStyle (:: Type{<:ProductSector{T}} ) where {T <: SectorTuple }
227+ function BraidingStyle (:: Type{<:ProductSector{T}} ) where {T}
215228 return mapreduce (BraidingStyle, & , _sectors (T))
216229end
217- function Base. isreal (:: Type{<:ProductSector{T}} ) where {T <: SectorTuple }
230+ function Base. isreal (:: Type{<:ProductSector{T}} ) where {T}
218231 return mapreduce (isreal, & , _sectors (T))
219232end
220233
221234fermionparity (P:: ProductSector ) = mapreduce (fermionparity, xor, P. sectors)
222235
223- dim (p:: ProductSector ) = * (dim .( p. sectors) ... )
236+ dim (p:: ProductSector ) = prod (dim, p. sectors)
224237
225238Base. isequal (p1:: ProductSector , p2:: ProductSector ) = isequal (p1. sectors, p2. sectors)
226239Base. hash (p:: ProductSector , h:: UInt ) = hash (p. sectors, h)
227- function Base. isless (p1:: ProductSector{T} , p2:: ProductSector{T} ) where {T <: SectorTuple }
228- I1 = findindex .(values .(_sectors (T)), p1. sectors)
229- I2 = findindex .(values .(_sectors (T)), p2. sectors)
240+ function Base. isless (p1:: ProductSector{T} , p2:: ProductSector{T} ) where {T}
241+ I1 = findindex .(values .(_sectors (T)), Tuple ( p1. sectors) )
242+ I2 = findindex .(values .(_sectors (T)), Tuple ( p2. sectors) )
230243 d1 = sum (I1) - length (I1)
231244 d2 = sum (I2) - length (I2)
232245 d1 < d2 && return true
@@ -236,20 +249,23 @@ end
236249
237250# Default construction from tensor product of sectors
238251# -----------------------------------------------------
239- ⊠ (s1, s2, s3, s4... ) = ⊠ (⊠ (s1, s2), s3, s4... )
240- const deligneproduct = ⊠
241252
242- """
243- ⊠(s₁::Sector, s₂::Sector)
253+ @doc """
244254 deligneproduct(s₁::Sector, s₂::Sector)
255+ deligneproduct(S₁::Type{<:Sector}, S₂::Type{<:Sector})
256+ s₁ ⊠ s₂
245257
246258Given two sectors `s₁` and `s₂`, which label an isomorphism class of simple objects in a
247259fusion category ``C₁`` and ``C₂``, `s1 ⊠ s2` (obtained as `\\ boxtimes+TAB`) labels the
248260isomorphism class of simple objects in the Deligne tensor product category ``C₁ ⊠ C₂``.
249261
250262The Deligne tensor product also works in the type domain and for spaces and tensors. For
251263group representations, we have `Irrep[G₁] ⊠ Irrep[G₂] == Irrep[G₁ × G₂]`.
252- """
264+ """ ⊠
265+ const deligneproduct = ⊠
266+
267+ ⊠ (s1, s2, s3, s4... ) = ⊠ (⊠ (s1, s2), s3, s4... )
268+
253269⊠ (s1:: Sector , s2:: Sector ) = ProductSector ((s1, s2))
254270⊠ (s1:: Trivial , s2:: Trivial ) = s1
255271⊠ (s1:: Sector , s2:: Trivial ) = s1
@@ -259,34 +275,31 @@ group representations, we have `Irrep[G₁] ⊠ Irrep[G₂] == Irrep[G₁ × G
259275⊠ (s1:: Trivial , p2:: ProductSector ) = p2
260276⊠ (s1:: Sector , p2:: ProductSector ) = ProductSector (tuple (s1, p2. sectors... ))
261277⊠ (p1:: ProductSector , p2:: ProductSector ) = ProductSector (tuple (p1. sectors... , p2. sectors... ))
262-
263- ⊠ (I1:: Type{Trivial} , I2:: Type{Trivial} ) = Trivial
264- ⊠ (I1:: Type{Trivial} , I2:: Type{<:ProductSector} ) = I2
265- ⊠ (I1:: Type{Trivial} , I2:: Type{<:Sector} ) = I2
266-
267- ⊠ (I1:: Type{<:ProductSector} , I2:: Type{Trivial} ) = I1
268- @static if VERSION >= v " 1.8"
269- Base. @assume_effects :foldable function ⊠ (
270- I1:: Type{<:ProductSector} , I2:: Type{<:ProductSector}
271- )
272- T1 = I1. parameters[1 ]
273- T2 = I2. parameters[1 ]
274- return ProductSector{Tuple{T1. parameters... , T2. parameters... }}
275- end
276- else
277- Base. @pure function ⊠ (I1:: Type{<:ProductSector} , I2:: Type{<:ProductSector} )
278- T1 = I1. parameters[1 ]
279- T2 = I2. parameters[1 ]
280- return ProductSector{Tuple{T1. parameters... , T2. parameters... }}
281- end
278+ function ⊠ (p1:: NamedProductSector , p2:: NamedProductSector )
279+ K = (keys (p1. sectors)... , keys (p2. sectors)... )
280+ V = (values (p1. sectors)... , values (p2. sectors)... )
281+ return ProductSector (NamedTuple {K} (V))
282282end
283- ⊠ (I1:: Type{<:ProductSector} , I2:: Type{<:Sector} ) = I1 ⊠ ProductSector{Tuple{I2}}
284283
285- ⊠ (I1:: Type{<:Sector} , I2:: Type{Trivial} ) = I1
286- ⊠ (I1:: Type{<:Sector} , I2:: Type{<:ProductSector} ) = ProductSector{Tuple{I1}} ⊠ I2
287- ⊠ (I1:: Type{<:Sector} , I2:: Type{<:Sector} ) = ProductSector{Tuple{I1, I2}}
284+ Base. @assume_effects :foldable function ⊠ (:: Type{I₁} , :: Type{I₂} ) where {I₁<: Sector ,I₂<: Sector }
285+ I₁ === Trivial && return I₂
286+ I₂ === Trivial && return I₁
287+ I₁ <: ProductSector || return ⊠ (ProductSector{Tuple{I₁}}, I₂)
288+ I₂ <: ProductSector || return ⊠ (I₁, ProductSector{Tuple{I₂}})
289+ return _deligneproduct_impl (I₁, I₂)
290+ end
291+ Base. @assume_effects :foldable function _deligneproduct_impl (
292+ :: Type{ProductSector{T₁}} , :: Type{ProductSector{T₂}}
293+ ) where {T₁, T₂}
294+ return ProductSector{Tuple{_sectors (T₁)... , _sectors (T₂)... }}
295+ end
296+ Base. @assume_effects :foldable function _deligneproduct_impl (
297+ :: Type{ProductSector{NamedTuple{K₁, V₁}}} , :: Type{ProductSector{NamedTuple{K₂, V₂}}}
298+ ) where {K₁, V₁, K₂, V₂}
299+ return ProductSector{NamedTuple{(K₁... , K₂... ), Tuple{_sectors (V₁)... , _sectors (V₂)... }}}
300+ end
288301
289- function Base. show (io:: IO , P:: ProductSector )
302+ function Base. show (io:: IO , P:: TupleProductSector )
290303 sectors = P. sectors
291304 compact = get (io, :typeinfo , nothing ) === typeof (P)
292305 sep = compact ? " , " : " ⊠ "
@@ -298,8 +311,23 @@ function Base.show(io::IO, P::ProductSector)
298311 end
299312 return print (io, " )" )
300313end
314+ function Base. show (io:: IO , P:: NamedProductSector )
315+ if get (io, :typeinfo , nothing ) != = typeof (P)
316+ print (io, type_repr (typeof (P)))
317+ end
318+ print (io, " (" )
319+ first = true
320+ for sector in P. sectors
321+ first || print (io, " , " )
322+ ioc = IOContext (io, :typeinfo => typeof (sector))
323+ show (ioc, sector)
324+ first = false
325+ end
326+ print (io, " )" )
327+ return nothing
328+ end
301329
302- function type_repr (P:: Type{<:ProductSector } )
330+ function type_repr (P:: Type{<:TupleProductSector } )
303331 sectors = P. parameters[1 ]. parameters
304332 if length (sectors) == 1
305333 s = " ProductSector{Tuple{" * type_repr (sectors[1 ]) * " }}"
@@ -316,6 +344,34 @@ function type_repr(P::Type{<:ProductSector})
316344 return s
317345end
318346
347+ """
348+ @NamedProductSector{key1::Type1, key2::Type2, ...}
349+ @NamedProductSector begin key1::Type1; key2::Type2; ...; end
350+
351+ This macro gives a more convenient syntax for declaring `ProductSector` types which have
352+ names associated to the components. This is also used when printing `ProductSector` types
353+ to e.g. the REPL.
354+ """
355+ macro NamedProductSector (ex)
356+ return esc (:(ProductSector{@NamedTuple ($ ex)}))
357+ end
358+ function type_repr (P:: Type{<:NamedProductSector} )
359+ names = P. parameters[1 ]. parameters[1 ]
360+ sectors = P. parameters[1 ]. parameters[2 ]. parameters
361+ iob = IOBuffer ()
362+ print (iob, " @NamedProductSector{" )
363+ first = true
364+ for (name, sector) in zip (names, sectors)
365+ first || print (iob, " , " )
366+ print (iob, name)
367+ print (iob, " ::" )
368+ print (iob, type_repr (sector))
369+ first = false
370+ end
371+ print (iob, " }" )
372+ return String (take! (iob))
373+ end
374+
319375#= =============================================================================
320376TODO : the following would implement pretty-printing of product sectors, i.e.
321377`ProductSector{Tuple{Irrep[G]}}` would be printed as `Irrep[G]`, and
0 commit comments