22# special (2,2) tensor that implements a standard braiding operation
33#= ===================================================================#
44"""
5- struct BraidingTensor{T,S<:IndexSpace} <: AbstractTensorMap{T, S, 2, 2}
6- BraidingTensor(V1::S, V2::S, adjoint::Bool=false) where {S<:IndexSpace}
5+ struct BraidingTensor{T,S<:IndexSpace,A<:DenseVector{T} } <: AbstractTensorMap{T, S, 2, 2}
6+ BraidingTensor(V1::S, V2::S, ::Type{A}, adjoint::Bool=false) where {S<:IndexSpace, A <: DenseVector{<:Number} }
77
88Specific subtype of [`AbstractTensorMap`](@ref) for representing the braiding tensor that
99braids the first input over the second input; its inverse can be obtained as the adjoint.
1010
1111It holds that `domain(BraidingTensor(V1, V2)) == V1 ⊗ V2` and
12- `codomain(BraidingTensor(V1, V2)) == V2 ⊗ V1`.
12+ `codomain(BraidingTensor(V1, V2)) == V2 ⊗ V1`. The storage type `TA`
13+ controls the array type of the braiding tensor used when indexing
14+ and multiplying with other tensors.
1315"""
14- struct BraidingTensor{T, S} <: AbstractTensorMap{T, S, 2, 2}
16+ struct BraidingTensor{T, S, A } <: AbstractTensorMap{T, S, 2, 2}
1517 V1:: S
1618 V2:: S
1719 adjoint:: Bool
18- function BraidingTensor {T, S} (V1:: S , V2:: S , adjoint:: Bool = false ) where {T, S <: IndexSpace }
19- for a in sectors (V1)
20- for b in sectors (V2)
21- for c in (a ⊗ b)
22- Nsymbol (a, b, c) == Nsymbol (b, a, c) ||
23- throw (ArgumentError (" Cannot define a braiding between $a and $b " ))
24- end
25- end
20+ function BraidingTensor {T, S, A} (V1:: S , V2:: S , adjoint:: Bool = false ) where {T, S <: IndexSpace , A <: DenseVector{T} }
21+ for a in sectors (V1), b in sectors (V2), c in (a ⊗ b)
22+ Nsymbol (a, b, c) == Nsymbol (b, a, c) ||
23+ throw (ArgumentError (" Cannot define a braiding between $a and $b " ))
2624 end
27- return new {T, S} (V1, V2, adjoint)
25+ return new {T, S, A } (V1, V2, adjoint)
2826 # partial construction: only construct rowr and colr when needed
2927 end
3028end
3129function BraidingTensor {T} (V1:: S , V2:: S , adjoint:: Bool = false ) where {T, S <: IndexSpace }
32- return BraidingTensor {T, S} (V1, V2, adjoint)
30+ return BraidingTensor {T, S, Vector{T} } (V1, V2, adjoint)
3331end
34- function BraidingTensor {T} (V1:: IndexSpace , V2:: IndexSpace , adjoint:: Bool = false ) where {T}
35- return BraidingTensor {T} (promote (V1, V2)... , adjoint)
32+ function BraidingTensor (V1:: S , V2:: S , adjoint:: Bool = false ) where {S <: IndexSpace }
33+ T = BraidingStyle (sectortype (S)) isa SymmetricBraiding ? Float64 : ComplexF64
34+ return BraidingTensor {T, S, Vector{T}} (V1, V2, adjoint)
35+ end
36+ # necessary due to HomSpace ctor below
37+ function BraidingTensor (V1:: S , V2:: S , A, adjoint:: Bool = false ) where {S <: IndexSpace }
38+ T = eltype (A)
39+ return BraidingTensor {T, S, A} (V1, V2, adjoint)
3640end
3741function BraidingTensor (V1:: IndexSpace , V2:: IndexSpace , adjoint:: Bool = false )
3842 return BraidingTensor (promote (V1, V2)... , adjoint)
3943end
40- function BraidingTensor (V1:: S , V2:: S , adjoint:: Bool = false ) where {S <: IndexSpace }
41- T = BraidingStyle (sectortype (S)) isa SymmetricBraiding ? Float64 : ComplexF64
42- return BraidingTensor {T, S} (V1, V2, adjoint)
43- end
4444function BraidingTensor (V:: HomSpace , adjoint:: Bool = false )
4545 domain (V) == reverse (codomain (V)) ||
4646 throw (SpaceMismatch (" Cannot define a braiding on $V " ))
4747 return BraidingTensor (V[2 ], V[1 ], adjoint)
4848end
49+ function BraidingTensor (V:: HomSpace , A, adjoint:: Bool = false )
50+ domain (V) == reverse (codomain (V)) ||
51+ throw (SpaceMismatch (" Cannot define a braiding on $V " ))
52+ return BraidingTensor (V[2 ], V[1 ], A, adjoint)
53+ end
4954function BraidingTensor {T} (V:: HomSpace , adjoint:: Bool = false ) where {T}
5055 domain (V) == reverse (codomain (V)) ||
5156 throw (SpaceMismatch (" Cannot define a braiding on $V " ))
5257 return BraidingTensor {T} (V[2 ], V[1 ], adjoint)
5358end
54- function Base. adjoint (b:: BraidingTensor{T, S} ) where {T, S}
55- return BraidingTensor {T, S} (b. V1, b. V2, ! b. adjoint)
59+ function Base. adjoint (b:: BraidingTensor{T, S, A } ) where {T, S, A }
60+ return BraidingTensor {T, S, A } (b. V1, b. V2, ! b. adjoint)
5661end
5762
63+ storagetype (:: Type{BraidingTensor{T, S, A}} ) where {T, S, A} = A
5864space (b:: BraidingTensor ) = b. adjoint ? b. V1 ⊗ b. V2 ← b. V2 ⊗ b. V1 : b. V2 ⊗ b. V1 ← b. V1 ⊗ b. V2
5965
60- # specializations to ignore the storagetype of BraidingTensor
61- promote_storagetype (:: Type{A} , :: Type{B} ) where {A <: BraidingTensor , B <: AbstractTensorMap } = storagetype (B)
62- promote_storagetype (:: Type{A} , :: Type{B} ) where {A <: AbstractTensorMap , B <: BraidingTensor } = storagetype (A)
63- promote_storagetype (:: Type{A} , :: Type{B} ) where {A <: BraidingTensor , B <: BraidingTensor } = storagetype (A)
64-
65- promote_storagetype (:: Type{T} , :: Type{A} , :: Type{B} ) where {T <: Number , A <: BraidingTensor , B <: AbstractTensorMap } =
66- similarstoragetype (B, T)
67- promote_storagetype (:: Type{T} , :: Type{A} , :: Type{B} ) where {T <: Number , A <: AbstractTensorMap , B <: BraidingTensor } =
68- similarstoragetype (A, T)
69- promote_storagetype (:: Type{T} , :: Type{A} , :: Type{B} ) where {T <: Number , A <: BraidingTensor , B <: BraidingTensor } =
70- similarstoragetype (A, T)
71-
7266function Base. getindex (b:: BraidingTensor )
7367 sectortype (b) === Trivial || throw (SectorMismatch ())
7468 (V1, V2) = domain (b)
@@ -99,6 +93,12 @@ function _braiding_factor(f₁, f₂, inv::Bool = false)
9993 return r
10094end
10195
96+ function _set_subblock! (data, val)
97+ f (I) = ((I[1 ] == I[4 ]) & (I[2 ] == I[3 ])) * val
98+ return data .= f .(CartesianIndices (data))
99+ end
100+
101+
102102@inline function subblock (
103103 b:: BraidingTensor , (f₁, f₂):: Tuple{FusionTree{I, 2}, FusionTree{I, 2}}
104104 ) where {I <: Sector }
@@ -115,15 +115,12 @@ end
115115 d = (dims (codomain (b), f₁. uncoupled)... , dims (domain (b), f₂. uncoupled)... )
116116 n1 = d[1 ] * d[2 ]
117117 n2 = d[3 ] * d[4 ]
118- data = sreshape (StridedView (Matrix {eltype(b)} (undef, n1, n2)), d)
118+ data_parent = storagetype (b)(undef, prod (d))
119+ data = sreshape (StridedView (data_parent), d)
119120 fill! (data, zero (eltype (b)))
120121
121122 r = _braiding_factor (f₁, f₂, b. adjoint)
122- if ! isnothing (r)
123- @inbounds for i in axes (data, 1 ), j in axes (data, 2 )
124- data[i, j, j, i] = r
125- end
126- end
123+ ! isnothing (r) && _set_subblock! (data, r)
127124 return data
128125end
129126
@@ -134,33 +131,20 @@ TensorMap(b::BraidingTensor) = copy!(similar(b), b)
134131Base. convert (:: Type{TensorMap} , b:: BraidingTensor ) = TensorMap (b)
135132
136133Base. complex (b:: BraidingTensor{<:Complex} ) = b
137- function Base. complex (b:: BraidingTensor )
138- return BraidingTensor {complex(scalartype(b))} (space (b), b. adjoint)
134+ function Base. complex (b:: BraidingTensor{T, S, A} ) where {T, S, A}
135+ Ac = similarstoragetype (A, complex (T))
136+ return BraidingTensor (space (b), Ac, b. adjoint)
139137end
140138
141- function block (b:: BraidingTensor , s:: Sector )
142- I = sectortype (b)
143- I == typeof (s) || throw (SectorMismatch ())
144-
145- # TODO : probably always square?
146- m = blockdim (codomain (b), s)
147- n = blockdim (domain (b), s)
148- data = Matrix {eltype(b)} (undef, (m, n))
149-
150- length (data) == 0 && return data # s ∉ blocksectors(b)
151-
152- data = fill! (data, zero (eltype (b)))
153-
139+ function _trivial_subblock! (data, b:: BraidingTensor )
154140 V1, V2 = codomain (b)
155- if sectortype (b) === Trivial
156- d1, d2 = dim (V1), dim (V2)
157- subblock = sreshape (StridedView (data), (d1, d2, d2, d1))
158- @inbounds for i in axes (subblock, 1 ), j in axes (subblock, 2 )
159- subblock[i, j, j, i] = one (eltype (b))
160- end
161- return data
162- end
141+ d1, d2 = dim (V1), dim (V2)
142+ subblock = sreshape (StridedView (data), (d1, d2, d2, d1))
143+ _set_subblock! (subblock, one (eltype (b)))
144+ return data
145+ end
163146
147+ function _nontrivial_subblock! (data, b:: BraidingTensor , s:: Sector )
164148 base_offset = first (blockstructure (b)[s][2 ]) - 1
165149
166150 for ((f₁, f₂), (sz, str, off)) in pairs (subblockstructure (space (b)))
@@ -169,14 +153,31 @@ function block(b::BraidingTensor, s::Sector)
169153 isnothing (r) && continue
170154 # change offset to account for single block
171155 subblock = StridedView (data, sz, str, off - base_offset)
172- @inbounds for i in axes (subblock, 1 ), j in axes (subblock, 2 )
173- subblock[i, j, j, i] = r
174- end
156+ _set_subblock! (subblock, r)
175157 end
176-
177158 return data
178159end
179160
161+ function block (b:: BraidingTensor , s:: Sector )
162+ I = sectortype (b)
163+ I == typeof (s) || throw (SectorMismatch ())
164+
165+ # TODO : probably always square?
166+ m = blockdim (codomain (b), s)
167+ n = blockdim (domain (b), s)
168+
169+ data = reshape (storagetype (b)(undef, m * n), (m, n))
170+
171+ m * n == 0 && return data # s ∉ blocksectors(b)
172+ fill! (data, zero (eltype (b)))
173+
174+ if sectortype (b) === Trivial
175+ return _trivial_subblock! (data, b)
176+ else
177+ return _nontrivial_subblock! (data, b, s)
178+ end
179+ end
180+
180181# Index manipulations
181182# -------------------
182183has_shared_permute (t:: BraidingTensor , :: Index2Tuple ) = false
0 commit comments