Skip to content

Commit 16104fc

Browse files
committed
finish fusion tree tests (not debugged) + move around imports
1 parent 4870e09 commit 16104fc

File tree

2 files changed

+319
-11
lines changed

2 files changed

+319
-11
lines changed

test/setup.jl

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using MultiTensorKit
2-
using TensorKitSectors, TensorKit
3-
using Test, TestExtras
2+
using TensorKitSectors
3+
using Random
44

55
const MTK = MultiTensorKit
66

7+
Random.seed!(1234)
8+
79
function unitarity_test(as::Vector{I}, bs::Vector{I},
810
cs::Vector{I}) where {I<:BimoduleSector}
911
@assert all(a.j == b.i for a in as, b in bs)
@@ -29,3 +31,53 @@ function unitarity_test(as::Vector{I}, bs::Vector{I},
2931
end
3032
return true
3133
end
34+
35+
all_objects(::Type{<:BimoduleSector}, i::Int, j::Int) = [I(i, j, k) for k in 1:MTK._numlabels(I, i, j)]
36+
37+
function rand_object(I::Type{<:BimoduleSector}, i::Int, j::Int)
38+
obs = all_objects(I, i, j)
39+
ob = rand(obs)
40+
if i == j
41+
while ob == one(ob) # unit of any fusion cat avoided
42+
ob = rand(obs)
43+
end
44+
end
45+
return ob
46+
end
47+
48+
function random_fusion(I::Type{<:BimoduleSector}, i::Int, j::Int, N::Int) # for fusion tree tests
49+
Cs = all_objects(I, i, i)
50+
Ds = all_objects(I, j, j)
51+
Ms = all_objects(I, i, j)
52+
Mops = all_objects(I, j, i)
53+
allobs = vcat(Cs, Ds, Ms, Mops)
54+
55+
in = nothing
56+
out = nothing
57+
while in === nothing
58+
out = ntuple(n -> rand(allobs), N)
59+
try
60+
in = rand(collect((out...)))
61+
catch e
62+
if isa(e, AssertionError)
63+
in = nothing
64+
else
65+
rethrow(e)
66+
end
67+
end
68+
end
69+
return out
70+
end
71+
72+
# for fusion tree merge test
73+
function safe_tensor_product(x::I, y::I) where {I<:BimoduleSector}
74+
try
75+
return x y
76+
catch e
77+
if e isa AssertionError
78+
return nothing
79+
else
80+
rethrow(e)
81+
end
82+
end
83+
end

test/test_A4.jl

Lines changed: 265 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
using MultiTensorKit
2+
using TensorKitSectors, TensorKit
3+
using Test, TestExtras
4+
using Random
5+
6+
const MTK = MultiTensorKit
7+
const TK = TensorKit
8+
19
I = A4Object
210
Istr = TensorKitSectors.type_repr(I)
311
r = size(I)
@@ -178,12 +186,6 @@ println("---------------------------------")
178186
i = 1
179187
j = 2
180188

181-
V = (Vect[I]((i, i, label) => 1 for label in MTK._numlabels(I, i, i)),
182-
Vect[I]((i, i, 1) => 1, (i, i, 2) => 2),
183-
Vect[I]((i, i, 1) => 1, (i, i, 2) => 1),
184-
Vect[I]((i, i, label) => 1 for label in MTK._numlabels(I, i, i)),
185-
Vect[I]((i, i, 1) => 2, (i, i, 3) => 3))
186-
187189
@timedtestset "Multifusion spaces " verbose = true begin
188190
@timedtestset "GradedSpace: $(TK.type_repr(Vect[I]))" begin
189191
gen = (values(I)[k] => (k + 1) for k in 1:length(values(I)))
@@ -308,10 +310,14 @@ V = (Vect[I]((i, i, label) => 1 for label in MTK._numlabels(I, i, i)),
308310
@test_throws SpaceMismatch ((V, V'))
309311
end
310312

311-
# CONTINUE HERE
313+
@timedtestset "HomSpace with $(TK.type_repr(Vect[I])) involving ($i, $j)" for i in 1:r, j in 1:r
314+
V = (Vect[I]((i, i, label) => 1 for label in 1:MTK._numlabels(I, i, i)),
315+
Vect[I]((i, j, label) => 1 for label in 1:MTK._numlabels(I, i, j)),
316+
Vect[I]((i, i, label) => 1 for label in 1:MTK._numlabels(I, i, i)),
317+
Vect[I]((i, j, 1) => 3),
318+
Vect[I]((j, j, label) => 1 for label in 1:MTK._numlabels(I, j, j)))
312319

313-
@timedtestset "HomSpace with $(TK.type_repr(Vect[I])) " begin
314-
for (V1, V2, V3, V4, V5) in (VIBC, VIBD, VIBM1, VIBM2, VIBMop1, VIBMop2)
320+
for (V1, V2, V3, V4, V5) in (V,)
315321
W = HomSpace(V1 V2, V3 V4 V5)
316322
@test W == (V3 V4 V5 V1 V2)
317323
@test W == (V1 V2 V3 V4 V5)
@@ -348,6 +354,256 @@ V = (Vect[I]((i, i, label) => 1 for label in MTK._numlabels(I, i, i)),
348354
end
349355
end
350356

357+
println("---------------------------------------")
358+
println("| Multifusion fusion tree tests |")
359+
println("---------------------------------------")
360+
361+
@timedtestset "Fusion trees for $(TK.type_repr(I)) involving ($i, $j)" verbose = true for i in 1:r, j in 1:7
362+
N = 6
363+
Mop = rand_object(I, j, i)
364+
M = rand_object(I, i, j)
365+
C0 = one(I(i, i, 1))
366+
C1 = rand_object(I, i, i)
367+
D0 = one(I(j, j, 1))
368+
D1 = rand_object(I, j, j)
369+
out = (Mop, C0, C1, M, D0, D1) # should I try to make a non-hardcoded example? could vary number of Cs and Ds, as well as randomly fuse and check if allowed
370+
isdual = ntuple(n -> rand(Bool), N)
371+
in = rand(collect((out...))) # will be in 𝒞ⱼⱼ with this choice of out
372+
373+
numtrees = length(fusiontrees(out, in, isdual)) # will be 1 for i != j
374+
@test numtrees == count(n -> true, fusiontrees(out, in, isdual))
375+
376+
it = @constinferred fusiontrees(out, in, isdual)
377+
@constinferred Nothing iterate(it)
378+
f, s = iterate(it)
379+
@constinferred Nothing iterate(it, s)
380+
@test f == @constinferred first(it)
381+
@testset "Fusion tree $Istr: printing" begin
382+
@test eval(Meta.parse(sprint(show, f))) == f
383+
end
384+
385+
@testset "Fusion tree $Istr: constructor properties" for u in (C0, D0)
386+
@constinferred FusionTree((), u, (), (), ())
387+
@constinferred FusionTree((u,), u, (false,), (), ())
388+
@constinferred FusionTree((u, u), u, (false, false), (), (1,))
389+
@constinferred FusionTree((u, u, u), u, (false, false, false), (u,), (1, 1))
390+
@constinferred FusionTree((u, u, u, u), u, (false, false, false, false), (u, u),
391+
(1, 1, 1))
392+
@test_throws MethodError FusionTree((u, u, u), u, (false, false), (u,), (1, 1))
393+
@test_throws MethodError FusionTree((u, u, u), u, (false, false, false), (u, u),
394+
(1, 1))
395+
@test_throws MethodError FusionTree((u, u, u), u, (false, false, false), (u,),
396+
(1, 1, 1))
397+
@test_throws MethodError FusionTree((u, u, u), u, (false, false, false), (), (1,))
398+
399+
f = FusionTree((u, u, u), u, (false, false, false), (u,), (1, 1))
400+
@test sectortype(f) == I
401+
@test length(f) == 3
402+
@test FusionStyle(f) == FusionStyle(I)
403+
@test BraidingStyle(f) == BraidingStyle(I)
404+
405+
# SimpleFusion
406+
errstr = "fusion tree requires inner lines if `FusionStyle(I) <: MultipleFusion`"
407+
@test_throws errstr FusionTree((), u, ())
408+
@test_throws errstr FusionTree((u,), u, (false,))
409+
@test_throws errstr FusionTree((u, u), u, (false, false))
410+
@test_throws errstr FusionTree((u, u, u), u)
411+
@test_throws errstr FusionTree((u, u, u, u)) # custom FusionTree constructor required here
412+
end
413+
414+
@testset "Fusion tree $Istr: insertat" begin
415+
N = 4
416+
out2 = random_fusion(I, i, j, N)
417+
in2 = rand(collect((out2...)))
418+
isdual2 = ntuple(n -> rand(Bool), N)
419+
f2 = rand(collect(fusiontrees(out2, in2, isdual2)))
420+
for k in 1:N
421+
out1, in1 = nothing, nothing
422+
while in1 === nothing
423+
try
424+
out1 = random_fusion(I, i, j, N) # guaranteed good fusion
425+
out1 = Base.setindex(out1, in2, k) # can lead to poor fusion
426+
in1 = rand(collect((out1...)))
427+
catch e
428+
if isa(e, AssertionError)
429+
in1 = nothing # keep trying till out1 is compatible with inserting in2 at k
430+
else
431+
rethrow(e)
432+
end
433+
end
434+
end
435+
isdual1 = ntuple(n -> rand(Bool), N)
436+
isdual1 = Base.setindex(isdual1, false, k)
437+
f1 = rand(collect(fusiontrees(out1, in1, isdual1)))
438+
439+
trees = @constinferred TK.insertat(f1, k, f2)
440+
@test norm(values(trees)) 1
441+
442+
f1a, f1b = @constinferred TK.split(f1, $k)
443+
@test length(TK.insertat(f1b, 1, f1a)) == 1
444+
@test first(TK.insertat(f1b, 1, f1a)) == (f1 => 1)
445+
446+
# no braid tests for non-hardcoded example
447+
end
448+
end
449+
# no planar trace tests
450+
451+
@testset "Fusion tree $Istr: elementary artin braid" begin
452+
N = length(out)
453+
isdual = ntuple(n -> rand(Bool), N)
454+
# no general artin braid test
455+
456+
# not sure how useful this test is, it does the trivial braiding (choice of out)
457+
f = rand(collect(it)) # in this case the 1 tree
458+
d1 = TK.artin_braid(f, 2) # takes unit C0 with current out
459+
d2 = empty(d1)
460+
for (f1, coeff1) in d1
461+
for (f2, coeff2) in TK.artin_braid(f1, 3)
462+
d2[f2] = get(d2, f2, zero(coeff1)) + coeff2 * coeff1
463+
end
464+
end
465+
d1 = d2
466+
d2 = empty(d1)
467+
for (f1, coeff1) in d1
468+
for (f2, coeff2) in TK.artin_braid(f1, 3; inv=true)
469+
d2[f2] = get(d2, f2, zero(coeff1)) + coeff2 * coeff1
470+
end
471+
end
472+
d1 = d2
473+
d2 = empty(d1)
474+
for (f1, coeff1) in d1
475+
for (f2, coeff2) in TK.artin_braid(f1, 2; inv=true)
476+
d2[f2] = get(d2, f2, zero(coeff1)) + coeff2 * coeff1
477+
end
478+
end
479+
d1 = d2
480+
for (f1, coeff1) in d1
481+
if f1 == f
482+
@test coeff1 1
483+
else
484+
@test isapprox(coeff1, 0; atol=1.0e-12, rtol=1.0e-12)
485+
end
486+
end
487+
end
488+
489+
# no braiding and permuting test
490+
@testset "Fusion tree $Istr: merging" begin
491+
N = 3
492+
out1 = random_fusion(I, i, j, N)
493+
out2 = random_fusion(I, i, j, N)
494+
in1 = rand(collect((out1...)))
495+
in2 = rand(collect((out2...)))
496+
tp = safe_tensor_product(in1, in2) # messy solution but it works
497+
while tp === nothing
498+
out1 = random_fusion(I, i, j, N)
499+
out2 = random_fusion(I, i, j, N)
500+
in1 = rand(collect((out1...)))
501+
in2 = rand(collect((out2...)))
502+
tp = safe_tensor_product(in1, in2)
503+
end
504+
505+
f1 = rand(collect(fusiontrees(out1, in1)))
506+
f2 = rand(collect(fusiontrees(out2, in2)))
507+
508+
509+
@test dim(in1) * dim(in2) sum(abs2(coeff) * dim(c) for c in in1 in2
510+
for μ in 1:Nsymbol(in1, in2, c)
511+
for (f, coeff) in TK.merge(f1, f2, c, μ))
512+
# no merge and braid interplay tests
513+
end
514+
515+
# hardcoded double fusion tree tests
516+
N = 6
517+
out = (Mop, C0, C1, M, D0, D1) # same as above
518+
out2 = (D0, D1, Mop, C0, C1, M) # different order that still fuses to D0 or D1
519+
520+
incoming = rand(collect((out...))) # will be in 𝒞ⱼⱼ
521+
while incoming collect((out2...)) # when i = j these don't necessarily fuse to the same object, since Mop x M doesn't return all objects in 𝒞ᵢᵢ
522+
Mop = rand_object(I, j, i)
523+
out2 = (D0, D1, Mop, C0, C1, M)
524+
@show i,j
525+
end
526+
527+
f1 = rand(collect(fusiontrees(out, incoming, ntuple(n -> rand(Bool), N))))
528+
@info "before here?"
529+
f2 = rand(collect(fusiontrees(out2, incoming, ntuple(n -> rand(Bool), N))))
530+
@info "or over here?"
531+
532+
@testset "Double fusion tree $Istr: repartitioning" begin
533+
for n in 0:(2 * N)
534+
d = @constinferred TK.repartition(f1, f2, $n)
535+
@test dim(incoming)
536+
sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d)
537+
d2 = Dict{typeof((f1, f2)),valtype(d)}()
538+
for ((f1′, f2′), coeff) in d
539+
for ((f1′′, f2′′), coeff2) in TK.repartition(f1′, f2′, N)
540+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff
541+
end
542+
end
543+
for ((f1′, f2′), coeff2) in d2
544+
if f1 == f1′ && f2 == f2′
545+
@test coeff2 1
546+
else
547+
@test isapprox(coeff2, 0; atol=1.0e-12, rtol=1.0e-12)
548+
end
549+
end
550+
end
551+
end
552+
553+
# no double fusion tree permutation tests
554+
@testset "Double fusion tree $Istr: transposition" begin
555+
for n in 0:(2N)
556+
i0 = rand(1:(2N))
557+
p = mod1.(i0 .+ (1:(2N)), 2N)
558+
ip = mod1.(-i0 .+ (1:(2N)), 2N)
559+
p′ = tuple(getindex.(Ref(vcat(1:N, (2N):-1:(N + 1))), p)...)
560+
p1, p2 = p′[1:n], p′[(2N):-1:(n + 1)]
561+
ip′ = tuple(getindex.(Ref(vcat(1:n, (2N):-1:(n + 1))), ip)...)
562+
ip1, ip2 = ip′[1:N], ip′[(2N):-1:(N + 1)]
563+
564+
d = @constinferred transpose(f1, f2, p1, p2)
565+
@test dim(incoming)
566+
sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d)
567+
d2 = Dict{typeof((f1, f2)),valtype(d)}()
568+
for ((f1′, f2′), coeff) in d
569+
d′ = transpose(f1′, f2′, ip1, ip2)
570+
for ((f1′′, f2′′), coeff2) in d′
571+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff
572+
end
573+
end
574+
for ((f1′, f2′), coeff2) in d2
575+
if f1 == f1′ && f2 == f2′
576+
@test coeff2 1
577+
else
578+
@test abs(coeff2) < 1.0e-12
579+
end
580+
end
581+
end
582+
end
583+
584+
@testset "Double fusion tree $Istr: planar trace" begin
585+
d1 = transpose(f1, f1, (N + 1, 1:N..., ((2N):-1:(N + 3))...), (N + 2,))
586+
f1front, = TK.split(f1, N - 1)
587+
T = sectorscalartype(I)
588+
d2 = Dict{typeof((f1front, f1front)),T}()
589+
for ((f1′, f2′), coeff′) in d1
590+
for ((f1′′, f2′′), coeff′′) in
591+
TK.planar_trace(f1′, f2′, (2:N...,), (1, ((2N):-1:(N + 3))...), (N + 1,),
592+
(N + 2,))
593+
coeff = coeff′ * coeff′′
594+
d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff
595+
end
596+
end
597+
for ((f1_, f2_), coeff) in d2
598+
if (f1_, f2_) == (f1front, f1front)
599+
@test coeff dim(f1.coupled) / dim(f1front.coupled)
600+
else
601+
@test abs(coeff) < 1.0e-12
602+
end
603+
end
604+
end
605+
end
606+
351607
@testset "$Istr ($i, $j) left and right units" for i in 1:r, j in 1:r
352608
Cij_obs = I.(i, j, MTK._get_dual_cache(I)[2][i, j])
353609

0 commit comments

Comments
 (0)