Skip to content

Commit 9470fa8

Browse files
committed
split up fusiontree tests
1 parent 5fb5a19 commit 9470fa8

2 files changed

Lines changed: 304 additions & 291 deletions

File tree

test/symmetries/doubletree.jl

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
using Test, TestExtras
2+
using TensorKit
3+
import TensorKit as TK
4+
using Random: randperm
5+
using TensorOperations
6+
7+
# TODO: remove this once type_repr works for all included types
8+
using TensorKitSectors
9+
10+
11+
@timedtestset "Fusion trees for $(TensorKit.type_repr(I))" verbose = true for I in (fast_tests ? fast_sectorlist : sectorlist)
12+
Istr = TensorKit.type_repr(I)
13+
@testset "Fusion tree $Istr: merging" begin
14+
N = 3
15+
out1 = random_fusion(I, Val(N))
16+
out2 = random_fusion(I, Val(N))
17+
in1 = rand(collect((out1...)))
18+
in2 = rand(collect((out2...)))
19+
tp = (in1, in2) # messy solution but it works
20+
while isempty(tp)
21+
out1 = random_fusion(I, Val(N))
22+
out2 = random_fusion(I, Val(N))
23+
in1 = rand(collect((out1...)))
24+
in2 = rand(collect((out2...)))
25+
tp = (in1, in2)
26+
end
27+
28+
f1 = rand(collect(fusiontrees(out1, in1)))
29+
f2 = rand(collect(fusiontrees(out2, in2)))
30+
31+
@constinferred TK.merge(f1, f2, first(in1 in2), 1)
32+
if !(FusionStyle(I) isa GenericFusion)
33+
@constinferred TK.merge(f1, f2, first(in1 in2), 1)
34+
@constinferred TK.merge(f1, f2, first(in1 in2))
35+
end
36+
@test dim(in1) * dim(in2) sum(
37+
abs2(coeff) * dim(c) for c in in1 in2
38+
for μ in 1:Nsymbol(in1, in2, c)
39+
for (f, coeff) in TK.merge(f1, f2, c, μ)
40+
)
41+
42+
if BraidingStyle(I) isa HasBraiding
43+
for c in in1 in2
44+
R = Rsymbol(in1, in2, c)
45+
for μ in 1:Nsymbol(in1, in2, c)
46+
trees1 = TK.merge(f1, f2, c, μ)
47+
48+
# test merge and braid interplay
49+
trees2 = Dict{keytype(trees1), complex(valtype(trees1))}()
50+
trees3 = Dict{keytype(trees1), complex(valtype(trees1))}()
51+
for ν in 1:Nsymbol(in2, in1, c)
52+
for (t, coeff) in TK.merge(f2, f1, c, ν)
53+
trees2[t] = get(trees2, t, zero(valtype(trees2))) + coeff * R[μ, ν]
54+
end
55+
end
56+
perm = ((N .+ (1:N))..., (1:N)...)
57+
levels = ntuple(identity, 2 * N)
58+
for (t, coeff) in trees1
59+
for (t′, coeff′) in braid(t, levels, perm)
60+
trees3[t′] = get(trees3, t′, zero(valtype(trees3))) + coeff * coeff′
61+
end
62+
end
63+
for (t, coeff) in trees3
64+
coeff′ = get(trees2, t, zero(coeff))
65+
@test isapprox(coeff, coeff′; atol = 1.0e-12, rtol = 1.0e-12)
66+
end
67+
68+
# test via conversion
69+
if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I)
70+
Af1 = convert(Array, f1)
71+
Af2 = convert(Array, f2)
72+
Af0 = convert(
73+
Array,
74+
FusionTree((f1.coupled, f2.coupled), c, (false, false), (), (μ,))
75+
)
76+
_Af = TensorOperations.tensorcontract(
77+
1:(N + 2), Af1, [1:N; -1], Af0, [-1; N + 1; N + 2]
78+
)
79+
Af = TensorOperations.tensorcontract(
80+
1:(2N + 1), Af2, [N .+ (1:N); -1], _Af, [1:N; -1; 2N + 1]
81+
)
82+
Af′ = zero(Af)
83+
for (f, coeff) in trees1
84+
Af′ .+= coeff .* convert(Array, f)
85+
end
86+
@test Af Af′
87+
end
88+
end
89+
end
90+
end
91+
end
92+
93+
if I <: ProductSector
94+
N = 3
95+
else
96+
N = 4
97+
end
98+
if UnitStyle(I) isa SimpleUnit
99+
out = random_fusion(I, Val(N))
100+
numtrees = count(n -> true, fusiontrees((out..., map(dual, out)...)))
101+
while !(0 < numtrees < 100)
102+
out = random_fusion(I, Val(N))
103+
numtrees = count(n -> true, fusiontrees((out..., map(dual, out)...)))
104+
end
105+
incoming = rand(collect((out...)))
106+
f1 = rand(collect(fusiontrees(out, incoming, ntuple(n -> rand(Bool), N))))
107+
f2 = rand(collect(fusiontrees(out[randperm(N)], incoming, ntuple(n -> rand(Bool), N))))
108+
else
109+
out = random_fusion(I, Val(N))
110+
out2 = random_fusion(I, Val(N))
111+
tp = (out...)
112+
tp2 = (out2...)
113+
while isempty(intersect(tp, tp2)) # guarantee fusion to same coloring
114+
out2 = random_fusion(I, Val(N))
115+
tp2 = (out2...)
116+
end
117+
@test_throws ArgumentError fusiontrees((out..., map(dual, out)...))
118+
incoming = rand(collect(intersect(tp, tp2)))
119+
f1 = rand(collect(fusiontrees(out, incoming, ntuple(n -> rand(Bool), N))))
120+
f2 = rand(collect(fusiontrees(out2, incoming, ntuple(n -> rand(Bool), N)))) # no permuting
121+
end
122+
123+
@testset "Double fusion tree $Istr: repartitioning" begin
124+
for n in 0:(2 * N)
125+
d = @constinferred TK.repartition(f1, f2, $n)
126+
@test dim(incoming)
127+
sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d)
128+
d2 = Dict{typeof((f1, f2)), valtype(d)}()
129+
for ((f1′, f2′), coeff) in d
130+
for ((f1′′, f2′′), coeff2) in TK.repartition(f1′, f2′, N)
131+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff
132+
end
133+
end
134+
for ((f1′, f2′), coeff2) in d2
135+
if f1 == f1′ && f2 == f2′
136+
@test coeff2 1
137+
else
138+
@test isapprox(coeff2, 0; atol = 1.0e-12, rtol = 1.0e-12)
139+
end
140+
end
141+
if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I)
142+
Af1 = convert(Array, f1)
143+
Af2 = permutedims(convert(Array, f2), [N:-1:1; N + 1])
144+
sz1 = size(Af1)
145+
sz2 = size(Af2)
146+
d1 = prod(sz1[1:(end - 1)])
147+
d2 = prod(sz2[1:(end - 1)])
148+
dc = sz1[end]
149+
A = reshape(
150+
reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))',
151+
(sz1[1:(end - 1)]..., sz2[1:(end - 1)]...)
152+
)
153+
A2 = zero(A)
154+
for ((f1′, f2′), coeff) in d
155+
Af1′ = convert(Array, f1′)
156+
Af2′ = permutedims(convert(Array, f2′), [(2N - n):-1:1; 2N - n + 1])
157+
sz1′ = size(Af1′)
158+
sz2′ = size(Af2′)
159+
d1′ = prod(sz1′[1:(end - 1)])
160+
d2′ = prod(sz2′[1:(end - 1)])
161+
dc′ = sz1′[end]
162+
A2 += coeff *
163+
reshape(
164+
reshape(Af1′, (d1′, dc′)) * reshape(Af2′, (d2′, dc′))',
165+
(sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...)
166+
)
167+
end
168+
@test A A2
169+
end
170+
end
171+
end
172+
@testset "Double fusion tree $Istr: permutation" begin
173+
if BraidingStyle(I) isa SymmetricBraiding
174+
for n in 0:(2N)
175+
p = (randperm(2 * N)...,)
176+
p1, p2 = p[1:n], p[(n + 1):(2N)]
177+
ip = invperm(p)
178+
ip1, ip2 = ip[1:N], ip[(N + 1):(2N)]
179+
180+
d = @constinferred TK.permute(f1, f2, p1, p2)
181+
@test dim(incoming)
182+
sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d)
183+
d2 = Dict{typeof((f1, f2)), valtype(d)}()
184+
for ((f1′, f2′), coeff) in d
185+
d′ = TK.permute(f1′, f2′, ip1, ip2)
186+
for ((f1′′, f2′′), coeff2) in d′
187+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) +
188+
coeff2 * coeff
189+
end
190+
end
191+
for ((f1′, f2′), coeff2) in d2
192+
if f1 == f1′ && f2 == f2′
193+
@test coeff2 1
194+
else
195+
@test abs(coeff2) < 1.0e-12
196+
end
197+
end
198+
199+
if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I)
200+
A = convert(Array, (f1, f2))
201+
Ap = permutedims(A, (p1..., p2...))
202+
A2 = zero(Ap)
203+
for ((f1′, f2′), coeff) in d
204+
A2 .+= coeff .* convert(Array, (f1′, f2′))
205+
end
206+
@test Ap A2
207+
end
208+
end
209+
end
210+
end
211+
@testset "Double fusion tree $Istr: transposition" begin
212+
for n in 0:(2N)
213+
i0 = rand(1:(2N))
214+
p = mod1.(i0 .+ (1:(2N)), 2N)
215+
ip = mod1.(-i0 .+ (1:(2N)), 2N)
216+
p′ = tuple(getindex.(Ref(vcat(1:N, (2N):-1:(N + 1))), p)...)
217+
p1, p2 = p′[1:n], p′[(2N):-1:(n + 1)]
218+
ip′ = tuple(getindex.(Ref(vcat(1:n, (2N):-1:(n + 1))), ip)...)
219+
ip1, ip2 = ip′[1:N], ip′[(2N):-1:(N + 1)]
220+
221+
d = @constinferred transpose(f1, f2, p1, p2)
222+
@test dim(incoming)
223+
sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d)
224+
d2 = Dict{typeof((f1, f2)), valtype(d)}()
225+
for ((f1′, f2′), coeff) in d
226+
d′ = transpose(f1′, f2′, ip1, ip2)
227+
for ((f1′′, f2′′), coeff2) in d′
228+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff
229+
end
230+
end
231+
for ((f1′, f2′), coeff2) in d2
232+
if f1 == f1′ && f2 == f2′
233+
@test coeff2 1
234+
else
235+
@test abs(coeff2) < 1.0e-12
236+
end
237+
end
238+
239+
if BraidingStyle(I) isa Bosonic
240+
d3 = permute(f1, f2, p1, p2)
241+
for (f1′, f2′) in union(keys(d), keys(d3))
242+
coeff1 = get(d, (f1′, f2′), zero(valtype(d)))
243+
coeff3 = get(d3, (f1′, f2′), zero(valtype(d3)))
244+
@test isapprox(coeff1, coeff3; atol = 1.0e-12)
245+
end
246+
end
247+
248+
if (BraidingStyle(I) isa Bosonic) && hasfusiontensor(I)
249+
Af1 = convert(Array, f1)
250+
Af2 = convert(Array, f2)
251+
sz1 = size(Af1)
252+
sz2 = size(Af2)
253+
d1 = prod(sz1[1:(end - 1)])
254+
d2 = prod(sz2[1:(end - 1)])
255+
dc = sz1[end]
256+
A = reshape(
257+
reshape(Af1, (d1, dc)) * reshape(Af2, (d2, dc))',
258+
(sz1[1:(end - 1)]..., sz2[1:(end - 1)]...)
259+
)
260+
Ap = permutedims(A, (p1..., p2...))
261+
A2 = zero(Ap)
262+
for ((f1′, f2′), coeff) in d
263+
Af1′ = convert(Array, f1′)
264+
Af2′ = convert(Array, f2′)
265+
sz1′ = size(Af1′)
266+
sz2′ = size(Af2′)
267+
d1′ = prod(sz1′[1:(end - 1)])
268+
d2′ = prod(sz2′[1:(end - 1)])
269+
dc′ = sz1′[end]
270+
A2 += coeff * reshape(
271+
reshape(Af1′, (d1′, dc′)) *
272+
reshape(Af2′, (d2′, dc′))',
273+
(sz1′[1:(end - 1)]..., sz2′[1:(end - 1)]...)
274+
)
275+
end
276+
@test Ap A2
277+
end
278+
end
279+
end
280+
@testset "Double fusion tree $Istr: planar trace" begin
281+
d1 = transpose(f1, f1, (N + 1, 1:N..., ((2N):-1:(N + 3))...), (N + 2,))
282+
f1front, = TK.split(f1, N - 1)
283+
T = sectorscalartype(I)
284+
d2 = Dict{typeof((f1front, f1front)), T}()
285+
for ((f1′, f2′), coeff′) in d1
286+
for ((f1′′, f2′′), coeff′′) in
287+
TK.planar_trace(
288+
f1′, f2′, (2:N...,), (1, ((2N):-1:(N + 3))...), (N + 1,),
289+
(N + 2,)
290+
)
291+
coeff = coeff′ * coeff′′
292+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff
293+
end
294+
end
295+
for ((f1_, f2_), coeff) in d2
296+
if (f1_, f2_) == (f1front, f1front)
297+
@test coeff dim(f1.coupled) / dim(f1front.coupled)
298+
else
299+
@test abs(coeff) < 1.0e-12
300+
end
301+
end
302+
end
303+
TK.empty_globalcaches!()
304+
end

0 commit comments

Comments
 (0)