From 7d5fd03c0a00265a75c192da7f361a3c3a5f72d8 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 15:51:44 -0400 Subject: [PATCH 01/16] setup test framework --- Project.toml | 6 +++++- test/runtests.jl | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index aacd69f69..0f580e579 100644 --- a/Project.toml +++ b/Project.toml @@ -27,6 +27,7 @@ TensorKitFiniteDifferencesExt = "FiniteDifferences" [compat] Aqua = "0.6, 0.7, 0.8" +ArgParse = "1.2.0" ChainRulesCore = "1" ChainRulesTestUtils = "1" Combinatorics = "1" @@ -37,6 +38,7 @@ MatrixAlgebraKit = "0.5.0" OhMyThreads = "0.8.0" PackageExtensionCompat = "1" Random = "1" +SafeTestsets = "0.1" ScopedValues = "1.3.0" Strided = "2" TensorKitSectors = "0.3" @@ -49,16 +51,18 @@ Zygote = "0.7" julia = "1.10" [extras] +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestExtras = "5ed8adda-3752-4e41-b88a-e8b09835ee3a" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Aqua", "Combinatorics", "LinearAlgebra", "TensorOperations", "Test", "TestExtras", "ChainRulesCore", "ChainRulesTestUtils", "FiniteDifferences", "Zygote"] +test = ["ArgParse", "Aqua", "Combinatorics", "LinearAlgebra", "TensorOperations", "Test", "TestExtras", "SafeTestsets", "ChainRulesCore", "ChainRulesTestUtils", "FiniteDifferences", "Zygote"] diff --git a/test/runtests.jl b/test/runtests.jl index 3ae62e9bc..dc38757c7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,55 @@ +# ARGS parsing +# ------------ +using ArgParse: ArgParse +using SafeTestsets: @safetestset + +function parse_commandline(args = ARGS) + s = ArgParse.ArgParseSettings() + ArgParse.@add_arg_table! s begin + "--groups" + action => :store_arg + nargs => '+' + arg_type = String + end + return ArgParse.parse_args(args, s; as_symbols = true) +end + +settings = parse_commandline() + +if isempty(settings[:groups]) + if haskey(ENV, "GROUP") + groups = [ENV["GROUP"]] + else + groups = filter(isdir, readdir(@__DIR__)) + end +else + groups = settings[:groups] +end + +checktestgroup(group) = isdir(joinpath(@__DIR__, group)) || + throw(ArgumentError("Invalid group ($group), no such folder")) +foreach(checktestgroup, groups) + +# Run test groups +# --------------- + +"match files of the form `*.jl`, but exclude `*setup*.jl`" +istestfile(fn) = endswith(fn, ".jl") && !contains(fn, "setup") + +# process test groups +@time for group in groups + @info "Running test group: $group" + grouppath = joinpath(@__DIR__, group) + @time for file in filter(istestfile, readdir(grouppath)) + @info "Running test file: $file" + filepath = joinpath(grouppath, file) + @eval @safetestset $file begin + include($filepath) + end + end +end + + using Test using TestExtras using Random From d806af5ac176a12645ffa8183e6502418cf210a0 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 16:06:13 -0400 Subject: [PATCH 02/16] setup buildkite --- test/runtests.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index dc38757c7..a12ef7423 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,8 @@ # ------------ using ArgParse: ArgParse using SafeTestsets: @safetestset +# using CUDA: CUDA +# using AMDGPU: AMDGPU function parse_commandline(args = ARGS) s = ArgParse.ArgParseSettings() @@ -30,6 +32,9 @@ checktestgroup(group) = isdir(joinpath(@__DIR__, group)) || throw(ArgumentError("Invalid group ($group), no such folder")) foreach(checktestgroup, groups) +# don't run all tests on GPU, only the GPU specific ones +is_buildkite = get(ENV, "BUILDKITE", "false") == "true" + # Run test groups # --------------- @@ -39,6 +44,16 @@ istestfile(fn) = endswith(fn, ".jl") && !contains(fn, "setup") # process test groups @time for group in groups @info "Running test group: $group" + + # handle GPU cases separately + if group == "cuda" + # CUDA.functional() || continue + elseif group == "amd" + # AMDGPU.functional() || continue + elseif is_buildkite + continue + end + grouppath = joinpath(@__DIR__, group) @time for file in filter(istestfile, readdir(grouppath)) @info "Running test file: $file" From 2d90cc16d6e32bd08bf4b46bcf6a6c24179c8113 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 16:13:56 -0400 Subject: [PATCH 03/16] skip AD tests on apple and prerelease --- test/runtests.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index a12ef7423..41ed2b648 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -54,6 +54,13 @@ istestfile(fn) = endswith(fn, ".jl") && !contains(fn, "setup") continue end + # somehow AD tests are unreasonably slow on Apple CI + # and ChainRulesTestUtils doesn't like prereleases + if group == "autodiff" + Sys.isapple() && get(ENV, "CI", "false") == "true" && continue + isempty(VERSION.prerelease) || continue + end + grouppath = joinpath(@__DIR__, group) @time for file in filter(istestfile, readdir(grouppath)) @info "Running test file: $file" From f9d7665572524d12a13912100e737094b374db67 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 16:15:18 -0400 Subject: [PATCH 04/16] separate tests into groups --- test/{ => autodiff}/ad.jl | 3 + test/braidingtensor.jl | 1 + test/bugfixes.jl | 83 ----- test/other/aqua.jl | 4 + test/other/bugfixes.jl | 87 ++++++ test/runtests.jl | 180 ----------- test/setup.jl | 168 ++++++++++ test/{ => symmetries}/fusiontrees.jl | 25 +- test/symmetries/spaces.jl | 440 +++++++++++++++++++++++++++ test/{ => tensors}/diagonal.jl | 9 +- test/{ => tensors}/factorizations.jl | 7 + test/{ => tensors}/planar.jl | 72 +---- test/{ => tensors}/tensors.jl | 10 +- 13 files changed, 742 insertions(+), 347 deletions(-) rename test/{ => autodiff}/ad.jl (99%) delete mode 100644 test/bugfixes.jl create mode 100644 test/other/aqua.jl create mode 100644 test/other/bugfixes.jl create mode 100644 test/setup.jl rename test/{ => symmetries}/fusiontrees.jl (98%) create mode 100644 test/symmetries/spaces.jl rename test/{ => tensors}/diagonal.jl (98%) rename test/{ => tensors}/factorizations.jl (98%) rename test/{ => tensors}/planar.jl (83%) rename test/{ => tensors}/tensors.jl (99%) diff --git a/test/ad.jl b/test/autodiff/ad.jl similarity index 99% rename from test/ad.jl rename to test/autodiff/ad.jl index 553bca745..fcc5032bb 100644 --- a/test/ad.jl +++ b/test/autodiff/ad.jl @@ -1,3 +1,6 @@ +using Test, TestExtras +using TensorKit +using TensorOperations using ChainRulesCore using ChainRulesTestUtils using FiniteDifferences: FiniteDifferences, central_fdm, forward_fdm diff --git a/test/braidingtensor.jl b/test/braidingtensor.jl index f290bc7f9..9b06b3536 100644 --- a/test/braidingtensor.jl +++ b/test/braidingtensor.jl @@ -1,4 +1,5 @@ # TODO: Make into proper tests and integrate in testset +# note: this is not part of the testsuite! import TensorKit: BraidingTensor diff --git a/test/bugfixes.jl b/test/bugfixes.jl deleted file mode 100644 index b65ae6159..000000000 --- a/test/bugfixes.jl +++ /dev/null @@ -1,83 +0,0 @@ -@timedtestset "Bugfixes" verbose = true begin - @testset "BugfixConvert" begin - v = randn( - ComplexF64, - ( - Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]( - (-3, 1 / 2, 1) => 3, (-5, 1 / 2, 1) => 10, (-7, 1 / 2, 1) => 13, - (-9, 1 / 2, 1) => 9, (-11, 1 / 2, 1) => 1, (-5, 3 / 2, 1) => 3, - (-7, 3 / 2, 1) => 3, (-9, 3 / 2, 1) => 1 - ) ⊗ Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]((1, 1 / 2, 1) => 1)' - ) ← Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]( - (-3, 1 / 2, 1) => 3, (-5, 1 / 2, 1) => 10, (-7, 1 / 2, 1) => 13, - (-9, 1 / 2, 1) => 9, (-11, 1 / 2, 1) => 1, (-5, 3 / 2, 1) => 3, - (-7, 3 / 2, 1) => 3, (-9, 3 / 2, 1) => 1 - ) - ) - w = convert(typeof(real(v)), v) - @test w == v - @test scalartype(w) == Float64 - end - - # https://github.com/quantumkithub/TensorKit.jl/issues/178 - @testset "Issue #178" begin - t = rand(U1Space(1 => 1) ← U1Space(1 => 1)') - a = convert(Array, t) - @test a == zeros(size(a)) - end - - # https://github.com/quantumkithub/TensorKit.jl/issues/194 - @testset "Issue #194" begin - t1 = rand(ℂ^4 ← ℂ^4) - t2 = tensoralloc(typeof(t1), space(t1), Val(true), TensorOperations.ManualAllocator()) - t3 = similar(t2, ComplexF64, space(t1)) - @test storagetype(t3) == Vector{ComplexF64} - t4 = similar(t2, domain(t1)) - @test storagetype(t4) == Vector{Float64} - t5 = similar(t2) - @test storagetype(t5) == Vector{Float64} - tensorfree!(t2) - end - - # https://github.com/quantumkithub/TensorKit.jl/issues/201 - @testset "Issue #201" begin - function f(A::AbstractTensorMap) - U, S, V, = svd_compact(A) - return tr(S) - end - function f(A::AbstractMatrix) - S = LinearAlgebra.svdvals(A) - return sum(S) - end - A₀ = randn(Z2Space(4, 4) ← Z2Space(4, 4)) - grad1, = Zygote.gradient(f, A₀) - grad2, = Zygote.gradient(f, convert(Array, A₀)) - @test convert(Array, grad1) ≈ grad2 - - function g(A::AbstractTensorMap) - U, S, V, = svd_compact(A) - return tr(U * V) - end - function g(A::AbstractMatrix) - U, S, V, = LinearAlgebra.svd(A) - return tr(U * V') - end - B₀ = randn(ComplexSpace(4) ← ComplexSpace(4)) - grad3, = Zygote.gradient(g, B₀) - grad4, = Zygote.gradient(g, convert(Array, B₀)) - @test convert(Array, grad3) ≈ grad4 - end - - # https://github.com/quantumkithub/TensorKit.jl/issues/209 - @testset "Issue #209" begin - function f(T, D) - @tensor T[1, 4, 1, 3] * D[3, 4] - end - V = Z2Space(2, 2) - D = DiagonalTensorMap(randn(4), V) - T = randn(V ⊗ V ← V ⊗ V) - g1, = Zygote.gradient(f, T, D) - g2, = Zygote.gradient(f, T, TensorMap(D)) - @test g1 ≈ g2 - end -end diff --git a/test/other/aqua.jl b/test/other/aqua.jl new file mode 100644 index 000000000..c0ebf29cf --- /dev/null +++ b/test/other/aqua.jl @@ -0,0 +1,4 @@ +using TensorKit +using Aqua + +Aqua.test_all(TensorKit) diff --git a/test/other/bugfixes.jl b/test/other/bugfixes.jl new file mode 100644 index 000000000..8fa9ab6df --- /dev/null +++ b/test/other/bugfixes.jl @@ -0,0 +1,87 @@ +using Test, TestExtras +using TensorKit +using TensorOperations +using LinearAlgebra: LinearAlgebra +using Zygote + +@testset "BugfixConvert" begin + v = randn( + ComplexF64, + ( + Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]( + (-3, 1 / 2, 1) => 3, (-5, 1 / 2, 1) => 10, (-7, 1 / 2, 1) => 13, + (-9, 1 / 2, 1) => 9, (-11, 1 / 2, 1) => 1, (-5, 3 / 2, 1) => 3, + (-7, 3 / 2, 1) => 3, (-9, 3 / 2, 1) => 1 + ) ⊗ Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]((1, 1 / 2, 1) => 1)' + ) ← Vect[(Irrep[U₁] ⊠ Irrep[SU₂] ⊠ FermionParity)]( + (-3, 1 / 2, 1) => 3, (-5, 1 / 2, 1) => 10, (-7, 1 / 2, 1) => 13, + (-9, 1 / 2, 1) => 9, (-11, 1 / 2, 1) => 1, (-5, 3 / 2, 1) => 3, + (-7, 3 / 2, 1) => 3, (-9, 3 / 2, 1) => 1 + ) + ) + w = convert(typeof(real(v)), v) + @test w == v + @test scalartype(w) == Float64 +end + +# https://github.com/quantumkithub/TensorKit.jl/issues/178 +@testset "Issue #178" begin + t = rand(U1Space(1 => 1) ← U1Space(1 => 1)') + a = convert(Array, t) + @test a == zeros(size(a)) +end + +# https://github.com/quantumkithub/TensorKit.jl/issues/194 +@testset "Issue #194" begin + t1 = rand(ℂ^4 ← ℂ^4) + t2 = tensoralloc(typeof(t1), space(t1), Val(true), TensorOperations.ManualAllocator()) + t3 = similar(t2, ComplexF64, space(t1)) + @test storagetype(t3) == Vector{ComplexF64} + t4 = similar(t2, domain(t1)) + @test storagetype(t4) == Vector{Float64} + t5 = similar(t2) + @test storagetype(t5) == Vector{Float64} + tensorfree!(t2) +end + +# https://github.com/quantumkithub/TensorKit.jl/issues/201 +@testset "Issue #201" begin + function f(A::AbstractTensorMap) + U, S, V, = svd_compact(A) + return tr(S) + end + function f(A::AbstractMatrix) + S = LinearAlgebra.svdvals(A) + return sum(S) + end + A₀ = randn(Z2Space(4, 4) ← Z2Space(4, 4)) + grad1, = Zygote.gradient(f, A₀) + grad2, = Zygote.gradient(f, convert(Array, A₀)) + @test convert(Array, grad1) ≈ grad2 + + function g(A::AbstractTensorMap) + U, S, V, = svd_compact(A) + return tr(U * V) + end + function g(A::AbstractMatrix) + U, S, V, = LinearAlgebra.svd(A) + return tr(U * V') + end + B₀ = randn(ComplexSpace(4) ← ComplexSpace(4)) + grad3, = Zygote.gradient(g, B₀) + grad4, = Zygote.gradient(g, convert(Array, B₀)) + @test convert(Array, grad3) ≈ grad4 +end + +# https://github.com/quantumkithub/TensorKit.jl/issues/209 +@testset "Issue #209" begin + function f(T, D) + @tensor T[1, 4, 1, 3] * D[3, 4] + end + V = Z2Space(2, 2) + D = DiagonalTensorMap(randn(4), V) + T = randn(V ⊗ V ← V ⊗ V) + g1, = Zygote.gradient(f, T, D) + g2, = Zygote.gradient(f, T, TensorMap(D)) + @test g1 ≈ g2 +end diff --git a/test/runtests.jl b/test/runtests.jl index 41ed2b648..2ad193f15 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -70,183 +70,3 @@ istestfile(fn) = endswith(fn, ".jl") && !contains(fn, "setup") end end end - - -using Test -using TestExtras -using Random -using TensorKit -using Combinatorics -using TensorKit: ProductSector, fusiontensor -using TensorKitSectors: TensorKitSectors -using TensorOperations -using Base.Iterators: take, product -# using SUNRepresentations: SUNIrrep -# const SU3Irrep = SUNIrrep{3} -using LinearAlgebra: LinearAlgebra -using Zygote: Zygote - -const TK = TensorKit - -Random.seed!(1234) - -# don't run all tests on GPU, only the GPU -# specific ones -is_buildkite = get(ENV, "BUILDKITE", "false") == "true" - -smallset(::Type{I}) where {I <: Sector} = take(values(I), 5) -function smallset(::Type{ProductSector{Tuple{I1, I2}}}) where {I1, I2} - iter = product(smallset(I1), smallset(I2)) - s = collect(i ⊠ j for (i, j) in iter if dim(i) * dim(j) <= 6) - return length(s) > 6 ? rand(s, 6) : s -end -function smallset(::Type{ProductSector{Tuple{I1, I2, I3}}}) where {I1, I2, I3} - iter = product(smallset(I1), smallset(I2), smallset(I3)) - s = collect(i ⊠ j ⊠ k for (i, j, k) in iter if dim(i) * dim(j) * dim(k) <= 6) - return length(s) > 6 ? rand(s, 6) : s -end -function randsector(::Type{I}) where {I <: Sector} - s = collect(smallset(I)) - a = rand(s) - while isunit(a) # don't use trivial label - a = rand(s) - end - return a -end -function hasfusiontensor(I::Type{<:Sector}) - try - fusiontensor(unit(I), unit(I), unit(I)) - return true - catch e - if e isa MethodError - return false - else - rethrow(e) - end - end -end - -sectorlist = ( - Z2Irrep, Z3Irrep, Z4Irrep, Z3Irrep ⊠ Z4Irrep, - U1Irrep, CU1Irrep, SU2Irrep, - FermionParity, FermionParity ⊠ FermionParity, - FermionParity ⊠ U1Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ SU2Irrep, # Hubbard-like - FibonacciAnyon, IsingAnyon, - Z2Irrep ⊠ FibonacciAnyon ⊠ FibonacciAnyon, -) - -# spaces -Vtr = (ℂ^2, (ℂ^3)', ℂ^4, ℂ^3, (ℂ^2)') -Vℤ₂ = ( - Vect[Z2Irrep](0 => 1, 1 => 1), - Vect[Z2Irrep](0 => 1, 1 => 2)', - Vect[Z2Irrep](0 => 3, 1 => 2)', - Vect[Z2Irrep](0 => 2, 1 => 3), - Vect[Z2Irrep](0 => 2, 1 => 5), -) -Vfℤ₂ = ( - Vect[FermionParity](0 => 1, 1 => 1), - Vect[FermionParity](0 => 1, 1 => 2)', - Vect[FermionParity](0 => 2, 1 => 1)', - Vect[FermionParity](0 => 2, 1 => 3), - Vect[FermionParity](0 => 2, 1 => 5), -) -Vℤ₃ = ( - Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 1), - Vect[Z3Irrep](0 => 2, 1 => 1, 2 => 1), - Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 1)', - Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 3), - Vect[Z3Irrep](0 => 1, 1 => 3, 2 => 3)', -) -VU₁ = ( - Vect[U1Irrep](0 => 1, 1 => 2, -1 => 2), - Vect[U1Irrep](0 => 3, 1 => 1, -1 => 1), - Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', - Vect[U1Irrep](0 => 1, 1 => 2, -1 => 3), - Vect[U1Irrep](0 => 1, 1 => 3, -1 => 3)', -) -VfU₁ = ( - Vect[FermionNumber](0 => 1, 1 => 2, -1 => 2), - Vect[FermionNumber](0 => 3, 1 => 1, -1 => 1), - Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1)', - Vect[FermionNumber](0 => 1, 1 => 2, -1 => 3), - Vect[FermionNumber](0 => 1, 1 => 3, -1 => 3)', -) -VCU₁ = ( - Vect[CU1Irrep]((0, 0) => 1, (0, 1) => 2, 1 => 1), - Vect[CU1Irrep]((0, 0) => 3, (0, 1) => 0, 1 => 1), - Vect[CU1Irrep]((0, 0) => 1, (0, 1) => 0, 1 => 2)', - Vect[CU1Irrep]((0, 0) => 2, (0, 1) => 2, 1 => 1), - Vect[CU1Irrep]((0, 0) => 2, (0, 1) => 1, 1 => 2)', -) -VSU₂ = ( - Vect[SU2Irrep](0 => 3, 1 // 2 => 1), - Vect[SU2Irrep](0 => 2, 1 => 1), - Vect[SU2Irrep](1 // 2 => 1, 1 => 1)', - Vect[SU2Irrep](0 => 2, 1 // 2 => 2), - Vect[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', -) -VfSU₂ = ( - Vect[FermionSpin](0 => 3, 1 // 2 => 1), - Vect[FermionSpin](0 => 2, 1 => 1), - Vect[FermionSpin](1 // 2 => 1, 1 => 1)', - Vect[FermionSpin](0 => 2, 1 // 2 => 2), - Vect[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', -) -VSU₂U₁ = ( - Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, -1) => 1), - Vect[SU2Irrep ⊠ U1Irrep]( - (0, 0) => 2, (0, 2) => 1, (1, 0) => 1, (1, -2) => 1, - (1 // 2, -1) => 1 - ), - Vect[SU2Irrep ⊠ U1Irrep]((1 // 2, 1) => 1, (1, -2) => 1)', - Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 2, (0, 2) => 1, (1 // 2, 1) => 1), - Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, 1) => 1)', -) -Vfib = ( - Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 2)', - Vect[FibonacciAnyon](:I => 3, :τ => 2)', - Vect[FibonacciAnyon](:I => 2, :τ => 3), - Vect[FibonacciAnyon](:I => 2, :τ => 2), -) - -if !is_buildkite - Ti = time() - @time include("fusiontrees.jl") - @time include("spaces.jl") - @time include("tensors.jl") - @time include("factorizations.jl") - @time include("diagonal.jl") - @time include("planar.jl") - if !(Sys.isapple() && get(ENV, "CI", "false") == "true") && isempty(VERSION.prerelease) - @time include("ad.jl") - end - @time include("bugfixes.jl") - Tf = time() - printstyled( - "Finished all tests in ", - string(round((Tf - Ti) / 60; sigdigits = 3)), - " minutes."; bold = true, color = Base.info_color() - ) - println() - @testset "Aqua" verbose = true begin - using Aqua - Aqua.test_all(TensorKit) - end -else - Ti = time() - #=using CUDA - if CUDA.functional() - end - using AMDGPU - if AMDGPU.functional() - end=# - Tf = time() - printstyled( - "Finished all GPU tests in ", - string(round((Tf - Ti) / 60; sigdigits = 3)), - " minutes."; bold = true, color = Base.info_color() - ) - println() -end diff --git a/test/setup.jl b/test/setup.jl new file mode 100644 index 000000000..93545c0b6 --- /dev/null +++ b/test/setup.jl @@ -0,0 +1,168 @@ +module TestSetup + +export smallset, randsector, hasfusiontensor, force_planar +export sectorlist +export Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁, Vfib + +using Test +using TestExtras +using Random +using TensorKit +using Combinatorics +using TensorKit: ProductSector, fusiontensor, pentagon_equation, hexagon_equation +using TensorKit: ℙ, PlanarTrivial +using TensorOperations +using Base.Iterators: take, product +using LinearAlgebra: LinearAlgebra + +Random.seed!(1234) + +smallset(::Type{I}) where {I <: Sector} = take(values(I), 5) +function smallset(::Type{ProductSector{Tuple{I1, I2}}}) where {I1, I2} + iter = product(smallset(I1), smallset(I2)) + s = collect(i ⊠ j for (i, j) in iter if dim(i) * dim(j) <= 6) + return length(s) > 6 ? rand(s, 6) : s +end +function smallset(::Type{ProductSector{Tuple{I1, I2, I3}}}) where {I1, I2, I3} + iter = product(smallset(I1), smallset(I2), smallset(I3)) + s = collect(i ⊠ j ⊠ k for (i, j, k) in iter if dim(i) * dim(j) * dim(k) <= 6) + return length(s) > 6 ? rand(s, 6) : s +end +function randsector(::Type{I}) where {I <: Sector} + s = collect(smallset(I)) + a = rand(s) + while a == one(a) # don't use trivial label + a = rand(s) + end + return a +end +function hasfusiontensor(I::Type{<:Sector}) + try + fusiontensor(one(I), one(I), one(I)) + return true + catch e + if e isa MethodError + return false + else + rethrow(e) + end + end +end + +""" + force_planar(obj) + +Replace an object with a planar equivalent -- i.e. one that disallows braiding. +""" +force_planar(V::ComplexSpace) = isdual(V) ? (ℙ^dim(V))' : ℙ^dim(V) +function force_planar(V::GradedSpace) + return GradedSpace((c ⊠ PlanarTrivial() => dim(V, c) for c in sectors(V))..., isdual(V)) +end +force_planar(V::ProductSpace) = mapreduce(force_planar, ⊗, V) +function force_planar(tsrc::TensorMap{<:Any, ComplexSpace}) + tdst = TensorMap{scalartype(tsrc)}( + undef, + force_planar(codomain(tsrc)) ← + force_planar(domain(tsrc)) + ) + copyto!(block(tdst, PlanarTrivial()), block(tsrc, Trivial())) + return tdst +end +function force_planar(tsrc::TensorMap{<:Any, <:GradedSpace}) + tdst = TensorMap{scalartype(tsrc)}( + undef, + force_planar(codomain(tsrc)) ← + force_planar(domain(tsrc)) + ) + for (c, b) in blocks(tsrc) + copyto!(block(tdst, c ⊠ PlanarTrivial()), b) + end + return tdst +end + +sectorlist = ( + Z2Irrep, Z3Irrep, Z4Irrep, Z3Irrep ⊠ Z4Irrep, + U1Irrep, CU1Irrep, SU2Irrep, + FermionParity, FermionParity ⊠ FermionParity, + FermionParity ⊠ U1Irrep ⊠ SU2Irrep, FermionParity ⊠ SU2Irrep ⊠ SU2Irrep, # Hubbard-like + FibonacciAnyon, IsingAnyon, + Z2Irrep ⊠ FibonacciAnyon ⊠ FibonacciAnyon, +) + +# spaces +Vtr = (ℂ^2, (ℂ^3)', ℂ^4, ℂ^3, (ℂ^2)') +Vℤ₂ = ( + Vect[Z2Irrep](0 => 1, 1 => 1), + Vect[Z2Irrep](0 => 1, 1 => 2)', + Vect[Z2Irrep](0 => 3, 1 => 2)', + Vect[Z2Irrep](0 => 2, 1 => 3), + Vect[Z2Irrep](0 => 2, 1 => 5), +) +Vfℤ₂ = ( + Vect[FermionParity](0 => 1, 1 => 1), + Vect[FermionParity](0 => 1, 1 => 2)', + Vect[FermionParity](0 => 2, 1 => 1)', + Vect[FermionParity](0 => 2, 1 => 3), + Vect[FermionParity](0 => 2, 1 => 5), +) +Vℤ₃ = ( + Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 1), + Vect[Z3Irrep](0 => 2, 1 => 1, 2 => 1), + Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 1)', + Vect[Z3Irrep](0 => 1, 1 => 2, 2 => 3), + Vect[Z3Irrep](0 => 1, 1 => 3, 2 => 3)', +) +VU₁ = ( + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 2), + Vect[U1Irrep](0 => 3, 1 => 1, -1 => 1), + Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', + Vect[U1Irrep](0 => 1, 1 => 2, -1 => 3), + Vect[U1Irrep](0 => 1, 1 => 3, -1 => 3)', +) +VfU₁ = ( + Vect[FermionNumber](0 => 1, 1 => 2, -1 => 2), + Vect[FermionNumber](0 => 3, 1 => 1, -1 => 1), + Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1)', + Vect[FermionNumber](0 => 1, 1 => 2, -1 => 3), + Vect[FermionNumber](0 => 1, 1 => 3, -1 => 3)', +) +VCU₁ = ( + Vect[CU1Irrep]((0, 0) => 1, (0, 1) => 2, 1 => 1), + Vect[CU1Irrep]((0, 0) => 3, (0, 1) => 0, 1 => 1), + Vect[CU1Irrep]((0, 0) => 1, (0, 1) => 0, 1 => 2)', + Vect[CU1Irrep]((0, 0) => 2, (0, 1) => 2, 1 => 1), + Vect[CU1Irrep]((0, 0) => 2, (0, 1) => 1, 1 => 2)', +) +VSU₂ = ( + Vect[SU2Irrep](0 => 3, 1 // 2 => 1), + Vect[SU2Irrep](0 => 2, 1 => 1), + Vect[SU2Irrep](1 // 2 => 1, 1 => 1)', + Vect[SU2Irrep](0 => 2, 1 // 2 => 2), + Vect[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', +) +VfSU₂ = ( + Vect[FermionSpin](0 => 3, 1 // 2 => 1), + Vect[FermionSpin](0 => 2, 1 => 1), + Vect[FermionSpin](1 // 2 => 1, 1 => 1)', + Vect[FermionSpin](0 => 2, 1 // 2 => 2), + Vect[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', +) +VSU₂U₁ = ( + Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, -1) => 1), + Vect[SU2Irrep ⊠ U1Irrep]( + (0, 0) => 2, (0, 2) => 1, (1, 0) => 1, (1, -2) => 1, + (1 // 2, -1) => 1 + ), + Vect[SU2Irrep ⊠ U1Irrep]((1 // 2, 1) => 1, (1, -2) => 1)', + Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 2, (0, 2) => 1, (1 // 2, 1) => 1), + Vect[SU2Irrep ⊠ U1Irrep]((0, 0) => 1, (1 // 2, 1) => 1)', +) +Vfib = ( + Vect[FibonacciAnyon](:I => 1, :τ => 1), + Vect[FibonacciAnyon](:I => 1, :τ => 2)', + Vect[FibonacciAnyon](:I => 3, :τ => 2)', + Vect[FibonacciAnyon](:I => 2, :τ => 3), + Vect[FibonacciAnyon](:I => 2, :τ => 2), +) + +end diff --git a/test/fusiontrees.jl b/test/symmetries/fusiontrees.jl similarity index 98% rename from test/fusiontrees.jl rename to test/symmetries/fusiontrees.jl index 75bb76e9d..fbeb1bb42 100644 --- a/test/fusiontrees.jl +++ b/test/symmetries/fusiontrees.jl @@ -1,10 +1,14 @@ -println("------------------------------------") -println("Fusion Trees") -println("------------------------------------") -ti = time() -@timedtestset "Fusion trees for $(TK.type_repr(I))" verbose = true for I in - sectorlist - Istr = TK.type_repr(I) +using Test, TestExtras +using TensorKit +import TensorKit as TK +using Random: randperm +using TensorOperations + +@isdefined(TestSetup) || include("../setup.jl") +using .TestSetup + +@timedtestset "Fusion trees for $(TensorKit.type_repr(I))" verbose = true for I in sectorlist + Istr = TensorKit.type_repr(I) N = 5 out = ntuple(n -> randsector(I), N) isdual = ntuple(n -> rand(Bool), N) @@ -592,10 +596,3 @@ ti = time() end TK.empty_globalcaches!() end -tf = time() -printstyled( - "Finished fusion tree tests in ", - string(round(tf - ti; sigdigits = 3)), - " seconds."; bold = true, color = Base.info_color() -) -println() diff --git a/test/symmetries/spaces.jl b/test/symmetries/spaces.jl new file mode 100644 index 000000000..f53d4024d --- /dev/null +++ b/test/symmetries/spaces.jl @@ -0,0 +1,440 @@ +using Test, TestExtras +using TensorKit + +@isdefined(TestSetup) || include("../setup.jl") +using .TestSetup + +@timedtestset "Fields" begin + @test isa(ℝ, Field) + @test isa(ℂ, Field) + @test eval(Meta.parse(sprint(show, ℝ))) == ℝ + @test eval(Meta.parse(sprint(show, ℂ))) == ℂ + @test ℝ ⊆ ℝ + @test ℝ ⊆ ℂ + @test ℂ ⊆ ℂ + @test !(ℂ ⊆ ℝ) + + for T in (Int8, Int16, Int32, Int64, BigInt) + @test one(T) ∈ ℝ + @test one(Rational{T}) ∈ ℝ + @test !(one(Complex{T}) ∈ ℝ) + @test !(one(Complex{Rational{T}}) ∈ ℝ) + @test one(T) ∈ ℂ + @test one(Rational{T}) ∈ ℂ + @test one(Complex{T}) ∈ ℂ + @test one(Complex{Rational{T}} ∈ ℂ) + + @test T ⊆ ℝ + @test Rational{T} ⊆ ℝ + @test !(Complex{T} ⊆ ℝ) + @test !(Complex{Rational{T}} ⊆ ℝ) + @test T ⊆ ℂ + @test Rational{T} ⊆ ℂ + @test Complex{T} ⊆ ℂ + @test Complex{Rational{T}} ⊆ ℂ + end + for T in (Float32, Float64, BigFloat) + @test one(T) ∈ ℝ + @test !(one(Complex{T}) ∈ ℝ) + @test one(T) ∈ ℂ + @test one(Complex{T} ∈ ℂ) + + @test T ⊆ ℝ + @test !(Complex{T} ⊆ ℝ) + @test T ⊆ ℂ + @test Complex{T} ⊆ ℂ + end +end + +@timedtestset "ElementarySpace: CartesianSpace" begin + d = 2 + V = ℝ^d + @test eval(Meta.parse(sprint(show, V))) == V + @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test isa(V, VectorSpace) + @test isa(V, ElementarySpace) + @test isa(InnerProductStyle(V), HasInnerProduct) + @test isa(InnerProductStyle(V), EuclideanInnerProduct) + @test isa(V, CartesianSpace) + @test !isdual(V) + @test !isdual(V') + @test V == CartesianSpace(Trivial() => d) == CartesianSpace(Dict(Trivial() => d)) + @test @constinferred(hash(V)) == hash(deepcopy(V)) + @test V == @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) + @test field(V) == ℝ + @test @constinferred(sectortype(V)) == Trivial + @test ((@constinferred sectors(V))...,) == (Trivial(),) + @test length(sectors(V)) == 1 + @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) + @test dim(@constinferred(zerospace(V))) == 0 + @test (sectors(zerospace(V))...,) == () + @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test ℝ^d == ℝ[](d) == CartesianSpace(d) == typeof(V)(d) + W = @constinferred ℝ^1 + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) + @test @constinferred(zerospace(V)) == ℝ^0 == zerospace(typeof(V)) + @test @constinferred(⊕(V, zerospace(V))) == V + @test @constinferred(⊕(V, V)) == ℝ^(2d) + @test @constinferred(⊕(V, unitspace(V))) == ℝ^(d + 1) + @test @constinferred(⊕(V, V, V, V)) == ℝ^(4d) + @test @constinferred(fuse(V, V)) == ℝ^(d^2) + @test @constinferred(fuse(V, V', V, V')) == ℝ^(d^4) + @test @constinferred(flip(V)) == V' + @test flip(V) ≅ V + @test flip(V) ≾ V + @test flip(V) ≿ V + @test V ≺ ⊕(V, V) + @test !(V ≻ ⊕(V, V)) + @test @constinferred(infimum(V, ℝ^3)) == V + @test @constinferred(supremum(V', ℝ^3)) == ℝ^3 +end + +@timedtestset "ElementarySpace: ComplexSpace" begin + d = 2 + V = ℂ^d + @test eval(Meta.parse(sprint(show, V))) == V + @test eval(Meta.parse(sprint(show, V'))) == V' + @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test isa(V, VectorSpace) + @test isa(V, ElementarySpace) + @test isa(InnerProductStyle(V), HasInnerProduct) + @test isa(InnerProductStyle(V), EuclideanInnerProduct) + @test isa(V, ComplexSpace) + @test !isdual(V) + @test isdual(V') + @test V == ComplexSpace(Trivial() => d) == ComplexSpace(Dict(Trivial() => d)) + @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') + @test @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) != V + @test @constinferred(field(V)) == ℂ + @test @constinferred(sectortype(V)) == Trivial + @test @constinferred(sectortype(V)) == Trivial + @test ((@constinferred sectors(V))...,) == (Trivial(),) + @test length(sectors(V)) == 1 + @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) + @test dim(@constinferred(zerospace(V))) == 0 + @test (sectors(zerospace(V))...,) == () + @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test ℂ^d == Vect[Trivial](d) == Vect[](Trivial() => d) == ℂ[](d) == typeof(V)(d) + W = @constinferred ℂ^1 + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) + @test @constinferred(zerospace(V)) == ℂ^0 == zerospace(typeof(V)) + @test @constinferred(⊕(V, zerospace(V))) == V + @test @constinferred(⊕(V, V)) == ℂ^(2d) + @test_throws SpaceMismatch (⊕(V, V')) + # promote_except = ErrorException("promotion of types $(typeof(ℝ^d)) and " * + # "$(typeof(ℂ^d)) failed to change any arguments") + # @test_throws promote_except (⊕(ℝ^d, ℂ^d)) + @test_throws ErrorException (⊗(ℝ^d, ℂ^d)) + @test @constinferred(⊕(V, V)) == ℂ^(2d) + @test @constinferred(⊕(V, unitspace(V))) == ℂ^(d + 1) + @test @constinferred(⊕(V, V, V, V)) == ℂ^(4d) + @test @constinferred(fuse(V, V)) == ℂ^(d^2) + @test @constinferred(fuse(V, V', V, V')) == ℂ^(d^4) + @test @constinferred(flip(V)) == V' + @test flip(V) ≅ V + @test flip(V) ≾ V + @test flip(V) ≿ V + @test V ≺ ⊕(V, V) + @test !(V ≻ ⊕(V, V)) + @test @constinferred(infimum(V, ℂ^3)) == V + @test @constinferred(supremum(V', (ℂ^3)')) == dual(ℂ^3) == conj(ℂ^3) +end + +@timedtestset "ElementarySpace: GeneralSpace" begin + d = 2 + V = GeneralSpace{ℂ}(d) + @test eval(Meta.parse(sprint(show, V))) == V + @test eval(Meta.parse(sprint(show, dual(V)))) == dual(V) + @test eval(Meta.parse(sprint(show, conj(V)))) == conj(V) + @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test !isdual(V) + @test isdual(V') + @test !isdual(conj(V)) + @test isdual(conj(V')) + @test !TensorKit.isconj(V) + @test !TensorKit.isconj(V') + @test TensorKit.isconj(conj(V)) + @test TensorKit.isconj(conj(V')) + @test isa(V, VectorSpace) + @test isa(V, ElementarySpace) + @test !isa(InnerProductStyle(V), HasInnerProduct) + @test !isa(InnerProductStyle(V), EuclideanInnerProduct) + @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') + @test @constinferred(dual(V)) != @constinferred(conj(V)) != V + @test @constinferred(field(V)) == ℂ + @test @constinferred(sectortype(V)) == Trivial + @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) + @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) +end + +@timedtestset "ElementarySpace: $(TensorKit.type_repr(Vect[I]))" for I in sectorlist + if Base.IteratorSize(values(I)) === Base.IsInfinite() + set = unique(vcat(unit(I), [randsector(I) for k in 1:10])) + gen = (c => 2 for c in set) + else + gen = (values(I)[k] => (k + 1) for k in 1:length(values(I))) + end + V = GradedSpace(gen) + @test eval(Meta.parse(TensorKit.type_repr(typeof(V)))) == typeof(V) + @test eval(Meta.parse(sprint(show, V))) == V + @test eval(Meta.parse(sprint(show, V'))) == V' + @test V' == GradedSpace(gen; dual = true) + @test V == @constinferred GradedSpace(gen...) + @test V' == @constinferred GradedSpace(gen...; dual = true) + @test V == @constinferred GradedSpace(tuple(gen...)) + @test V' == @constinferred GradedSpace(tuple(gen...); dual = true) + @test V == @constinferred GradedSpace(Dict(gen)) + @test V' == @constinferred GradedSpace(Dict(gen); dual = true) + @test V == @inferred Vect[I](gen) + @test V' == @constinferred Vect[I](gen; dual = true) + @test V == @constinferred Vect[I](gen...) + @test V' == @constinferred Vect[I](gen...; dual = true) + @test V == @constinferred Vect[I](Dict(gen)) + @test V' == @constinferred Vect[I](Dict(gen); dual = true) + @test V == @constinferred typeof(V)(c => dim(V, c) for c in sectors(V)) + if I isa ZNIrrep + @test V == @constinferred typeof(V)(V.dims) + @test V' == @constinferred typeof(V)(V.dims; dual = true) + end + @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') + @test V == GradedSpace(reverse(collect(gen))...) + @test eval(Meta.parse(sprint(show, V))) == V + @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + # space with no sectors + @test dim(@constinferred(zerospace(V))) == 0 + # space with a single sector + W = @constinferred GradedSpace(unit(I) => 1) + @test W == GradedSpace(unit(I) => 1, randsector(I) => 0) + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) + @test @constinferred(zerospace(V)) == GradedSpace(unit(I) => 0) + # randsector never returns trivial sector, so this cannot error + @test_throws ArgumentError GradedSpace(unit(I) => 1, randsector(I) => 0, unit(I) => 3) + @test eval(Meta.parse(sprint(show, W))) == W + @test isa(V, VectorSpace) + @test isa(V, ElementarySpace) + @test isa(InnerProductStyle(V), HasInnerProduct) + @test isa(InnerProductStyle(V), EuclideanInnerProduct) + @test isa(V, GradedSpace) + @test isa(V, GradedSpace{I}) + @test @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) != V + @test @constinferred(field(V)) == ℂ + @test @constinferred(sectortype(V)) == I + slist = @constinferred sectors(V) + @test @constinferred(TensorKit.hassector(V, first(slist))) + @test @constinferred(dim(V)) == sum(dim(s) * dim(V, s) for s in slist) + @test @constinferred(reduceddim(V)) == sum(dim(V, s) for s in slist) + @constinferred dim(V, first(slist)) + if hasfusiontensor(I) + @test @constinferred(TensorKit.axes(V)) == Base.OneTo(dim(V)) + end + @test @constinferred(⊕(V, zerospace(V))) == V + @test @constinferred(⊕(V, V)) == Vect[I](c => 2dim(V, c) for c in sectors(V)) + @test @constinferred(⊕(V, V, V, V)) == Vect[I](c => 4dim(V, c) for c in sectors(V)) + @test @constinferred(⊕(V, unitspace(V))) == Vect[I](c => isunit(c) + dim(V, c) for c in sectors(V)) + @test @constinferred(fuse(V, unitspace(V))) == V + d = Dict{I, Int}() + for a in sectors(V), b in sectors(V) + for c in a ⊗ b + d[c] = get(d, c, 0) + dim(V, a) * dim(V, b) * Nsymbol(a, b, c) + end + end + @test @constinferred(fuse(V, V)) == GradedSpace(d) + @test @constinferred(flip(V)) == Vect[I](conj(c) => dim(V, c) for c in sectors(V))' + @test flip(V) ≅ V + @test flip(V) ≾ V + @test flip(V) ≿ V + @test @constinferred(⊕(V, V)) == @constinferred supremum(V, ⊕(V, V)) + @test V == @constinferred infimum(V, ⊕(V, V)) + @test V ≺ ⊕(V, V) + @test !(V ≻ ⊕(V, V)) + @test infimum(V, GradedSpace(unit(I) => 3)) == GradedSpace(unit(I) => 2) + @test_throws SpaceMismatch (⊕(V, V')) +end + +@timedtestset "ProductSpace{ℂ}" begin + V1, V2, V3, V4 = ℂ^1, ℂ^2, ℂ^3, ℂ^4 + P = @constinferred ProductSpace(V1, V2, V3, V4) + @test eval(Meta.parse(sprint(show, P))) == P + @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) + @test isa(P, VectorSpace) + @test isa(P, CompositeSpace) + @test spacetype(P) == ComplexSpace + @test sectortype(P) == Trivial + @test @constinferred(hash(P)) == hash(deepcopy(P)) != hash(P') + @test P == deepcopy(P) + @test P == typeof(P)(P...) + @constinferred (x -> tuple(x...))(P) + @test @constinferred(dual(P)) == P' + @test @constinferred(field(P)) == ℂ + @test @constinferred(*(V1, V2, V3, V4)) == P + @test @constinferred(⊗(V1, V2, V3, V4)) == P + @test @constinferred(⊗(V1, V2 ⊗ V3 ⊗ V4)) == P + @test @constinferred(⊗(V1 ⊗ V2, V3 ⊗ V4)) == P + @test @constinferred(⊗(V1, V2, V3 ⊗ V4)) == P + @test @constinferred(⊗(V1, V2 ⊗ V3, V4)) == P + @test V1 * V2 * unitspace(V1) * V3 * V4 == + @constinferred(insertleftunit(P, 3)) == + @constinferred(insertrightunit(P, 2)) + @test @constinferred(removeunit(V1 * V2 * unitspace(V1)' * V3 * V4, 3)) == P + @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 + @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 + @test fuse(V1, V2', V3) ≿ V1 ⊗ V2' ⊗ V3 + @test fuse(flip(V1), V2, flip(V3)) ≅ V1 ⊗ V2 ⊗ V3 + @test @constinferred(⊗(P)) == P + @test @constinferred(⊗(V1)) == ProductSpace(V1) + @test eval(Meta.parse(sprint(show, ⊗(V1)))) == ⊗(V1) + @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == + @constinferred(one(P)) == @constinferred(one(typeof(P))) == + ProductSpace{ComplexSpace}(()) + @test eval(Meta.parse(sprint(show, one(P)))) == one(P) + @test @constinferred(⊗(one(P), P)) == P + @test @constinferred(⊗(P, one(P))) == P + @test @constinferred(⊗(one(P), one(P))) == one(P) + @test @constinferred(adjoint(P)) == dual(P) == V4' ⊗ V3' ⊗ V2' ⊗ V1' + @test @constinferred(dims(P)) == map(dim, (V1, V2, V3, V4)) + @test @constinferred(dim(P)) == prod(dim, (V1, V2, V3, V4)) + @test @constinferred(dim(P, 2)) == dim(V2) + @test @constinferred(dim(one(P))) == 1 + @test first(@constinferred(sectors(P))) == (Trivial(), Trivial(), Trivial(), Trivial()) + @test first(@constinferred(sectors(one(P)))) == () + cube(x) = x^3 + @test @constinferred(cube(V1)) == V1 ⊗ V1 ⊗ V1 + N = 3 + @test V1^N == V1 ⊗ V1 ⊗ V1 + @test P^2 == P ⊗ P + @test @constinferred(dims(P, first(sectors(P)))) == dims(P) + @test ((@constinferred blocksectors(P))...,) == (Trivial(),) + @test isempty(blocksectors(P ⊗ ℂ^0)) + @test isempty(@constinferred(sectors(P ⊗ ℂ^0))) + @test @constinferred(blockdim(P, first(blocksectors(P)))) == dim(P) + @test @constinferred(blockdim(P, Trivial())) == dim(P) + @test @constinferred(blockdim(one(P), Trivial())) == 1 + @test Base.IteratorEltype(P) == Base.IteratorEltype(typeof(P)) == Base.IteratorEltype(P.spaces) + @test Base.IteratorSize(P) == Base.IteratorSize(typeof(P)) == Base.IteratorSize(P.spaces) + @test Base.eltype(P) == Base.eltype(typeof(P)) == typeof(V1) + @test eltype(collect(P)) == typeof(V1) + @test collect(P) == [V1, V2, V3, V4] + @test_throws MethodError P ⊕ P +end + +@timedtestset "ProductSpace{SU₂Space}" begin + V1, V2, V3 = SU₂Space(0 => 3, 1 // 2 => 1), SU₂Space(0 => 2, 1 => 1), + SU₂Space(1 // 2 => 1, 1 => 1)' + P = @constinferred ProductSpace(V1, V2, V3) + @test eval(Meta.parse(sprint(show, P))) == P + @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) + @test isa(P, VectorSpace) + @test isa(P, CompositeSpace) + @test spacetype(P) == SU₂Space + @test sectortype(P) == Irrep[SU₂] == SU2Irrep + @test @constinferred(hash(P)) == hash(deepcopy(P)) != hash(P') + @test @constinferred(dual(P)) == P' + @test @constinferred(field(P)) == ℂ + @test @constinferred(*(V1, V2, V3)) == P + @test @constinferred(⊗(V1, V2, V3)) == P + @test @constinferred(adjoint(P)) == dual(P) == V3' ⊗ V2' ⊗ V1' + @test V1 * V2 * unitspace(V1)' * V3 == + @constinferred(insertleftunit(P, 3; conj = true)) == + @constinferred(insertrightunit(P, 2; conj = true)) + @test P == @constinferred(removeunit(insertleftunit(P, 3), 3)) + @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 + @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 ≾ fuse(V1 ⊗ V2' ⊗ V3) + @test fuse(V1, V2') ⊗ V3 ≾ V1 ⊗ V2' ⊗ V3 + @test fuse(V1, V2', V3) ≿ V1 ⊗ V2' ⊗ V3 ≿ fuse(V1 ⊗ V2' ⊗ V3) + @test V1 ⊗ fuse(V2', V3) ≿ V1 ⊗ V2' ⊗ V3 + @test fuse(flip(V1) ⊗ V2) ⊗ flip(V3) ≅ V1 ⊗ V2 ⊗ V3 + @test @constinferred(⊗(V1)) == ProductSpace(V1) + @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == + @constinferred(one(P)) == @constinferred(one(typeof(P))) + @test @constinferred(dims(P)) == map(dim, (V1, V2, V3)) + @test @constinferred(dim(P)) == prod(dim, (V1, V2, V3)) + @test @constinferred(dim(one(P))) == 1 + @test first(@constinferred(sectors(one(P)))) == () + @test @constinferred(blockdim(one(P), Irrep[SU₂](0))) == 1 + for s in @constinferred(sectors(P)) + @test hassector(P, s) + @test @constinferred(dims(P, s)) == dim.((V1, V2, V3), s) + end + @test sum(dim(c) * blockdim(P, c) for c in @constinferred(blocksectors(P))) == dim(P) +end + +@timedtestset "Deligne tensor product of spaces" begin + V1 = SU₂Space(0 => 3, 1 // 2 => 1) + V2 = SU₂Space(0 => 2, 1 => 1)' + V3 = ℤ₃Space(0 => 3, 1 => 2, 2 => 1) + V4 = ℂ^3 + + for W1 in (V1, V2, V3, V4) + for W2 in (V1, V2, V3, V4) + for W3 in (V1, V2, V3, V4) + for W4 in (V1, V2, V3, V4) + Ws = @constinferred(W1 ⊠ W2 ⊠ W3 ⊠ W4) + @test Ws == @constinferred((W1 ⊠ W2) ⊠ (W3 ⊠ W4)) == + @constinferred(((W1 ⊠ W2) ⊠ W3) ⊠ W4) == + @constinferred((W1 ⊠ (W2 ⊠ W3)) ⊠ W4) == + @constinferred(W1 ⊠ ((W2 ⊠ W3)) ⊠ W4) == + @constinferred(W1 ⊠ (W2 ⊠ (W3 ⊠ W4))) + I1, I2, I3, I4 = map(sectortype, (W1, W2, W3, W4)) + I = sectortype(Ws) + @test I == @constinferred((I1 ⊠ I2) ⊠ (I3 ⊠ I4)) == + @constinferred(((I1 ⊠ I2) ⊠ I3) ⊠ I4) == + @constinferred((I1 ⊠ (I2 ⊠ I3)) ⊠ I4) == + @constinferred(I1 ⊠ ((I2 ⊠ I3)) ⊠ I4) == + @constinferred(I1 ⊠ (I2 ⊠ (I3 ⊠ I4))) + @test dim(Ws) == dim(W1) * dim(W2) * dim(W3) * dim(W4) + end + end + end + end + @test sectortype(@constinferred((V1 ⊗ V2) ⊠ V3)) == @constinferred(Irrep[SU₂ × ℤ₃]) + @test dim((V1 ⊗ V2) ⊠ V3) == dim(V1) * dim(V2) * dim(V3) + @test sectortype((V1 ⊗ V2) ⊠ V3 ⊠ V4) == Irrep[SU₂ × ℤ₃] + @test dim((V1 ⊗ V2) ⊠ V3 ⊠ V4) == dim(V1) * dim(V2) * dim(V3) * dim(V4) + @test fuse(V2 ⊠ V4) == fuse(V4 ⊠ V2) == SU₂Space(0 => 6, 1 => 3) + @test fuse(V3 ⊠ V4) == fuse(V4 ⊠ V3) == ℤ₃Space(0 => 9, 1 => 6, 2 => 3) +end + +@timedtestset "HomSpace" begin + for (V1, V2, V3, V4, V5) in (Vtr, Vℤ₃, VSU₂) + W = TensorKit.HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) + @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) + @test W == (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) + @test W' == (V1 ⊗ V2 → V3 ⊗ V4 ⊗ V5) + @test eval(Meta.parse(sprint(show, W))) == W + @test eval(Meta.parse(sprint(show, typeof(W)))) == typeof(W) + @test spacetype(W) == typeof(V1) + @test sectortype(W) == sectortype(V1) + @test W[1] == V1 + @test W[2] == V2 + @test W[3] == V3' + @test W[4] == V4' + @test W[5] == V5' + @test @constinferred(hash(W)) == hash(deepcopy(W)) != hash(W') + @test W == deepcopy(W) + @test W == @constinferred permute(W, ((1, 2), (3, 4, 5))) + @test permute(W, ((2, 4, 5), (3, 1))) == (V2 ⊗ V4' ⊗ V5' ← V3 ⊗ V1') + @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TensorKit.compose(W, W') + @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)) == + @constinferred(insertleftunit(W)) == + @constinferred(insertrightunit(W)) + @test @constinferred(removeunit(insertleftunit(W), $(numind(W) + 1))) == W + @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)') == + @constinferred(insertleftunit(W; conj = true)) == + @constinferred(insertrightunit(W; conj = true)) + @test (unitspace(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == + @constinferred(insertleftunit(W, 1)) == + @constinferred(insertrightunit(W, 0)) + @test (V1 ⊗ V2 ⊗ unitspace(V1) ← V3 ⊗ V4 ⊗ V5) == + @constinferred(insertrightunit(W, 2)) + @test (V1 ⊗ V2 ← unitspace(V1) ⊗ V3 ⊗ V4 ⊗ V5) == + @constinferred(insertleftunit(W, 3)) + @test @constinferred(removeunit(insertleftunit(W, 3), 3)) == W + @test @constinferred(insertrightunit(one(V1) ← V1, 0)) == (unitspace(V1) ← V1) + @test_throws BoundsError insertleftunit(one(V1) ← V1, 0) + end +end + +TensorKit.empty_globalcaches!() diff --git a/test/diagonal.jl b/test/tensors/diagonal.jl similarity index 98% rename from test/diagonal.jl rename to test/tensors/diagonal.jl index 5a358236e..ad174bf4e 100644 --- a/test/diagonal.jl +++ b/test/tensors/diagonal.jl @@ -1,7 +1,12 @@ +using Test, TestExtras +using TensorKit + diagspacelist = ( - (ℂ^4)', Vect[Z2Irrep](0 => 2, 1 => 3), + (ℂ^4)', + Vect[Z2Irrep](0 => 2, 1 => 3), Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1), - Vect[SU2Irrep](0 => 2, 1 => 1)', Vect[FibonacciAnyon](:I => 2, :τ => 2), + Vect[SU2Irrep](0 => 2, 1 => 1)', + Vect[FibonacciAnyon](:I => 2, :τ => 2), ) @testset "DiagonalTensor with domain $V" for V in diagspacelist diff --git a/test/factorizations.jl b/test/tensors/factorizations.jl similarity index 98% rename from test/factorizations.jl rename to test/tensors/factorizations.jl index 294cbdf60..c5b4e8748 100644 --- a/test/factorizations.jl +++ b/test/tensors/factorizations.jl @@ -1,3 +1,10 @@ +using Test, TestExtras +using TensorKit +using LinearAlgebra: LinearAlgebra + +@isdefined(TestSetup) || include("../setup.jl") +using .TestSetup + spacelist = try if ENV["CI"] == "true" println("Detected running on CI") diff --git a/test/planar.jl b/test/tensors/planar.jl similarity index 83% rename from test/planar.jl rename to test/tensors/planar.jl index 470f69aed..d5e8bb569 100644 --- a/test/planar.jl +++ b/test/tensors/planar.jl @@ -1,74 +1,12 @@ -using TensorKit, TensorOperations, Test -using TensorKit: BraidingTensor +using Test, TestExtras +using TensorKit using TensorKit: planaradd!, planartrace!, planarcontract! using TensorKit: PlanarTrivial, ℙ +using TensorOperations -""" - force_planar(obj) +@isdefined(TestSetup) || include("../setup.jl") +using .TestSetup -Replace an object with a planar equivalent -- i.e. one that disallows braiding. -""" -force_planar(V::ComplexSpace) = isdual(V) ? (ℙ^dim(V))' : ℙ^dim(V) -function force_planar(V::GradedSpace) - return GradedSpace((c ⊠ PlanarTrivial() => dim(V, c) for c in sectors(V))..., isdual(V)) -end -force_planar(V::ProductSpace) = mapreduce(force_planar, ⊗, V) -function force_planar(tsrc::TensorMap{<:Any, ComplexSpace}) - tdst = TensorMap{scalartype(tsrc)}( - undef, - force_planar(codomain(tsrc)) ← - force_planar(domain(tsrc)) - ) - copyto!(block(tdst, PlanarTrivial()), block(tsrc, Trivial())) - return tdst -end -function force_planar(tsrc::TensorMap{<:Any, <:GradedSpace}) - tdst = TensorMap{scalartype(tsrc)}( - undef, - force_planar(codomain(tsrc)) ← - force_planar(domain(tsrc)) - ) - for (c, b) in blocks(tsrc) - copyto!(block(tdst, c ⊠ PlanarTrivial()), b) - end - return tdst -end - -Vtr = ( - ℂ^3, - (ℂ^2)', - ℂ^5, - ℂ^6, - (ℂ^7)', -) -VU₁ = ( - Vect[U1Irrep](0 => 1, 1 => 2, -1 => 2), - Vect[U1Irrep](0 => 3, 1 => 1, -1 => 1), - Vect[U1Irrep](0 => 2, 1 => 2, -1 => 1)', - Vect[U1Irrep](0 => 1, 1 => 2, -1 => 3), - Vect[U1Irrep](0 => 1, 1 => 3, -1 => 3)', -) -VfU₁ = ( - Vect[FermionNumber](0 => 1, 1 => 2, -1 => 2), - Vect[FermionNumber](0 => 3, 1 => 1, -1 => 1), - Vect[FermionNumber](0 => 2, 1 => 2, -1 => 1)', - Vect[FermionNumber](0 => 1, 1 => 2, -1 => 3), - Vect[FermionNumber](0 => 1, 1 => 3, -1 => 3)', -) -VfSU₂ = ( - Vect[FermionSpin](0 => 3, 1 // 2 => 1), - Vect[FermionSpin](0 => 2, 1 => 1), - Vect[FermionSpin](1 // 2 => 1, 1 => 1)', - Vect[FermionSpin](0 => 2, 1 // 2 => 2), - Vect[FermionSpin](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', -) -Vfib = ( - Vect[FibonacciAnyon](:I => 1, :τ => 2), - Vect[FibonacciAnyon](:I => 2, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 1), - Vect[FibonacciAnyon](:I => 1, :τ => 1), -) @testset "Braiding tensor" begin for V in (Vtr, VU₁, VfU₁, VfSU₂, Vfib) W = V[1] ⊗ V[2] ← V[2] ⊗ V[1] diff --git a/test/tensors.jl b/test/tensors/tensors.jl similarity index 99% rename from test/tensors.jl rename to test/tensors/tensors.jl index c0c1d90b2..103c53f8d 100644 --- a/test/tensors.jl +++ b/test/tensors/tensors.jl @@ -1,5 +1,13 @@ +using Test, TestExtras +using TensorKit +using Combinatorics: permutations +using LinearAlgebra: LinearAlgebra + +@isdefined(TestSetup) || include("../setup.jl") +using .TestSetup + spacelist = try - if ENV["CI"] == "true" + if get(ENV, "CI", "false") == "true" println("Detected running on CI") if Sys.iswindows() (Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂) From b25d9b4da723ad1937d6db410d7339fd52babdbc Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 17:29:10 -0400 Subject: [PATCH 05/16] update action --- .github/workflows/CI.yml | 71 +++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a402fbac1..434f33ed4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,68 +1,71 @@ name: CI + on: push: branches: - - 'master' - 'main' - 'release-' + tags: '*' + + paths-ignore: + - 'docs/**' pull_request: workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + # Cancel intermediate builds: only if it is a pull request build. + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: version: - - '1' - 'lts' + - '1' + group: + - symmetries + - tensors + - other + - autodiff os: - ubuntu-latest - macOS-latest - windows-latest - steps: - - uses: actions/checkout@v5 - - uses: julia-actions/setup-julia@v2 - with: - version: ${{ matrix.version }} - - uses: julia-actions/cache@v2 - - uses: julia-actions/julia-buildpkg@latest - - uses: julia-actions/julia-runtest@latest - env: - JULIA_NUM_THREADS: 4 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - with: - files: lcov.info + uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/Tests.yml@main" + with: + group: "${{ matrix.group }}" + julia-version: "${{ matrix.version }}" + os: "${{ matrix.os }}" + nthreads: 4 + timeout-minutes: 120 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + test-nightly: needs: test - name: Julia nightly - ${{ matrix.os }} - ${{ matrix.arch }} - runs-on: ${{ matrix.os }} strategy: matrix: version: - 'nightly' + group: + - symmetries + - tensors + - other + - autodiff os: - ubuntu-latest - macOS-latest - windows-latest - continue-on-error: true - steps: - - uses: actions/checkout@v5 - - uses: julia-actions/setup-julia@v2 - with: - version: ${{ matrix.version }} - - uses: julia-actions/cache@v2 - - uses: julia-actions/julia-buildpkg@latest - - uses: julia-actions/julia-runtest@latest - env: - JULIA_NUM_THREADS: 4 + uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/Tests.yml@main" + with: + group: "${{ matrix.group }}" + julia-version: "${{ matrix.version }}" + os: "${{ matrix.os }}" + nthreads: 4 + timeout-minutes: 120 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 0893e9991c606be292111025ce932300b02874d0 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 18:07:50 -0400 Subject: [PATCH 06/16] properly namespace the `show` tests --- test/symmetries/fusiontrees.jl | 2 +- test/symmetries/spaces.jl | 50 ++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/test/symmetries/fusiontrees.jl b/test/symmetries/fusiontrees.jl index fbeb1bb42..6105498db 100644 --- a/test/symmetries/fusiontrees.jl +++ b/test/symmetries/fusiontrees.jl @@ -27,7 +27,7 @@ using .TestSetup @constinferred Nothing iterate(it, s) @test f == @constinferred first(it) @testset "Fusion tree $Istr: printing" begin - @test eval(Meta.parse(sprint(show, f))) == f + @test eval(Meta.parse(sprint(show, f; context = (:module => @__MODULE__)))) == f end @testset "Fusion tree $Istr: constructor properties" begin u = unit(I) diff --git a/test/symmetries/spaces.jl b/test/symmetries/spaces.jl index f53d4024d..95b27e2bf 100644 --- a/test/symmetries/spaces.jl +++ b/test/symmetries/spaces.jl @@ -4,11 +4,13 @@ using TensorKit @isdefined(TestSetup) || include("../setup.jl") using .TestSetup +parse_show(x) = Meta.parse(sprint(show, x; context = (:module => @__MODULE__))) + @timedtestset "Fields" begin @test isa(ℝ, Field) @test isa(ℂ, Field) - @test eval(Meta.parse(sprint(show, ℝ))) == ℝ - @test eval(Meta.parse(sprint(show, ℂ))) == ℂ + @test eval(parse_show(ℝ)) == ℝ + @test eval(parse_show(ℂ)) == ℂ @test ℝ ⊆ ℝ @test ℝ ⊆ ℂ @test ℂ ⊆ ℂ @@ -49,8 +51,8 @@ end @timedtestset "ElementarySpace: CartesianSpace" begin d = 2 V = ℝ^d - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test eval(parse_show(V)) == V + @test eval(parse_show(typeof(V))) == typeof(V) @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test isa(InnerProductStyle(V), HasInnerProduct) @@ -93,9 +95,9 @@ end @timedtestset "ElementarySpace: ComplexSpace" begin d = 2 V = ℂ^d - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, V'))) == V' - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test eval(parse_show(V)) == V + @test eval(parse_show(V')) == V' + @test eval(parse_show(typeof(V))) == typeof(V) @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test isa(InnerProductStyle(V), HasInnerProduct) @@ -145,10 +147,10 @@ end @timedtestset "ElementarySpace: GeneralSpace" begin d = 2 V = GeneralSpace{ℂ}(d) - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, dual(V)))) == dual(V) - @test eval(Meta.parse(sprint(show, conj(V)))) == conj(V) - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test eval(parse_show(V)) == V + @test eval(parse_show(dual(V))) == dual(V) + @test eval(parse_show(conj(V))) == conj(V) + @test eval(parse_show(typeof(V))) == typeof(V) @test !isdual(V) @test isdual(V') @test !isdual(conj(V)) @@ -179,8 +181,8 @@ end end V = GradedSpace(gen) @test eval(Meta.parse(TensorKit.type_repr(typeof(V)))) == typeof(V) - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, V'))) == V' + @test eval(parse_show(V)) == V + @test eval(parse_show(V')) == V' @test V' == GradedSpace(gen; dual = true) @test V == @constinferred GradedSpace(gen...) @test V' == @constinferred GradedSpace(gen...; dual = true) @@ -201,8 +203,8 @@ end end @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') @test V == GradedSpace(reverse(collect(gen))...) - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) + @test eval(parse_show(V)) == V + @test eval(parse_show(typeof(V))) == typeof(V) # space with no sectors @test dim(@constinferred(zerospace(V))) == 0 # space with a single sector @@ -212,7 +214,7 @@ end @test @constinferred(zerospace(V)) == GradedSpace(unit(I) => 0) # randsector never returns trivial sector, so this cannot error @test_throws ArgumentError GradedSpace(unit(I) => 1, randsector(I) => 0, unit(I) => 3) - @test eval(Meta.parse(sprint(show, W))) == W + @test eval(parse_show(W)) == W @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test isa(InnerProductStyle(V), HasInnerProduct) @@ -257,8 +259,8 @@ end @timedtestset "ProductSpace{ℂ}" begin V1, V2, V3, V4 = ℂ^1, ℂ^2, ℂ^3, ℂ^4 P = @constinferred ProductSpace(V1, V2, V3, V4) - @test eval(Meta.parse(sprint(show, P))) == P - @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) + @test eval(parse_show(P)) == P + @test eval(parse_show(typeof(P))) == typeof(P) @test isa(P, VectorSpace) @test isa(P, CompositeSpace) @test spacetype(P) == ComplexSpace @@ -285,11 +287,11 @@ end @test fuse(flip(V1), V2, flip(V3)) ≅ V1 ⊗ V2 ⊗ V3 @test @constinferred(⊗(P)) == P @test @constinferred(⊗(V1)) == ProductSpace(V1) - @test eval(Meta.parse(sprint(show, ⊗(V1)))) == ⊗(V1) + @test eval(parse_show(⊗(V1))) == ⊗(V1) @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == @constinferred(one(P)) == @constinferred(one(typeof(P))) == ProductSpace{ComplexSpace}(()) - @test eval(Meta.parse(sprint(show, one(P)))) == one(P) + @test eval(parse_show(one(P))) == one(P) @test @constinferred(⊗(one(P), P)) == P @test @constinferred(⊗(P, one(P))) == P @test @constinferred(⊗(one(P), one(P))) == one(P) @@ -324,8 +326,8 @@ end V1, V2, V3 = SU₂Space(0 => 3, 1 // 2 => 1), SU₂Space(0 => 2, 1 => 1), SU₂Space(1 // 2 => 1, 1 => 1)' P = @constinferred ProductSpace(V1, V2, V3) - @test eval(Meta.parse(sprint(show, P))) == P - @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) + @test eval(parse_show(P)) == P + @test eval(parse_show(typeof(P))) == typeof(P) @test isa(P, VectorSpace) @test isa(P, CompositeSpace) @test spacetype(P) == SU₂Space @@ -403,8 +405,8 @@ end @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) @test W == (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) @test W' == (V1 ⊗ V2 → V3 ⊗ V4 ⊗ V5) - @test eval(Meta.parse(sprint(show, W))) == W - @test eval(Meta.parse(sprint(show, typeof(W)))) == typeof(W) + @test eval(parse_show(W)) == W + @test eval(parse_show(typeof(W))) == typeof(W) @test spacetype(W) == typeof(V1) @test sectortype(W) == sectortype(V1) @test W[1] == V1 From 1a706b166bf4e184a1bf47ef9fc80ca82712bda9 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Wed, 8 Oct 2025 18:27:43 -0400 Subject: [PATCH 07/16] fix missing type_repr implementations --- test/symmetries/fusiontrees.jl | 3 +++ test/symmetries/spaces.jl | 3 +++ 2 files changed, 6 insertions(+) diff --git a/test/symmetries/fusiontrees.jl b/test/symmetries/fusiontrees.jl index 6105498db..c001f4e26 100644 --- a/test/symmetries/fusiontrees.jl +++ b/test/symmetries/fusiontrees.jl @@ -4,6 +4,9 @@ import TensorKit as TK using Random: randperm using TensorOperations +# TODO: remove this once type_repr works for all included types +using TensorKitSectors + @isdefined(TestSetup) || include("../setup.jl") using .TestSetup diff --git a/test/symmetries/spaces.jl b/test/symmetries/spaces.jl index 95b27e2bf..a7447949d 100644 --- a/test/symmetries/spaces.jl +++ b/test/symmetries/spaces.jl @@ -1,6 +1,9 @@ using Test, TestExtras using TensorKit +# TODO: remove this once type_repr works for all included types +using TensorKitSectors + @isdefined(TestSetup) || include("../setup.jl") using .TestSetup From 4f8c37dbabf9805f428b0b8a64da8b2414e640d7 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 17 Oct 2025 11:31:36 -0400 Subject: [PATCH 08/16] attempt to fix compat check --- .github/workflows/CompatCheck.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CompatCheck.yml b/.github/workflows/CompatCheck.yml index 5bbd6d6c8..499f2065a 100644 --- a/.github/workflows/CompatCheck.yml +++ b/.github/workflows/CompatCheck.yml @@ -20,8 +20,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - downgrade_mode: ['alldeps'] - julia-version: ['1.10', '1'] + downgrade_mode: ['deps'] + julia-version: ['1.10', '1.12'] steps: - uses: actions/checkout@v5 - uses: julia-actions/setup-julia@v2 @@ -31,5 +31,6 @@ jobs: with: mode: ${{ matrix.downgrade_mode }} skip: Random, LinearAlgebra, Test, Combinatorics + julia_version: ${{ matrix.julia-version }} - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 From 0149eafaaf11c21ae48c230f305b4668dc458671 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 17 Oct 2025 11:56:03 -0400 Subject: [PATCH 09/16] some fixes after merges --- test/autodiff/ad.jl | 7 +- test/spaces.jl | 438 -------------------------------------- test/symmetries/spaces.jl | 23 +- test/tensors/diagonal.jl | 2 +- test/tensors/tensors.jl | 9 +- 5 files changed, 22 insertions(+), 457 deletions(-) delete mode 100644 test/spaces.jl diff --git a/test/autodiff/ad.jl b/test/autodiff/ad.jl index fcc5032bb..ed5d0f25b 100644 --- a/test/autodiff/ad.jl +++ b/test/autodiff/ad.jl @@ -1,5 +1,6 @@ using Test, TestExtras using TensorKit +using TensorKit: type_repr, SectorDict using TensorOperations using ChainRulesCore using ChainRulesTestUtils @@ -36,7 +37,7 @@ function ChainRulesTestUtils.test_approx( end # make sure that norms are computed correctly: -function FiniteDifferences.to_vec(t::TK.SectorDict) +function FiniteDifferences.to_vec(t::SectorDict) T = scalartype(valtype(t)) vec = mapreduce(vcat, t; init = T[]) do (c, b) return reshape(b, :) .* sqrt(dim(c)) @@ -46,7 +47,7 @@ function FiniteDifferences.to_vec(t::TK.SectorDict) function from_vec(x_real) x = T <: Real ? x_real : reinterpret(T, x_real) ctr = 0 - return TK.SectorDict( + return SectorDict( c => ( n = length(b); b′ = reshape(view(x, ctr .+ (1:n)), size(b)) ./ sqrt(dim(c)); @@ -200,7 +201,7 @@ spacelist = ( for V in spacelist I = sectortype(eltype(V)) - Istr = TK.type_repr(I) + Istr = type_repr(I) eltypes = isreal(sectortype(eltype(V))) ? (Float64, ComplexF64) : (ComplexF64,) symmetricbraiding = BraidingStyle(sectortype(eltype(V))) isa SymmetricBraiding println("---------------------------------------") diff --git a/test/spaces.jl b/test/spaces.jl deleted file mode 100644 index 3d1730f2e..000000000 --- a/test/spaces.jl +++ /dev/null @@ -1,438 +0,0 @@ -println("------------------------------------") -println("| Fields and vector spaces |") -println("------------------------------------") -@timedtestset "Fields and vector spaces" verbose = true begin - @timedtestset "Fields" begin - @test isa(ℝ, Field) - @test isa(ℂ, Field) - @test eval(Meta.parse(sprint(show, ℝ))) == ℝ - @test eval(Meta.parse(sprint(show, ℂ))) == ℂ - @test ℝ ⊆ ℝ - @test ℝ ⊆ ℂ - @test ℂ ⊆ ℂ - @test !(ℂ ⊆ ℝ) - - for T in (Int8, Int16, Int32, Int64, BigInt) - @test one(T) ∈ ℝ - @test one(Rational{T}) ∈ ℝ - @test !(one(Complex{T}) ∈ ℝ) - @test !(one(Complex{Rational{T}}) ∈ ℝ) - @test one(T) ∈ ℂ - @test one(Rational{T}) ∈ ℂ - @test one(Complex{T}) ∈ ℂ - @test one(Complex{Rational{T}} ∈ ℂ) - - @test T ⊆ ℝ - @test Rational{T} ⊆ ℝ - @test !(Complex{T} ⊆ ℝ) - @test !(Complex{Rational{T}} ⊆ ℝ) - @test T ⊆ ℂ - @test Rational{T} ⊆ ℂ - @test Complex{T} ⊆ ℂ - @test Complex{Rational{T}} ⊆ ℂ - end - for T in (Float32, Float64, BigFloat) - @test one(T) ∈ ℝ - @test !(one(Complex{T}) ∈ ℝ) - @test one(T) ∈ ℂ - @test one(Complex{T} ∈ ℂ) - - @test T ⊆ ℝ - @test !(Complex{T} ⊆ ℝ) - @test T ⊆ ℂ - @test Complex{T} ⊆ ℂ - end - end - - @timedtestset "ElementarySpace: CartesianSpace" begin - d = 2 - V = ℝ^d - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) - @test isa(V, VectorSpace) - @test isa(V, ElementarySpace) - @test isa(InnerProductStyle(V), HasInnerProduct) - @test isa(InnerProductStyle(V), EuclideanInnerProduct) - @test isa(V, CartesianSpace) - @test !isdual(V) - @test !isdual(V') - @test V == CartesianSpace(Trivial() => d) == CartesianSpace(Dict(Trivial() => d)) - @test @constinferred(hash(V)) == hash(deepcopy(V)) - @test V == @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) - @test field(V) == ℝ - @test @constinferred(sectortype(V)) == Trivial - @test ((@constinferred sectors(V))...,) == (Trivial(),) - @test length(sectors(V)) == 1 - @test @constinferred(TK.hassector(V, Trivial())) - @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test dim(@constinferred(zerospace(V))) == 0 - @test (sectors(zerospace(V))...,) == () - @test @constinferred(TK.axes(V)) == Base.OneTo(d) - @test ℝ^d == ℝ[](d) == CartesianSpace(d) == typeof(V)(d) - W = @constinferred ℝ^1 - @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) - @test @constinferred(zerospace(V)) == ℝ^0 == zerospace(typeof(V)) - @test @constinferred(⊕(V, zerospace(V))) == V - @test @constinferred(⊕(V, V)) == ℝ^(2d) - @test @constinferred(⊕(V, unitspace(V))) == ℝ^(d + 1) - @test @constinferred(⊕(V, V, V, V)) == ℝ^(4d) - @test @constinferred(fuse(V, V)) == ℝ^(d^2) - @test @constinferred(fuse(V, V', V, V')) == ℝ^(d^4) - @test @constinferred(flip(V)) == V' - @test flip(V) ≅ V - @test flip(V) ≾ V - @test flip(V) ≿ V - @test V ≺ ⊕(V, V) - @test !(V ≻ ⊕(V, V)) - @test @constinferred(infimum(V, ℝ^3)) == V - @test @constinferred(supremum(V', ℝ^3)) == ℝ^3 - end - - @timedtestset "ElementarySpace: ComplexSpace" begin - d = 2 - V = ℂ^d - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, V'))) == V' - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) - @test isa(V, VectorSpace) - @test isa(V, ElementarySpace) - @test isa(InnerProductStyle(V), HasInnerProduct) - @test isa(InnerProductStyle(V), EuclideanInnerProduct) - @test isa(V, ComplexSpace) - @test !isdual(V) - @test isdual(V') - @test V == ComplexSpace(Trivial() => d) == ComplexSpace(Dict(Trivial() => d)) - @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') - @test @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) != V - @test @constinferred(field(V)) == ℂ - @test @constinferred(sectortype(V)) == Trivial - @test @constinferred(sectortype(V)) == Trivial - @test ((@constinferred sectors(V))...,) == (Trivial(),) - @test length(sectors(V)) == 1 - @test @constinferred(TK.hassector(V, Trivial())) - @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test dim(@constinferred(zerospace(V))) == 0 - @test (sectors(zerospace(V))...,) == () - @test @constinferred(TK.axes(V)) == Base.OneTo(d) - @test ℂ^d == Vect[Trivial](d) == Vect[](Trivial() => d) == ℂ[](d) == typeof(V)(d) - W = @constinferred ℂ^1 - @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) - @test @constinferred(zerospace(V)) == ℂ^0 == zerospace(typeof(V)) - @test @constinferred(⊕(V, zerospace(V))) == V - @test @constinferred(⊕(V, V)) == ℂ^(2d) - @test_throws SpaceMismatch (⊕(V, V')) - # promote_except = ErrorException("promotion of types $(typeof(ℝ^d)) and " * - # "$(typeof(ℂ^d)) failed to change any arguments") - # @test_throws promote_except (⊕(ℝ^d, ℂ^d)) - @test_throws ErrorException (⊗(ℝ^d, ℂ^d)) - @test @constinferred(⊕(V, V)) == ℂ^(2d) - @test @constinferred(⊕(V, unitspace(V))) == ℂ^(d + 1) - @test @constinferred(⊕(V, V, V, V)) == ℂ^(4d) - @test @constinferred(fuse(V, V)) == ℂ^(d^2) - @test @constinferred(fuse(V, V', V, V')) == ℂ^(d^4) - @test @constinferred(flip(V)) == V' - @test flip(V) ≅ V - @test flip(V) ≾ V - @test flip(V) ≿ V - @test V ≺ ⊕(V, V) - @test !(V ≻ ⊕(V, V)) - @test @constinferred(infimum(V, ℂ^3)) == V - @test @constinferred(supremum(V', (ℂ^3)')) == dual(ℂ^3) == conj(ℂ^3) - end - - @timedtestset "ElementarySpace: GeneralSpace" begin - d = 2 - V = GeneralSpace{ℂ}(d) - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, dual(V)))) == dual(V) - @test eval(Meta.parse(sprint(show, conj(V)))) == conj(V) - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) - @test !isdual(V) - @test isdual(V') - @test !isdual(conj(V)) - @test isdual(conj(V')) - @test !TK.isconj(V) - @test !TK.isconj(V') - @test TK.isconj(conj(V)) - @test TK.isconj(conj(V')) - @test isa(V, VectorSpace) - @test isa(V, ElementarySpace) - @test !isa(InnerProductStyle(V), HasInnerProduct) - @test !isa(InnerProductStyle(V), EuclideanInnerProduct) - @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') - @test @constinferred(dual(V)) != @constinferred(conj(V)) != V - @test @constinferred(field(V)) == ℂ - @test @constinferred(sectortype(V)) == Trivial - @test @constinferred(TK.hassector(V, Trivial())) - @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test @constinferred(TK.axes(V)) == Base.OneTo(d) - end - - @timedtestset "ElementarySpace: $(TK.type_repr(Vect[I]))" for I in sectorlist - if Base.IteratorSize(values(I)) === Base.IsInfinite() - set = unique(vcat(unit(I), [randsector(I) for k in 1:10])) - gen = (c => 2 for c in set) - else - gen = (values(I)[k] => (k + 1) for k in 1:length(values(I))) - end - V = GradedSpace(gen) - @test eval(Meta.parse(TK.type_repr(typeof(V)))) == typeof(V) - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, V'))) == V' - @test V' == GradedSpace(gen; dual = true) - @test V == @constinferred GradedSpace(gen...) - @test V' == @constinferred GradedSpace(gen...; dual = true) - @test V == @constinferred GradedSpace(tuple(gen...)) - @test V' == @constinferred GradedSpace(tuple(gen...); dual = true) - @test V == @constinferred GradedSpace(Dict(gen)) - @test V' == @constinferred GradedSpace(Dict(gen); dual = true) - @test V == @inferred Vect[I](gen) - @test V' == @constinferred Vect[I](gen; dual = true) - @test V == @constinferred Vect[I](gen...) - @test V' == @constinferred Vect[I](gen...; dual = true) - @test V == @constinferred Vect[I](Dict(gen)) - @test V' == @constinferred Vect[I](Dict(gen); dual = true) - @test V == @constinferred typeof(V)(c => dim(V, c) for c in sectors(V)) - if I isa ZNIrrep - @test V == @constinferred typeof(V)(V.dims) - @test V' == @constinferred typeof(V)(V.dims; dual = true) - end - @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') - @test V == GradedSpace(reverse(collect(gen))...) - @test eval(Meta.parse(sprint(show, V))) == V - @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) - # space with no sectors - @test dim(@constinferred(zerospace(V))) == 0 - # space with a single sector - W = @constinferred GradedSpace(unit(I) => 1) - @test W == GradedSpace(unit(I) => 1, randsector(I) => 0) - @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) - @test @constinferred(zerospace(V)) == GradedSpace(unit(I) => 0) - # randsector never returns trivial sector, so this cannot error - @test_throws ArgumentError GradedSpace(unit(I) => 1, randsector(I) => 0, unit(I) => 3) - @test eval(Meta.parse(sprint(show, W))) == W - @test isa(V, VectorSpace) - @test isa(V, ElementarySpace) - @test isa(InnerProductStyle(V), HasInnerProduct) - @test isa(InnerProductStyle(V), EuclideanInnerProduct) - @test isa(V, GradedSpace) - @test isa(V, GradedSpace{I}) - @test @constinferred(dual(V)) == @constinferred(conj(V)) == @constinferred(adjoint(V)) != V - @test @constinferred(field(V)) == ℂ - @test @constinferred(sectortype(V)) == I - slist = @constinferred sectors(V) - @test @constinferred(TK.hassector(V, first(slist))) - @test @constinferred(dim(V)) == sum(dim(s) * dim(V, s) for s in slist) - @test @constinferred(reduceddim(V)) == sum(dim(V, s) for s in slist) - @constinferred dim(V, first(slist)) - if hasfusiontensor(I) - @test @constinferred(TK.axes(V)) == Base.OneTo(dim(V)) - end - @test @constinferred(⊕(V, zerospace(V))) == V - @test @constinferred(⊕(V, V)) == Vect[I](c => 2dim(V, c) for c in sectors(V)) - @test @constinferred(⊕(V, V, V, V)) == Vect[I](c => 4dim(V, c) for c in sectors(V)) - @test @constinferred(⊕(V, oneunit(V))) == Vect[I](c => isunit(c) + dim(V, c) for c in sectors(V)) - @test @constinferred(fuse(V, oneunit(V))) == V - d = Dict{I, Int}() - for a in sectors(V), b in sectors(V) - for c in a ⊗ b - d[c] = get(d, c, 0) + dim(V, a) * dim(V, b) * Nsymbol(a, b, c) - end - end - @test @constinferred(fuse(V, V)) == GradedSpace(d) - @test @constinferred(flip(V)) == Vect[I](conj(c) => dim(V, c) for c in sectors(V))' - @test flip(V) ≅ V - @test flip(V) ≾ V - @test flip(V) ≿ V - @test @constinferred(⊕(V, V)) == @constinferred supremum(V, ⊕(V, V)) - @test V == @constinferred infimum(V, ⊕(V, V)) - @test V ≺ ⊕(V, V) - @test !(V ≻ ⊕(V, V)) - @test infimum(V, GradedSpace(unit(I) => 3)) == GradedSpace(unit(I) => 2) - @test_throws SpaceMismatch (⊕(V, V')) - end - - @timedtestset "ProductSpace{ℂ}" begin - V1, V2, V3, V4 = ℂ^1, ℂ^2, ℂ^3, ℂ^4 - P = @constinferred ProductSpace(V1, V2, V3, V4) - @test eval(Meta.parse(sprint(show, P))) == P - @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) - @test isa(P, VectorSpace) - @test isa(P, CompositeSpace) - @test spacetype(P) == ComplexSpace - @test sectortype(P) == Trivial - @test @constinferred(hash(P)) == hash(deepcopy(P)) != hash(P') - @test P == deepcopy(P) - @test P == typeof(P)(P...) - @constinferred (x -> tuple(x...))(P) - @test @constinferred(dual(P)) == P' - @test @constinferred(field(P)) == ℂ - @test @constinferred(*(V1, V2, V3, V4)) == P - @test @constinferred(⊗(V1, V2, V3, V4)) == P - @test @constinferred(⊗(V1, V2 ⊗ V3 ⊗ V4)) == P - @test @constinferred(⊗(V1 ⊗ V2, V3 ⊗ V4)) == P - @test @constinferred(⊗(V1, V2, V3 ⊗ V4)) == P - @test @constinferred(⊗(V1, V2 ⊗ V3, V4)) == P - @test V1 * V2 * unitspace(V1) * V3 * V4 == - @constinferred(insertleftunit(P, 3)) == - @constinferred(insertrightunit(P, 2)) - @test @constinferred(removeunit(V1 * V2 * unitspace(V1)' * V3 * V4, 3)) == P - @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 - @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 - @test fuse(V1, V2', V3) ≿ V1 ⊗ V2' ⊗ V3 - @test fuse(flip(V1), V2, flip(V3)) ≅ V1 ⊗ V2 ⊗ V3 - @test @constinferred(⊗(P)) == P - @test @constinferred(⊗(V1)) == ProductSpace(V1) - @test eval(Meta.parse(sprint(show, ⊗(V1)))) == ⊗(V1) - @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == - @constinferred(one(P)) == @constinferred(one(typeof(P))) == - ProductSpace{ComplexSpace}(()) - @test eval(Meta.parse(sprint(show, one(P)))) == one(P) - @test @constinferred(⊗(one(P), P)) == P - @test @constinferred(⊗(P, one(P))) == P - @test @constinferred(⊗(one(P), one(P))) == one(P) - @test @constinferred(adjoint(P)) == dual(P) == V4' ⊗ V3' ⊗ V2' ⊗ V1' - @test @constinferred(dims(P)) == map(dim, (V1, V2, V3, V4)) - @test @constinferred(dim(P)) == prod(dim, (V1, V2, V3, V4)) - @test @constinferred(dim(P, 2)) == dim(V2) - @test @constinferred(dim(one(P))) == 1 - @test first(@constinferred(sectors(P))) == (Trivial(), Trivial(), Trivial(), Trivial()) - @test first(@constinferred(sectors(one(P)))) == () - cube(x) = x^3 - @test @constinferred(cube(V1)) == V1 ⊗ V1 ⊗ V1 - N = 3 - @test V1^N == V1 ⊗ V1 ⊗ V1 - @test P^2 == P ⊗ P - @test @constinferred(dims(P, first(sectors(P)))) == dims(P) - @test ((@constinferred blocksectors(P))...,) == (Trivial(),) - @test isempty(blocksectors(P ⊗ ℂ^0)) - @test isempty(@constinferred(sectors(P ⊗ ℂ^0))) - @test @constinferred(blockdim(P, first(blocksectors(P)))) == dim(P) - @test @constinferred(blockdim(P, Trivial())) == dim(P) - @test @constinferred(blockdim(one(P), Trivial())) == 1 - @test Base.IteratorEltype(P) == Base.IteratorEltype(typeof(P)) == Base.IteratorEltype(P.spaces) - @test Base.IteratorSize(P) == Base.IteratorSize(typeof(P)) == Base.IteratorSize(P.spaces) - @test Base.eltype(P) == Base.eltype(typeof(P)) == typeof(V1) - @test eltype(collect(P)) == typeof(V1) - @test collect(P) == [V1, V2, V3, V4] - @test_throws MethodError P ⊕ P - end - - @timedtestset "ProductSpace{SU₂Space}" begin - V1, V2, V3 = SU₂Space(0 => 3, 1 // 2 => 1), SU₂Space(0 => 2, 1 => 1), - SU₂Space(1 // 2 => 1, 1 => 1)' - P = @constinferred ProductSpace(V1, V2, V3) - @test eval(Meta.parse(sprint(show, P))) == P - @test eval(Meta.parse(sprint(show, typeof(P)))) == typeof(P) - @test isa(P, VectorSpace) - @test isa(P, CompositeSpace) - @test spacetype(P) == SU₂Space - @test sectortype(P) == Irrep[SU₂] == SU2Irrep - @test @constinferred(hash(P)) == hash(deepcopy(P)) != hash(P') - @test @constinferred(dual(P)) == P' - @test @constinferred(field(P)) == ℂ - @test @constinferred(*(V1, V2, V3)) == P - @test @constinferred(⊗(V1, V2, V3)) == P - @test @constinferred(adjoint(P)) == dual(P) == V3' ⊗ V2' ⊗ V1' - @test V1 * V2 * unitspace(V1)' * V3 == - @constinferred(insertleftunit(P, 3; conj = true)) == - @constinferred(insertrightunit(P, 2; conj = true)) - @test P == @constinferred(removeunit(insertleftunit(P, 3), 3)) - @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 - @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 ≾ fuse(V1 ⊗ V2' ⊗ V3) - @test fuse(V1, V2') ⊗ V3 ≾ V1 ⊗ V2' ⊗ V3 - @test fuse(V1, V2', V3) ≿ V1 ⊗ V2' ⊗ V3 ≿ fuse(V1 ⊗ V2' ⊗ V3) - @test V1 ⊗ fuse(V2', V3) ≿ V1 ⊗ V2' ⊗ V3 - @test fuse(flip(V1) ⊗ V2) ⊗ flip(V3) ≅ V1 ⊗ V2 ⊗ V3 - @test @constinferred(⊗(V1)) == ProductSpace(V1) - @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == - @constinferred(one(P)) == @constinferred(one(typeof(P))) - @test @constinferred(dims(P)) == map(dim, (V1, V2, V3)) - @test @constinferred(dim(P)) == prod(dim, (V1, V2, V3)) - @test @constinferred(dim(one(P))) == 1 - @test first(@constinferred(sectors(one(P)))) == () - @test @constinferred(blockdim(one(P), Irrep[SU₂](0))) == 1 - for s in @constinferred(sectors(P)) - @test hassector(P, s) - @test @constinferred(dims(P, s)) == dim.((V1, V2, V3), s) - end - @test sum(dim(c) * blockdim(P, c) for c in @constinferred(blocksectors(P))) == dim(P) - end - - @timedtestset "Deligne tensor product of spaces" begin - V1 = SU₂Space(0 => 3, 1 // 2 => 1) - V2 = SU₂Space(0 => 2, 1 => 1)' - V3 = ℤ₃Space(0 => 3, 1 => 2, 2 => 1) - V4 = ℂ^3 - - for W1 in (V1, V2, V3, V4) - for W2 in (V1, V2, V3, V4) - for W3 in (V1, V2, V3, V4) - for W4 in (V1, V2, V3, V4) - Ws = @constinferred(W1 ⊠ W2 ⊠ W3 ⊠ W4) - @test Ws == @constinferred((W1 ⊠ W2) ⊠ (W3 ⊠ W4)) == - @constinferred(((W1 ⊠ W2) ⊠ W3) ⊠ W4) == - @constinferred((W1 ⊠ (W2 ⊠ W3)) ⊠ W4) == - @constinferred(W1 ⊠ ((W2 ⊠ W3)) ⊠ W4) == - @constinferred(W1 ⊠ (W2 ⊠ (W3 ⊠ W4))) - I1, I2, I3, I4 = map(sectortype, (W1, W2, W3, W4)) - I = sectortype(Ws) - @test I == @constinferred((I1 ⊠ I2) ⊠ (I3 ⊠ I4)) == - @constinferred(((I1 ⊠ I2) ⊠ I3) ⊠ I4) == - @constinferred((I1 ⊠ (I2 ⊠ I3)) ⊠ I4) == - @constinferred(I1 ⊠ ((I2 ⊠ I3)) ⊠ I4) == - @constinferred(I1 ⊠ (I2 ⊠ (I3 ⊠ I4))) - @test dim(Ws) == dim(W1) * dim(W2) * dim(W3) * dim(W4) - end - end - end - end - @test sectortype(@constinferred((V1 ⊗ V2) ⊠ V3)) == @constinferred(Irrep[SU₂ × ℤ₃]) - @test dim((V1 ⊗ V2) ⊠ V3) == dim(V1) * dim(V2) * dim(V3) - @test sectortype((V1 ⊗ V2) ⊠ V3 ⊠ V4) == Irrep[SU₂ × ℤ₃] - @test dim((V1 ⊗ V2) ⊠ V3 ⊠ V4) == dim(V1) * dim(V2) * dim(V3) * dim(V4) - @test fuse(V2 ⊠ V4) == fuse(V4 ⊠ V2) == SU₂Space(0 => 6, 1 => 3) - @test fuse(V3 ⊠ V4) == fuse(V4 ⊠ V3) == ℤ₃Space(0 => 9, 1 => 6, 2 => 3) - end - - @timedtestset "HomSpace" begin - for (V1, V2, V3, V4, V5) in (Vtr, Vℤ₃, VSU₂) - W = TK.HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) - @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) - @test W == (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) - @test W' == (V1 ⊗ V2 → V3 ⊗ V4 ⊗ V5) - @test eval(Meta.parse(sprint(show, W))) == W - @test eval(Meta.parse(sprint(show, typeof(W)))) == typeof(W) - @test spacetype(W) == typeof(V1) - @test sectortype(W) == sectortype(V1) - @test W[1] == V1 - @test W[2] == V2 - @test W[3] == V3' - @test W[4] == V4' - @test W[5] == V5' - @test @constinferred(hash(W)) == hash(deepcopy(W)) != hash(W') - @test W == deepcopy(W) - @test W == @constinferred permute(W, ((1, 2), (3, 4, 5))) - @test permute(W, ((2, 4, 5), (3, 1))) == (V2 ⊗ V4' ⊗ V5' ← V3 ⊗ V1') - @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TK.compose(W, W') - @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)) == - @constinferred(insertleftunit(W)) == - @constinferred(insertrightunit(W)) - @test @constinferred(removeunit(insertleftunit(W), $(numind(W) + 1))) == W - @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)') == - @constinferred(insertleftunit(W; conj = true)) == - @constinferred(insertrightunit(W; conj = true)) - @test (unitspace(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == - @constinferred(insertleftunit(W, 1)) == - @constinferred(insertrightunit(W, 0)) - @test (V1 ⊗ V2 ⊗ unitspace(V1) ← V3 ⊗ V4 ⊗ V5) == - @constinferred(insertrightunit(W, 2)) - @test (V1 ⊗ V2 ← unitspace(V1) ⊗ V3 ⊗ V4 ⊗ V5) == - @constinferred(insertleftunit(W, 3)) - @test @constinferred(removeunit(insertleftunit(W, 3), 3)) == W - @test @constinferred(insertrightunit(one(V1) ← V1, 0)) == (unitspace(V1) ← V1) - @test_throws BoundsError insertleftunit(one(V1) ← V1, 0) - end - end - TK.empty_globalcaches!() -end diff --git a/test/symmetries/spaces.jl b/test/symmetries/spaces.jl index a7447949d..8bab395b7 100644 --- a/test/symmetries/spaces.jl +++ b/test/symmetries/spaces.jl @@ -1,5 +1,6 @@ using Test, TestExtras using TensorKit +using TensorKit: hassector, type_repr, HomSpace # TODO: remove this once type_repr works for all included types using TensorKitSectors @@ -70,11 +71,11 @@ end @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @test length(sectors(V)) == 1 - @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) @test dim(@constinferred(zerospace(V))) == 0 @test (sectors(zerospace(V))...,) == () - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test @constinferred(axes(V)) == Base.OneTo(d) @test ℝ^d == ℝ[](d) == CartesianSpace(d) == typeof(V)(d) W = @constinferred ℝ^1 @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) @@ -116,11 +117,11 @@ end @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @test length(sectors(V)) == 1 - @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) @test dim(@constinferred(zerospace(V))) == 0 @test (sectors(zerospace(V))...,) == () - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test @constinferred(axes(V)) == Base.OneTo(d) @test ℂ^d == Vect[Trivial](d) == Vect[](Trivial() => d) == ℂ[](d) == typeof(V)(d) W = @constinferred ℂ^1 @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) @@ -170,12 +171,12 @@ end @test @constinferred(dual(V)) != @constinferred(conj(V)) != V @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == Trivial - @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test @constinferred(axes(V)) == Base.OneTo(d) end -@timedtestset "ElementarySpace: $(TensorKit.type_repr(Vect[I]))" for I in sectorlist +@timedtestset "ElementarySpace: $(type_repr(Vect[I]))" for I in sectorlist if Base.IteratorSize(values(I)) === Base.IsInfinite() set = unique(vcat(unit(I), [randsector(I) for k in 1:10])) gen = (c => 2 for c in set) @@ -183,7 +184,7 @@ end gen = (values(I)[k] => (k + 1) for k in 1:length(values(I))) end V = GradedSpace(gen) - @test eval(Meta.parse(TensorKit.type_repr(typeof(V)))) == typeof(V) + @test eval(Meta.parse(type_repr(typeof(V)))) == typeof(V) @test eval(parse_show(V)) == V @test eval(parse_show(V')) == V' @test V' == GradedSpace(gen; dual = true) @@ -228,12 +229,12 @@ end @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == I slist = @constinferred sectors(V) - @test @constinferred(TensorKit.hassector(V, first(slist))) + @test @constinferred(hassector(V, first(slist))) @test @constinferred(dim(V)) == sum(dim(s) * dim(V, s) for s in slist) @test @constinferred(reduceddim(V)) == sum(dim(V, s) for s in slist) @constinferred dim(V, first(slist)) if hasfusiontensor(I) - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(dim(V)) + @test @constinferred(axes(V)) == Base.OneTo(dim(V)) end @test @constinferred(⊕(V, zerospace(V))) == V @test @constinferred(⊕(V, V)) == Vect[I](c => 2dim(V, c) for c in sectors(V)) @@ -404,7 +405,7 @@ end @timedtestset "HomSpace" begin for (V1, V2, V3, V4, V5) in (Vtr, Vℤ₃, VSU₂) - W = TensorKit.HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) + W = HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) @test W == (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) @test W' == (V1 ⊗ V2 → V3 ⊗ V4 ⊗ V5) diff --git a/test/tensors/diagonal.jl b/test/tensors/diagonal.jl index ad174bf4e..9154ed3b4 100644 --- a/test/tensors/diagonal.jl +++ b/test/tensors/diagonal.jl @@ -37,7 +37,7 @@ diagspacelist = ( b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TK.blocktype(t) + @test typeof(b1) === TensorKit.blocktype(t) # basic linear algebra @test isa(@constinferred(norm(t)), real(T)) @test norm(t)^2 ≈ dot(t, t) diff --git a/test/tensors/tensors.jl b/test/tensors/tensors.jl index 103c53f8d..51c326cf9 100644 --- a/test/tensors/tensors.jl +++ b/test/tensors/tensors.jl @@ -1,5 +1,6 @@ using Test, TestExtras using TensorKit +using TensorKit: type_repr using Combinatorics: permutations using LinearAlgebra: LinearAlgebra @@ -25,7 +26,7 @@ end for V in spacelist I = sectortype(first(V)) - Istr = TK.type_repr(I) + Istr = type_repr(I) println("---------------------------------------") println("Tensors with symmetry: $Istr") println("---------------------------------------") @@ -50,7 +51,7 @@ for V in spacelist b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TK.blocktype(t) + @test typeof(b1) === TensorKit.blocktype(t) @test typeof(c) === sectortype(t) end end @@ -112,7 +113,7 @@ for V in spacelist b2 = @constinferred block(t', first(blocksectors(t'))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TK.blocktype(t') + @test typeof(b1) === TensorKit.blocktype(t') @test typeof(c) === sectortype(t) # linear algebra @test isa(@constinferred(norm(t)), real(T)) @@ -558,7 +559,7 @@ for V in spacelist @test t3 ≈ t4 end end - TK.empty_globalcaches!() + TensorKit.empty_globalcaches!() end @timedtestset "Deligne tensor product: test via conversion" begin From 9b8fc3110e4b1da0b3fe591d359781d8e716c678 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 17 Oct 2025 14:43:58 -0400 Subject: [PATCH 10/16] slightly larger space to avoid cutting everything --- test/autodiff/ad.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/autodiff/ad.jl b/test/autodiff/ad.jl index ed5d0f25b..eb52a81db 100644 --- a/test/autodiff/ad.jl +++ b/test/autodiff/ad.jl @@ -191,7 +191,7 @@ spacelist = ( Vect[SU2Irrep](0 => 1, 1 // 2 => 1, 3 // 2 => 1)', ), ( - Vect[FibonacciAnyon](:I => 1, :τ => 1), + Vect[FibonacciAnyon](:I => 2, :τ => 1), Vect[FibonacciAnyon](:I => 1, :τ => 2)', Vect[FibonacciAnyon](:I => 2, :τ => 2)', Vect[FibonacciAnyon](:I => 2, :τ => 3), From d7fddc223a59f483a9eaddcc8a0798299fcb1580 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 17 Oct 2025 16:16:30 -0400 Subject: [PATCH 11/16] more tweaks --- test/autodiff/ad.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/autodiff/ad.jl b/test/autodiff/ad.jl index eb52a81db..0c5d8a52a 100644 --- a/test/autodiff/ad.jl +++ b/test/autodiff/ad.jl @@ -597,7 +597,7 @@ for V in spacelist # TODO: I'm not sure how to properly test with spaces that might change # with the finite-difference methods, as then the jacobian is ill-defined. - trunc = truncrank(round(Int, min(dim(domain(t)), dim(codomain(t))) ÷ 2)) + trunc = truncrank(round(Int, min(dim(domain(t)), dim(codomain(t))) * (3 / 4))) USVᴴ_trunc = svd_trunc(t; trunc) ΔUSVᴴ_trunc = rand_tangent.(USVᴴ_trunc) remove_svdgauge_dependence!( From f82c78d08bd69f4f5bdbbed28fc6ad238e760310 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Thu, 23 Oct 2025 08:30:31 -0400 Subject: [PATCH 12/16] more tweaks --- test/autodiff/ad.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/autodiff/ad.jl b/test/autodiff/ad.jl index 0c5d8a52a..0230936f7 100644 --- a/test/autodiff/ad.jl +++ b/test/autodiff/ad.jl @@ -597,7 +597,7 @@ for V in spacelist # TODO: I'm not sure how to properly test with spaces that might change # with the finite-difference methods, as then the jacobian is ill-defined. - trunc = truncrank(round(Int, min(dim(domain(t)), dim(codomain(t))) * (3 / 4))) + trunc = truncrank(max(2, round(Int, min(dim(domain(t)), dim(codomain(t))) * (3 / 4)))) USVᴴ_trunc = svd_trunc(t; trunc) ΔUSVᴴ_trunc = rand_tangent.(USVᴴ_trunc) remove_svdgauge_dependence!( From d4fc2c43e986caf2fd8ce76a9f08b54dbe8d36fd Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Thu, 23 Oct 2025 10:58:30 -0400 Subject: [PATCH 13/16] fix tensorkitsectors issue --- Project.toml | 2 +- src/spaces/gradedspace.jl | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index 0f580e579..3a51f5f52 100644 --- a/Project.toml +++ b/Project.toml @@ -41,7 +41,7 @@ Random = "1" SafeTestsets = "0.1" ScopedValues = "1.3.0" Strided = "2" -TensorKitSectors = "0.3" +TensorKitSectors = "0.3.1" TensorOperations = "5.1" Test = "1" TestExtras = "0.2,0.3" diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index 687c79acd..7155ab220 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -268,17 +268,18 @@ function type_repr(::Type{<:GradedSpace{ProductSector{T}}}) where end # Specific constructors for Z_N -const ZNSpace{N} = GradedSpace{ZNIrrep{N}, NTuple{N, Int}} -ZNSpace{N}(dims::NTuple{N, Int}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) -ZNSpace{N}(dims::Vararg{Int, N}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) -ZNSpace(dims::NTuple{N, Int}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) -ZNSpace(dims::Vararg{Int, N}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +const ZNSpace{N, T} = GradedSpace{ZNIrrep{N, T}, NTuple{N, Int}} +ZNSpace{N}(dims; dual::Bool = false) where {N} = Rep[ℤ{N}](dims; dual) +ZNSpace{N, T}(dims::NTuple{N, Int}; dual::Bool = false) where {T, N} = ZNSpace{N, T}(dims, dual) +ZNSpace{N, T}(dims::Vararg{Int, N}; dual::Bool = false) where {T, N} = ZNSpace{N, T}(dims, dual) +ZNSpace(dims::NTuple{N, Int}; dual::Bool = false) where {N} = Rep[ℤ{N}](dims, dual) +ZNSpace(dims::Vararg{Int, N}; dual::Bool = false) where {N} = Rep[ℤ{N}](dims, dual) # TODO: Do we still need all of those # ASCII type aliases -const Z2Space = ZNSpace{2} -const Z3Space = ZNSpace{3} -const Z4Space = ZNSpace{4} +const Z2Space = ZNSpace{2, UInt8} +const Z3Space = ZNSpace{3, UInt8} +const Z4Space = ZNSpace{4, UInt8} const U1Space = Rep[U₁] const CU1Space = Rep[CU₁] const SU2Space = Rep[SU₂] From e3ed7201f16963db88c83f8216f3289c717b1df9 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 24 Oct 2025 07:37:18 -0400 Subject: [PATCH 14/16] add compatchecker comment --- .github/workflows/CompatCheck.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CompatCheck.yml b/.github/workflows/CompatCheck.yml index 499f2065a..079244269 100644 --- a/.github/workflows/CompatCheck.yml +++ b/.github/workflows/CompatCheck.yml @@ -21,6 +21,8 @@ jobs: strategy: matrix: downgrade_mode: ['deps'] + # TODO: this should be ['1.10', '1'] but that is currently broken: + # https://github.com/julia-actions/julia-downgrade-compat/issues/25 julia-version: ['1.10', '1.12'] steps: - uses: actions/checkout@v5 From aa50cddadd522234fb8c71297a0cd40cc63256e1 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 24 Oct 2025 07:56:35 -0400 Subject: [PATCH 15/16] add code suggestions --- test/runtests.jl | 4 ++- test/setup.jl | 8 +----- test/symmetries/spaces.jl | 59 ++++++++++++++++++++++----------------- test/tensors/planar.jl | 2 +- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 2ad193f15..854991a7f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -22,7 +22,7 @@ if isempty(settings[:groups]) if haskey(ENV, "GROUP") groups = [ENV["GROUP"]] else - groups = filter(isdir, readdir(@__DIR__)) + groups = filter(isdir ∘ Base.Fix1(joinpath, @__DIR__), readdir(@__DIR__)) end else groups = settings[:groups] @@ -32,6 +32,8 @@ checktestgroup(group) = isdir(joinpath(@__DIR__, group)) || throw(ArgumentError("Invalid group ($group), no such folder")) foreach(checktestgroup, groups) +@info "Loaded test groups:" groups + # don't run all tests on GPU, only the GPU specific ones is_buildkite = get(ENV, "BUILDKITE", "false") == "true" diff --git a/test/setup.jl b/test/setup.jl index 93545c0b6..0af9ce633 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -4,16 +4,10 @@ export smallset, randsector, hasfusiontensor, force_planar export sectorlist export Vtr, Vℤ₂, Vfℤ₂, Vℤ₃, VU₁, VfU₁, VCU₁, VSU₂, VfSU₂, VSU₂U₁, Vfib -using Test -using TestExtras using Random using TensorKit -using Combinatorics -using TensorKit: ProductSector, fusiontensor, pentagon_equation, hexagon_equation using TensorKit: ℙ, PlanarTrivial -using TensorOperations using Base.Iterators: take, product -using LinearAlgebra: LinearAlgebra Random.seed!(1234) @@ -38,7 +32,7 @@ function randsector(::Type{I}) where {I <: Sector} end function hasfusiontensor(I::Type{<:Sector}) try - fusiontensor(one(I), one(I), one(I)) + TensorKit.fusiontensor(one(I), one(I), one(I)) return true catch e if e isa MethodError diff --git a/test/symmetries/spaces.jl b/test/symmetries/spaces.jl index 8bab395b7..a9edb5828 100644 --- a/test/symmetries/spaces.jl +++ b/test/symmetries/spaces.jl @@ -8,13 +8,22 @@ using TensorKitSectors @isdefined(TestSetup) || include("../setup.jl") using .TestSetup -parse_show(x) = Meta.parse(sprint(show, x; context = (:module => @__MODULE__))) +""" + eval_show(x) + +Use `show` to generate a string representation of `x`, then parse and evaluate the resulting expression. +""" +function eval_show(x) + str = sprint(show, x; context = (:module => @__MODULE__)) + ex = Meta.parse(str) + return eval(ex) +end @timedtestset "Fields" begin @test isa(ℝ, Field) @test isa(ℂ, Field) - @test eval(parse_show(ℝ)) == ℝ - @test eval(parse_show(ℂ)) == ℂ + @test eval_show(ℝ) == ℝ + @test eval_show(ℂ) == ℂ @test ℝ ⊆ ℝ @test ℝ ⊆ ℂ @test ℂ ⊆ ℂ @@ -55,8 +64,8 @@ end @timedtestset "ElementarySpace: CartesianSpace" begin d = 2 V = ℝ^d - @test eval(parse_show(V)) == V - @test eval(parse_show(typeof(V))) == typeof(V) + @test eval_show(V) == V + @test eval_show(typeof(V)) == typeof(V) @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test isa(InnerProductStyle(V), HasInnerProduct) @@ -99,9 +108,9 @@ end @timedtestset "ElementarySpace: ComplexSpace" begin d = 2 V = ℂ^d - @test eval(parse_show(V)) == V - @test eval(parse_show(V')) == V' - @test eval(parse_show(typeof(V))) == typeof(V) + @test eval_show(V) == V + @test eval_show(V') == V' + @test eval_show(typeof(V)) == typeof(V) @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test isa(InnerProductStyle(V), HasInnerProduct) @@ -151,10 +160,10 @@ end @timedtestset "ElementarySpace: GeneralSpace" begin d = 2 V = GeneralSpace{ℂ}(d) - @test eval(parse_show(V)) == V - @test eval(parse_show(dual(V))) == dual(V) - @test eval(parse_show(conj(V))) == conj(V) - @test eval(parse_show(typeof(V))) == typeof(V) + @test eval_show(V) == V + @test eval_show(dual(V)) == dual(V) + @test eval_show(conj(V)) == conj(V) + @test eval_show(typeof(V)) == typeof(V) @test !isdual(V) @test isdual(V') @test !isdual(conj(V)) @@ -185,8 +194,8 @@ end end V = GradedSpace(gen) @test eval(Meta.parse(type_repr(typeof(V)))) == typeof(V) - @test eval(parse_show(V)) == V - @test eval(parse_show(V')) == V' + @test eval_show(V) == V + @test eval_show(V') == V' @test V' == GradedSpace(gen; dual = true) @test V == @constinferred GradedSpace(gen...) @test V' == @constinferred GradedSpace(gen...; dual = true) @@ -207,8 +216,8 @@ end end @test @constinferred(hash(V)) == hash(deepcopy(V)) != hash(V') @test V == GradedSpace(reverse(collect(gen))...) - @test eval(parse_show(V)) == V - @test eval(parse_show(typeof(V))) == typeof(V) + @test eval_show(V) == V + @test eval_show(typeof(V)) == typeof(V) # space with no sectors @test dim(@constinferred(zerospace(V))) == 0 # space with a single sector @@ -218,7 +227,7 @@ end @test @constinferred(zerospace(V)) == GradedSpace(unit(I) => 0) # randsector never returns trivial sector, so this cannot error @test_throws ArgumentError GradedSpace(unit(I) => 1, randsector(I) => 0, unit(I) => 3) - @test eval(parse_show(W)) == W + @test eval_show(W) == W @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test isa(InnerProductStyle(V), HasInnerProduct) @@ -263,8 +272,8 @@ end @timedtestset "ProductSpace{ℂ}" begin V1, V2, V3, V4 = ℂ^1, ℂ^2, ℂ^3, ℂ^4 P = @constinferred ProductSpace(V1, V2, V3, V4) - @test eval(parse_show(P)) == P - @test eval(parse_show(typeof(P))) == typeof(P) + @test eval_show(P) == P + @test eval_show(typeof(P)) == typeof(P) @test isa(P, VectorSpace) @test isa(P, CompositeSpace) @test spacetype(P) == ComplexSpace @@ -291,11 +300,11 @@ end @test fuse(flip(V1), V2, flip(V3)) ≅ V1 ⊗ V2 ⊗ V3 @test @constinferred(⊗(P)) == P @test @constinferred(⊗(V1)) == ProductSpace(V1) - @test eval(parse_show(⊗(V1))) == ⊗(V1) + @test eval_show(⊗(V1)) == ⊗(V1) @test @constinferred(one(V1)) == @constinferred(one(typeof(V1))) == @constinferred(one(P)) == @constinferred(one(typeof(P))) == ProductSpace{ComplexSpace}(()) - @test eval(parse_show(one(P))) == one(P) + @test eval_show(one(P)) == one(P) @test @constinferred(⊗(one(P), P)) == P @test @constinferred(⊗(P, one(P))) == P @test @constinferred(⊗(one(P), one(P))) == one(P) @@ -330,8 +339,8 @@ end V1, V2, V3 = SU₂Space(0 => 3, 1 // 2 => 1), SU₂Space(0 => 2, 1 => 1), SU₂Space(1 // 2 => 1, 1 => 1)' P = @constinferred ProductSpace(V1, V2, V3) - @test eval(parse_show(P)) == P - @test eval(parse_show(typeof(P))) == typeof(P) + @test eval_show(P) == P + @test eval_show(typeof(P)) == typeof(P) @test isa(P, VectorSpace) @test isa(P, CompositeSpace) @test spacetype(P) == SU₂Space @@ -409,8 +418,8 @@ end @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) @test W == (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) @test W' == (V1 ⊗ V2 → V3 ⊗ V4 ⊗ V5) - @test eval(parse_show(W)) == W - @test eval(parse_show(typeof(W))) == typeof(W) + @test eval_show(W) == W + @test eval_show(typeof(W)) == typeof(W) @test spacetype(W) == typeof(V1) @test sectortype(W) == sectortype(V1) @test W[1] == V1 diff --git a/test/tensors/planar.jl b/test/tensors/planar.jl index d5e8bb569..57b6e9786 100644 --- a/test/tensors/planar.jl +++ b/test/tensors/planar.jl @@ -1,7 +1,7 @@ using Test, TestExtras using TensorKit -using TensorKit: planaradd!, planartrace!, planarcontract! using TensorKit: PlanarTrivial, ℙ +using TensorKit: planaradd!, planartrace!, planarcontract! using TensorOperations @isdefined(TestSetup) || include("../setup.jl") From 1ddf2d9588b41112813464e5a4f03677ccceda8f Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Fri, 24 Oct 2025 08:13:05 -0400 Subject: [PATCH 16/16] better fix of ZNIrrep shenanigans --- Project.toml | 2 +- src/spaces/gradedspace.jl | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Project.toml b/Project.toml index 3a51f5f52..0b305d802 100644 --- a/Project.toml +++ b/Project.toml @@ -41,7 +41,7 @@ Random = "1" SafeTestsets = "0.1" ScopedValues = "1.3.0" Strided = "2" -TensorKitSectors = "0.3.1" +TensorKitSectors = "=0.3.0, 0.3.2" TensorOperations = "5.1" Test = "1" TestExtras = "0.2,0.3" diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index 7155ab220..687c79acd 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -268,18 +268,17 @@ function type_repr(::Type{<:GradedSpace{ProductSector{T}}}) where end # Specific constructors for Z_N -const ZNSpace{N, T} = GradedSpace{ZNIrrep{N, T}, NTuple{N, Int}} -ZNSpace{N}(dims; dual::Bool = false) where {N} = Rep[ℤ{N}](dims; dual) -ZNSpace{N, T}(dims::NTuple{N, Int}; dual::Bool = false) where {T, N} = ZNSpace{N, T}(dims, dual) -ZNSpace{N, T}(dims::Vararg{Int, N}; dual::Bool = false) where {T, N} = ZNSpace{N, T}(dims, dual) -ZNSpace(dims::NTuple{N, Int}; dual::Bool = false) where {N} = Rep[ℤ{N}](dims, dual) -ZNSpace(dims::Vararg{Int, N}; dual::Bool = false) where {N} = Rep[ℤ{N}](dims, dual) +const ZNSpace{N} = GradedSpace{ZNIrrep{N}, NTuple{N, Int}} +ZNSpace{N}(dims::NTuple{N, Int}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +ZNSpace{N}(dims::Vararg{Int, N}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +ZNSpace(dims::NTuple{N, Int}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) +ZNSpace(dims::Vararg{Int, N}; dual::Bool = false) where {N} = ZNSpace{N}(dims, dual) # TODO: Do we still need all of those # ASCII type aliases -const Z2Space = ZNSpace{2, UInt8} -const Z3Space = ZNSpace{3, UInt8} -const Z4Space = ZNSpace{4, UInt8} +const Z2Space = ZNSpace{2} +const Z3Space = ZNSpace{3} +const Z4Space = ZNSpace{4} const U1Space = Rep[U₁] const CU1Space = Rep[CU₁] const SU2Space = Rep[SU₂]