|
| 1 | +using MultiTensorKit |
| 2 | +using TensorKitSectors, TensorKit |
| 3 | +using Test, TestExtras |
| 4 | +using Random |
| 5 | + |
| 6 | +const MTK = MultiTensorKit |
| 7 | +const TK = TensorKit |
| 8 | + |
1 | 9 | I = A4Object |
2 | 10 | Istr = TensorKitSectors.type_repr(I) |
3 | 11 | r = size(I) |
@@ -178,12 +186,6 @@ println("---------------------------------") |
178 | 186 | i = 1 |
179 | 187 | j = 2 |
180 | 188 |
|
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 | | - |
187 | 189 | @timedtestset "Multifusion spaces " verbose = true begin |
188 | 190 | @timedtestset "GradedSpace: $(TK.type_repr(Vect[I]))" begin |
189 | 191 | 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)), |
308 | 310 | @test_throws SpaceMismatch (⊕(V, V')) |
309 | 311 | end |
310 | 312 |
|
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))) |
312 | 319 |
|
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,) |
315 | 321 | W = HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) |
316 | 322 | @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) |
317 | 323 | @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)), |
348 | 354 | end |
349 | 355 | end |
350 | 356 |
|
| 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 | + |
351 | 607 | @testset "$Istr ($i, $j) left and right units" for i in 1:r, j in 1:r |
352 | 608 | Cij_obs = I.(i, j, MTK._get_dual_cache(I)[2][i, j]) |
353 | 609 |
|
|
0 commit comments