diff --git a/Project.toml b/Project.toml index d5f94a7..d8cf8f5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FusionTensors" uuid = "e16ca583-1f51-4df0-8e12-57d32947d33e" authors = ["ITensor developers and contributors"] -version = "0.5.1" +version = "0.5.2" [deps] Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" diff --git a/src/fusiontensor/base_interface.jl b/src/fusiontensor/base_interface.jl index 4def6f8..ea3241d 100644 --- a/src/fusiontensor/base_interface.jl +++ b/src/fusiontensor/base_interface.jl @@ -73,14 +73,19 @@ function Base.getindex(ft::FusionTensor, f1::SectorFusionTree, f2::SectorFusionT return reshape(charge_matrix, charge_block_size(ft, f1, f2)) end +Base.imag(ft::FusionTensor) = set_data_matrix(ft, imag(data_matrix(ft))) + +Base.permutedims(ft::FusionTensor, args...) = fusiontensor_permutedims(ft, args...) + +Base.real(ft::FusionTensor{<:Real}) = ft # same object +Base.real(ft::FusionTensor) = set_data_matrix(ft, real(data_matrix(ft))) + function Base.setindex!( ft::FusionTensor, a::AbstractArray, f1::SectorFusionTree, f2::SectorFusionTree ) return view(ft, f1, f2) .= a end -Base.permutedims(ft::FusionTensor, args...) = fusiontensor_permutedims(ft, args...) - function Base.similar(ft::FusionTensor, T::Type) # reuse trees_block_mapping diff --git a/test/test_basics.jl b/test/test_basics.jl index 295cfc0..a575664 100644 --- a/test/test_basics.jl +++ b/test/test_basics.jl @@ -28,7 +28,7 @@ using GradedArrays: space_isequal using TensorAlgebra: tuplemortar using TensorProducts: tensor_product -using LinearAlgebra: LinearAlgebra +using LinearAlgebra: LinearAlgebra, norm using Random: Random include("setup.jl") @@ -184,12 +184,53 @@ end @test sector_type(ft3) === TrivialSector end +@testset "specific constructors" begin + g1 = gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 3]) + g2 = gradedrange([U1(0) => 2, U1(1) => 2, U1(3) => 1]) + g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1]) + g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1]) + + fta = FusionTensorAxes((g1,), (g2, g3)) + @test zeros(fta) isa FusionTensor{Float64,3} + @test zeros(ComplexF64, fta) isa FusionTensor{ComplexF64,3} + + rng = Random.default_rng() + ft1 = randn(rng, ComplexF64, fta) + @test ft1 isa FusionTensor{ComplexF64,3} + @test all(!=(0), data_matrix(ft1)[Block(1, 5)]) + @test randn(rng, fta) isa FusionTensor{Float64,3} + @test randn(ComplexF64, fta) isa FusionTensor{ComplexF64,3} + @test randn(fta) isa FusionTensor{Float64,3} + + ft2 = FusionTensor(LinearAlgebra.I, (g1, g2)) + @test ft2 isa FusionTensor{Float64,4} + @test axes(ft2) == FusionTensorAxes((g1, g2), dual.((g1, g2))) + @test collect(eachblockstoredindex(data_matrix(ft2))) == map(i -> Block(i, i), 1:6) + for i in 1:6 + m = data_matrix(ft2)[Block(i, i)] + @test m == LinearAlgebra.I(size(m, 1)) + end + + ft2 = FusionTensor(3 * LinearAlgebra.I, (g1, g2)) + @test ft2 isa FusionTensor{Float64,4} + @test axes(ft2) == FusionTensorAxes((g1, g2), dual.((g1, g2))) + @test collect(eachblockstoredindex(data_matrix(ft2))) == map(i -> Block(i, i), 1:6) + for i in 1:6 + m = data_matrix(ft2)[Block(i, i)] + @test m == 3 * LinearAlgebra.I(size(m, 1)) + end + + @test FusionTensor{ComplexF64}(LinearAlgebra.I, (g1, g2)) isa FusionTensor{ComplexF64,4} +end + @testset "Base operations" begin g1 = gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 3]) g2 = gradedrange([U1(0) => 2, U1(1) => 2, U1(3) => 1]) g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1]) g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1]) - ft3 = FusionTensor{Float64}(undef, (g1, g2), (g3, g4)) + ft3 = randn(FusionTensorAxes((g1, g2), (g3, g4))) + @test ft3 isa FusionTensor{Float64,4} + @test norm(ft3) ≉ 0 @test isnothing(check_sanity(ft3)) ft4 = +ft3 @@ -199,6 +240,8 @@ end @test isnothing(check_sanity(ft4)) @test codomain_axes(ft4) === codomain_axes(ft3) @test domain_axes(ft4) === domain_axes(ft3) + @test norm(ft4) ≈ norm(ft3) + @test norm(ft4 + ft3) ≈ 0.0 ft4 = ft3 + ft3 @test codomain_axes(ft4) === codomain_axes(ft3) @@ -206,6 +249,7 @@ end @test space_isequal(codomain_axis(ft4), codomain_axis(ft3)) @test space_isequal(domain_axis(ft4), domain_axis(ft3)) @test isnothing(check_sanity(ft4)) + @test norm(ft4) ≈ 2norm(ft3) ft4 = ft3 - ft3 @test codomain_axes(ft4) === codomain_axes(ft3) @@ -213,6 +257,7 @@ end @test space_isequal(codomain_axis(ft4), codomain_axis(ft3)) @test space_isequal(domain_axis(ft4), domain_axis(ft3)) @test isnothing(check_sanity(ft4)) + @test norm(ft4) ≈ 0.0 ft4 = 2 * ft3 @test codomain_axes(ft4) === codomain_axes(ft3) @@ -221,6 +266,7 @@ end @test space_isequal(domain_axis(ft4), domain_axis(ft3)) @test isnothing(check_sanity(ft4)) @test eltype(ft4) == Float64 + @test norm(ft4) ≈ 2norm(ft3) ft4 = 2.0 * ft3 @test codomain_axes(ft4) === codomain_axes(ft3) @@ -237,17 +283,27 @@ end @test space_isequal(domain_axis(ft4), domain_axis(ft3)) @test isnothing(check_sanity(ft4)) @test eltype(ft4) == Float64 + @test norm(ft4) ≈ norm(ft3) / 2.0 - ft5 = 2.0im * ft3 + ft5 = (1.0 + 2.0im) * ft3 @test codomain_axes(ft5) === codomain_axes(ft3) @test domain_axes(ft5) === domain_axes(ft3) @test space_isequal(codomain_axis(ft5), codomain_axis(ft3)) @test space_isequal(domain_axis(ft5), domain_axis(ft3)) @test isnothing(check_sanity(ft4)) @test eltype(ft5) == ComplexF64 + @test norm(ft5) ≈ √5 * norm(ft3) - ft4 = conj(ft3) - @test ft4 === ft3 # same object + @test conj(ft3) === ft3 # same object + @test real(ft3) === ft3 + @test norm(imag(ft3)) == 0 + + @test conj(ft5) isa FusionTensor{ComplexF64,4} + @test real(ft5) isa FusionTensor{Float64,4} + @test imag(ft3) isa FusionTensor{Float64,4} + @test conj(ft5) ≈ (1.0 - 2.0im) * ft3 + @test real(ft5) ≈ ft3 + @test imag(ft5) ≈ 2ft3 ft6 = conj(ft5) @test ft6 !== ft5 # different object @@ -257,6 +313,7 @@ end @test space_isequal(codomain_axis(ft6), codomain_axis(ft5)) @test space_isequal(domain_axis(ft6), domain_axis(ft5)) @test eltype(ft6) == ComplexF64 + @test ft6 + ft5 ≈ 2 * real(ft5) ad = adjoint(ft3) @test ad isa FusionTensor @@ -274,45 +331,6 @@ end @test_throws ArgumentError ft7 * ft3 end -@testset "specific constructors" begin - g1 = gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 3]) - g2 = gradedrange([U1(0) => 2, U1(1) => 2, U1(3) => 1]) - g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1]) - g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1]) - - fta = FusionTensorAxes((g1,), (g2, g3)) - @test zeros(fta) isa FusionTensor{Float64,3} - @test zeros(ComplexF64, fta) isa FusionTensor{ComplexF64,3} - - rng = Random.default_rng() - ft1 = randn(rng, ComplexF64, fta) - @test ft1 isa FusionTensor{ComplexF64,3} - @test all(!=(0), data_matrix(ft1)[Block(1, 5)]) - @test randn(rng, fta) isa FusionTensor{Float64,3} - @test randn(ComplexF64, fta) isa FusionTensor{ComplexF64,3} - @test randn(fta) isa FusionTensor{Float64,3} - - ft2 = FusionTensor(LinearAlgebra.I, (g1, g2)) - @test ft2 isa FusionTensor{Float64,4} - @test axes(ft2) == FusionTensorAxes((g1, g2), dual.((g1, g2))) - @test collect(eachblockstoredindex(data_matrix(ft2))) == map(i -> Block(i, i), 1:6) - for i in 1:6 - m = data_matrix(ft2)[Block(i, i)] - @test m == LinearAlgebra.I(size(m, 1)) - end - - ft2 = FusionTensor(3 * LinearAlgebra.I, (g1, g2)) - @test ft2 isa FusionTensor{Float64,4} - @test axes(ft2) == FusionTensorAxes((g1, g2), dual.((g1, g2))) - @test collect(eachblockstoredindex(data_matrix(ft2))) == map(i -> Block(i, i), 1:6) - for i in 1:6 - m = data_matrix(ft2)[Block(i, i)] - @test m == 3 * LinearAlgebra.I(size(m, 1)) - end - - @test FusionTensor{ComplexF64}(LinearAlgebra.I, (g1, g2)) isa FusionTensor{ComplexF64,4} -end - @testset "missing SectorProduct" begin g1 = gradedrange([SectorProduct(U1(1)) => 1]) g2 = gradedrange([SectorProduct(U1(1), SU2(1//2)) => 1])