Skip to content

Commit 0218dd1

Browse files
committed
incorporate code suggestions
1 parent 340bfc4 commit 0218dd1

7 files changed

Lines changed: 127 additions & 99 deletions

File tree

docs/make.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ mathengine = MathJax3(
5050
)
5151
)
5252

53+
# docstrings don't need `using TensorKit`
54+
DocMeta.setdocmeta!(TensorKit, :DocTestSetup, :(using TensorKit); recursive = true)
55+
5356
makedocs(;
5457
modules = [TensorKit, TensorKitSectors],
5558
sitename = "TensorKit.jl",

docs/src/lib/fusiontrees.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ split
2525
TensorKit.join
2626
merge
2727
elementary_trace
28-
planar_trace(f::FusionTree{I, N}, q::Index2Tuple{N₃, N₃}) where {I, N, N₃}
28+
planar_trace(f::FusionTree, q::Index2Tuple)
2929
artin_braid
3030
braid(f::FusionTree{I, N}, p::IndexTuple{N}, levels::IndexTuple{N}) where {I, N}
3131
permute(f::FusionTree{I, N}, p::IndexTuple{N}) where {I, N}

docs/src/man/fusiontrees.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ For this, we provide an interface
124124

125125
[`braid(f::FusionTree{I, N}, p::IndexTuple{N}, levels::IndexTuple{N})`](@ref braid(::FusionTree{I, N}, ::IndexTuple{N}, ::IndexTuple{N}) where {I, N})
126126

127-
where the braid is specified as a permutation, such that the new sector at position `i` was originally at position `permutation[i]`, and where every uncoupled sector is also assigned a level or depth.
127+
where the braid is specified as a permutation, such that the new sector at position `i` was originally at position `p[i]`, and where every uncoupled sector is also assigned a level or depth.
128128
The permutation is decomposed into swaps between neighbouring sectors, and when two sectors are swapped, their respective level will determine whether the left sector is braided over or under its right neighbor.
129129
This interface does not allow to specify the most general braid, and in particular will never wind one line around another, but can be used as a more general building block for arbitrary braids than the elementary Artin generators.
130130
A graphical example makes this probably more clear, i.e for `levels = (1, 2, 3, 4, 5)` and `permutation = (5, 3, 1, 4, 2)`, the corresponding braid is given by

src/fusiontrees/basic_manipulations.jl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
# -> rewrite generic fusion tree in basis of fusion trees in standard form
44
# -> only depend on Fsymbol
55
"""
6-
split(f::FusionTree{I, N}, M::Int)
7-
-> (::FusionTree{I, M}, ::FusionTree{I, N-M+1})
6+
split(f::FusionTree{I, N}, M::Int) -> (::FusionTree{I, M}, ::FusionTree{I, N - M + 1})
87
98
Split a fusion tree into two. The first tree has as uncoupled sectors the first `M`
109
uncoupled sectors of the input tree `f`, whereas its coupled sector corresponds to the
@@ -15,6 +14,18 @@ operation is the inverse of `join` in the sense that if `f == join(split(f, M)..
1514
holds for all `M` between `0` and `N`, where `N` is the number of uncoupled sectors of `f`.
1615
1716
See also [`join`](@ref) and [`insertat`](@ref).
17+
18+
## Examples
19+
20+
```jldoctest
21+
julia> f = FusionTree{Z2Irrep}((1, 1, 0), 0, (false, false, false));
22+
23+
julia> f₁, f₂ = TensorKit.split(f, 2)
24+
(FusionTree{Irrep[ℤ₂]}((1, 1), 0, (false, false), ()), FusionTree{Irrep[ℤ₂]}((0, 0), 0, (false, false), ()))
25+
26+
julia> TensorKit.join(f₁, f₂) == f
27+
true
28+
```
1829
"""
1930
@inline function split(f::FusionTree{I, N}, M::Int) where {I, N} # inline helps with constant propagation of M
2031
0 <= M <= N ||
@@ -52,6 +63,17 @@ operation is the inverse of split, in the sense that `f == join(split(f, M)...)`
5263
holds for all `M` between `0` and `N`, where `N` is the number of uncoupled sectors of `f`.
5364
5465
See also [`split`](@ref) and [`insertat`](@ref).
66+
67+
## Examples
68+
69+
```jldoctest
70+
julia> f₁ = FusionTree{Z2Irrep}((1, 1), 0, (false, false));
71+
72+
julia> f₂ = FusionTree{Z2Irrep}((0, 0), 0, (false, false));
73+
74+
julia> f = TensorKit.join(f₁, f₂)
75+
FusionTree{Irrep[ℤ₂]}((1, 1, 0), 0, (false, false, false), (0,))
76+
```
5577
"""
5678
@inline function join(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {I, N₁, N₂}
5779
(f₁.coupled == f₂.uncoupled[1] && !f₂.isdual[1]) ||
@@ -574,4 +596,3 @@ function flip((f₁, f₂)::FusionTreePair{I, N₁, N₂}, ind; inv::Bool = fals
574596
end
575597
return SingletonDict((f₁′, f₂′) => factor)
576598
end
577-

src/fusiontrees/duality_manipulations.jl

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -573,37 +573,39 @@ CacheStyle(::typeof(fstranspose), k::FSBTransposeKey{I}) where {I} =
573573
# -> composite manipulations that depend on the duality (rigidity) and pivotal structure
574574
# -> planar manipulations that do not require braiding, everything is in Fsymbol (A/Bsymbol)
575575

576-
function planar_trace(
577-
(f₁, f₂)::FusionTreePair{I}, (p1, p2)::Index2Tuple{N₁, N₂}, (q1, q2)::Index2Tuple{N₃, N₃}
578-
) where {I, N₁, N₂, N₃}
579-
N = N₁ + N₂ + 2N₃
580-
@assert length(f₁) + length(f₂) == N
581-
if N₃ == 0
582-
return transpose((f₁, f₂), (p1, p2))
576+
function planar_trace((f₁, f₂)::FusionTreePair, (p₁, p₂)::Index2Tuple, (q₁, q₂)::Index2Tuple)
577+
length(q₁) == length(q₂) ||
578+
throw(ArgumentError(lazy"trace index tuples q₁ and q₂ must have equal length, got $(length(q₁)) and $(length(q₂))"))
579+
I = sectortype(f₁)
580+
N = length(p₁) + length(p₂) + 2 * length(q₁)
581+
length(f₁) + length(f₂) == N ||
582+
throw(ArgumentError(lazy"fusion tree pair has $(length(f₁) + length(f₂)) indices, but permutation expects $N = $(length(p₁)) + $(length(p₂)) + 2×$(length(q₁))"))
583+
if isempty(q₁)
584+
return transpose((f₁, f₂), (p₁, p₂))
583585
end
584586

585587
linearindex = (
586588
ntuple(identity, Val(length(f₁)))...,
587589
reverse(length(f₁) .+ ntuple(identity, Val(length(f₂))))...,
588590
)
589591

590-
q1= TupleTools.getindices(linearindex, q1)
591-
q2= TupleTools.getindices(linearindex, q2)
592-
p1′, p2= let q′ = (q1..., q2...)
592+
q₁= TupleTools.getindices(linearindex, q₁)
593+
q₂= TupleTools.getindices(linearindex, q₂)
594+
p₁′, p₂= let q′ = (q₁..., q₂...)
593595
(
594-
map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p1)),
595-
map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p2)),
596+
map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p₁)),
597+
map(l -> l - count(l .> q′), TupleTools.getindices(linearindex, p₂)),
596598
)
597599
end
598600

599601
T = fusionscalartype(I)
600-
F₁ = fusiontreetype(I, N₁)
601-
F₂ = fusiontreetype(I, N₂)
602+
F₁ = fusiontreetype(I, length(p₁))
603+
F₂ = fusiontreetype(I, length(p₂))
602604
newtrees = FusionTreeDict{Tuple{F₁, F₂}, T}()
603605
if FusionStyle(I) isa UniqueFusion
604606
(f₁′, f₂′), coeff′ = repartition((f₁, f₂), N)
605-
for (f₁′′, coeff′′) in planar_trace(f₁′, (q1′, q2′))
606-
(f12′′′, coeff′′′) = transpose((f₁′′, f₂′), (p1′, p2′))
607+
for (f₁′′, coeff′′) in planar_trace(f₁′, (q₁′, q₂′))
608+
(f12′′′, coeff′′′) = transpose((f₁′′, f₂′), (p₁′, p₂′))
607609
coeff = coeff′ * coeff′′ * coeff′′′
608610
iszero(coeff) || (newtrees[f12′′′] = get(newtrees, f12′′′, zero(coeff)) + coeff)
609611
end
@@ -612,9 +614,9 @@ function planar_trace(
612614
src = FusionTreeBlock([(f₁, f₂)])
613615
dst, U = repartition(src, N)
614616
for ((f₁′, f₂′), coeff′) in zip(fusiontrees(dst), U)
615-
for (f₁′′, coeff′′) in planar_trace(f₁′, (q1′, q2′))
617+
for (f₁′′, coeff′′) in planar_trace(f₁′, (q₁′, q₂′))
616618
src′ = FusionTreeBlock([(f₁′′, f₂′)])
617-
dst′, U′ = transpose(src′, (p1′, p2′))
619+
dst′, U′ = transpose(src′, (p₁′, p₂′))
618620
for (f12′′′, coeff′′′) in zip(fusiontrees(dst′), U′)
619621
coeff = coeff′ * coeff′′ * coeff′′′
620622
iszero(coeff) || (newtrees[f12′′′] = get(newtrees, f12′′′, zero(coeff)) + coeff)
@@ -626,20 +628,23 @@ function planar_trace(
626628
end
627629

628630
"""
629-
planar_trace(f::FusionTree{I,N}, (q1, q2)::Index2Tuple{N₃,N₃}) where {I,N,N₃}
630-
-> <:AbstractDict{FusionTree{I,N-2*N₃}, <:Number}
631+
planar_trace(f::FusionTree, (q₁, q₂)::Index2Tuple)
632+
-> <:AbstractDict{<:FusionTree, <:Number}
631633
632-
Perform a planar trace of the uncoupled indices of the fusion tree `f` at `q1` with those at
633-
`q2`, where `q1[i]` is connected to `q2[i]` for all `i`. The result is returned as a dictionary
634-
of output trees and corresponding coefficients.
634+
Perform a planar trace of the uncoupled indices of the fusion tree `f` at `q₁` with those at `q₂`,
635+
where `q₁[i]` is connected to `q₂[i]` for all `i`. The result is returned as a dictionary of output
636+
trees and corresponding coefficients.
635637
"""
636-
function planar_trace(f::FusionTree{I, N}, (q1, q2)::Index2Tuple{N₃, N₃}) where {I, N, N₃}
638+
function planar_trace(f::FusionTree, (q₁, q₂)::Index2Tuple)
639+
length(q₁) == length(q₂) ||
640+
throw(ArgumentError(lazy"trace index tuples q₁ and q₂ must have equal length, got $(length(q₁)) and $(length(q₂))"))
641+
I = sectortype(f)
637642
T = fusionscalartype(I)
638-
F = fusiontreetype(I, N - 2 * N₃)
643+
F = fusiontreetype(I, length(f) - 2 * length(q₁))
639644
newtrees = FusionTreeDict{F, T}()
640-
N₃ === 0 && return push!(newtrees, f => one(T))
645+
isempty(q₁) && return push!(newtrees, f => one(T))
641646

642-
for (i, j) in zip(q1, q2)
647+
for (i, j) in zip(q₁, q₂)
643648
(f.uncoupled[i] == dual(f.uncoupled[j]) && f.isdual[i] != f.isdual[j]) ||
644649
return newtrees
645650
end
@@ -649,29 +654,30 @@ function planar_trace(f::FusionTree{I, N}, (q1, q2)::Index2Tuple{N₃, N₃}) wh
649654
# tracing away neighbouring pairs.
650655
k = 1
651656
local i, j
652-
while k <= N₃
653-
if mod1(q1[k] + 1, N) == q2[k]
654-
i = q1[k]
655-
j = q2[k]
657+
while k <= length(q₁)
658+
if mod1(q₁[k] + 1, length(f)) == q₂[k]
659+
i = q₁[k]
660+
j = q₂[k]
656661
break
657-
elseif mod1(q2[k] + 1, N) == q1[k]
658-
i = q2[k]
659-
j = q1[k]
662+
elseif mod1(q₂[k] + 1, length(f)) == q₁[k]
663+
i = q₂[k]
664+
j = q₁[k]
660665
break
661666
else
662667
k += 1
663668
end
664669
end
665-
k > N₃ && throw(ArgumentError("Not a planar trace"))
670+
k > length(q₁) &&
671+
throw(ArgumentError(lazy"indices $q₁ and $q₂ do not form a valid planar trace on a fusion tree with $(length(f)) legs: no neighboring pair found among the remaining trace indices"))
666672

667-
q1= let i = i, j = j
668-
map(l -> (l - (l > i) - (l > j)), TupleTools.deleteat(q1, k))
673+
q₁= let i = i, j = j
674+
map(l -> (l - (l > i) - (l > j)), TupleTools.deleteat(q₁, k))
669675
end
670-
q2= let i = i, j = j
671-
map(l -> (l - (l > i) - (l > j)), TupleTools.deleteat(q2, k))
676+
q₂= let i = i, j = j
677+
map(l -> (l - (l > i) - (l > j)), TupleTools.deleteat(q₂, k))
672678
end
673679
for (f′, coeff′) in elementary_trace(f, i)
674-
for (f′′, coeff′′) in planar_trace(f′, (q1′, q2′))
680+
for (f′′, coeff′′) in planar_trace(f′, (q₁′, q₂′))
675681
coeff = coeff′ * coeff′′
676682
if !iszero(coeff)
677683
newtrees[f′′] = get(newtrees, f′′, zero(coeff)) + coeff

src/spaces/homspace.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fusiontrees(W::HomSpace) = fusionblockstructure(W).fusiontreelist
141141
"""
142142
fusionblocks(W::HomSpace)
143143
144-
Return the fusionblocks corresponding to all valid fusion channels of a given `HomSpace`,
144+
Return the [`FusionTreeBlock`](@ref)s corresponding to all valid fusion channels of a given `HomSpace`,
145145
grouped by their uncoupled charges.
146146
"""
147147
function fusionblocks(W::HomSpace)

src/tensors/tensoroperations.jl

Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,20 @@ TO.tensorcost(t::AbstractTensorMap, i::Int) = dim(space(t, i))
190190
"""
191191
trace_permute!(tdst::AbstractTensorMap, tsrc::AbstractTensorMap,
192192
(p₁, p₂)::Index2Tuple, (q₁, q₂)::Index2Tuple,
193-
α::Number, β::Number, backend=TO.DefaultBackend())
193+
α::Number, β::Number, backend = TO.DefaultBackend())
194194
195195
Return the updated `tdst`, which is the result of adding `α * tsrc` to `tdst` after permuting
196196
the indices of `tsrc` according to `(p₁, p₂)` and furthermore tracing the indices in `q₁` and `q₂`.
197197
"""
198198
function trace_permute!(
199199
tdst::AbstractTensorMap,
200-
tsrc::AbstractTensorMap,
201-
(p₁, p₂)::Index2Tuple,
202-
(q₁, q₂)::Index2Tuple,
203-
α::Number,
204-
β::Number,
205-
backend = TO.DefaultBackend()
200+
tsrc::AbstractTensorMap, (p₁, p₂)::Index2Tuple, (q₁, q₂)::Index2Tuple,
201+
α::Number, β::Number, backend = TO.DefaultBackend()
206202
)
207203
# some input checks
208204
S = check_spacetype(tdst, tsrc)
209-
if !(BraidingStyle(sectortype(S)) isa SymmetricBraiding)
205+
I = sectortype(S)
206+
if !(BraidingStyle(I) isa SymmetricBraiding)
210207
throw(SectorMismatch("only tensors with symmetric braiding rules can be contracted; try `@planar` instead"))
211208
end
212209
(N₃ = length(q₁)) == length(q₂) ||
@@ -223,60 +220,61 @@ function trace_permute!(
223220
q₁ = $(q₁), q₂ = $(q₂)"))
224221
end
225222

226-
I = sectortype(S)
227223
if I === Trivial
228-
cod = codomain(tsrc)
229-
dom = domain(tsrc)
230-
n = length(cod)
231224
TO.tensortrace!(tdst[], tsrc[], (p₁, p₂), (q₁, q₂), false, α, β, backend)
232-
return tdst
225+
else
226+
_trace_permute!(FusionStyle(I), tdst, tsrc, (p₁, p₂), (q₁, q₂), α, β, backend)
233227
end
234228

235-
cod = codomain(tsrc)
236-
dom = domain(tsrc)
237-
n = length(cod)
229+
return tdst
230+
end
231+
232+
function _trace_permute!(::UniqueFusion, tdst, tsrc, (p₁, p₂), (q₁, q₂), α, β, backend)
238233
scale!(tdst, β)
239-
r₁ = (p₁..., q₁...)
240-
r₂ = (p₂..., q₂...)
241-
242-
# TODO: Is it worth to add multithreading support?
243-
if FusionStyle(I) isa UniqueFusion
244-
for (f₁, f₂) in fusiontrees(tsrc)
245-
(f₁′, f₂′), coeff = permute((f₁, f₂), (r₁, r₂))
246-
f₁′′, g₁ = split(f₁′, N₁)
247-
f₂′′, g₂ = split(f₂′, N₂)
248-
g₁ == g₂ || continue
249-
coeff *= dim(g₁.coupled) / dim(g₁.uncoupled[1])
250-
for i in 2:length(g₁.uncoupled)
251-
if !(g₁.isdual[i])
252-
coeff *= twist(g₁.uncoupled[i])
253-
end
234+
r₁, r₂ = (p₁..., q₁...), (p₂..., q₂...)
235+
236+
for (f₁, f₂) in fusiontrees(tsrc)
237+
(f₁′, f₂′), coeff = permute((f₁, f₂), (r₁, r₂))
238+
f₁′′, g₁ = split(f₁′, N₁)
239+
f₂′′, g₂ = split(f₂′, N₂)
240+
g₁ == g₂ || continue
241+
coeff *= dim(g₁.coupled) / dim(g₁.uncoupled[1])
242+
for i in 2:length(g₁.uncoupled)
243+
if !(g₁.isdual[i])
244+
coeff *= twist(g₁.uncoupled[i])
254245
end
255-
C = tdst[f₁′′, f₂′′]
256-
A = tsrc[f₁, f₂]
257-
α′ = α * coeff
258-
TO.tensortrace!(C, A, (p₁, p₂), (q₁, q₂), false, α′, One(), backend)
259246
end
260-
else
261-
for src in fusionblocks(tsrc)
262-
dst, U = permute(src, (r₁, r₂))
263-
for (i, (f₁, f₂)) in enumerate(fusiontrees(src))
264-
for (j, (f₁′, f₂′)) in enumerate(fusiontrees(dst))
265-
coeff = U[j, i]
266-
f₁′′, g₁ = split(f₁′, N₁)
267-
f₂′′, g₂ = split(f₂′, N₂)
268-
g₁ == g₂ || continue
269-
coeff *= dim(g₁.coupled) / dim(g₁.uncoupled[1])
270-
for i in 2:length(g₁.uncoupled)
271-
if !(g₁.isdual[i])
272-
coeff *= twist(g₁.uncoupled[i])
273-
end
247+
C = tdst[f₁′′, f₂′′]
248+
A = tsrc[f₁, f₂]
249+
α′ = α * coeff
250+
TO.tensortrace!(C, A, (p₁, p₂), (q₁, q₂), false, α′, One(), backend)
251+
end
252+
253+
return tdst
254+
end
255+
256+
function _trace_permute!(::FusionStyle, tdst, tsrc, (p₁, p₂), (q₁, q₂), α, β, backend)
257+
scale!(tdst, β)
258+
r₁, r₂ = (p₁..., q₁...), (p₂..., q₂...)
259+
260+
for src in fusionblocks(tsrc)
261+
dst, U = permute(src, (r₁, r₂))
262+
for (i, (f₁, f₂)) in enumerate(fusiontrees(src))
263+
for (j, (f₁′, f₂′)) in enumerate(fusiontrees(dst))
264+
coeff = U[j, i]
265+
f₁′′, g₁ = split(f₁′, N₁)
266+
f₂′′, g₂ = split(f₂′, N₂)
267+
g₁ == g₂ || continue
268+
coeff *= dim(g₁.coupled) / dim(g₁.uncoupled[1])
269+
for i in 2:length(g₁.uncoupled)
270+
if !(g₁.isdual[i])
271+
coeff *= twist(g₁.uncoupled[i])
274272
end
275-
C = tdst[f₁′′, f₂′′]
276-
A = tsrc[f₁, f₂]
277-
α′ = α * coeff
278-
TO.tensortrace!(C, A, (p₁, p₂), (q₁, q₂), false, α′, One(), backend)
279273
end
274+
C = tdst[f₁′′, f₂′′]
275+
A = tsrc[f₁, f₂]
276+
α′ = α * coeff
277+
TO.tensortrace!(C, A, (p₁, p₂), (q₁, q₂), false, α′, One(), backend)
280278
end
281279
end
282280
end

0 commit comments

Comments
 (0)