From 744057c0534a34f08fa9dac785a3bd29b2ef8683 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Sun, 5 May 2024 17:59:47 +0200 Subject: [PATCH 1/9] Refactor some space checks and homspace manipulations --- src/spaces/homspace.jl | 13 +++++++++++++ src/tensors/indexmanipulations.jl | 31 +++++++++++++------------------ src/tensors/linalg.jl | 7 +++---- src/tensors/tensoroperations.jl | 18 +++++------------- test/spaces.jl | 3 +++ 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index abcfb5ef..5f6d62a1 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -115,3 +115,16 @@ function dim(W::HomSpace) end return d end + +# Operations on HomSpaces +# ----------------------- +function permute(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁,N₂}) where {S,N₁,N₂} + cod = ProductSpace{S,N₁}(map(n -> W[n], p₁)) + dom = ProductSpace{S,N₂}(map(n -> dual(W[n]), p₂)) + return cod ← dom +end + +function Base.:*(W::HomSpace{S}, V::HomSpace{S}) where {S} + domain(W) == codomain(V) || throw(SpaceMismatch("$(domain(W)) ≠ $(codomain(V))")) + return HomSpace(codomain(W), domain(V)) +end diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index dccb38a2..dea30cdd 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -30,19 +30,19 @@ To permute into an existing destination, see [permute!](@ref) and [`add_permute! """ function permute(t::AbstractTensorMap{S}, (p₁, p₂)::Index2Tuple{N₁,N₂}; copy::Bool=false) where {S,N₁,N₂} - cod = ProductSpace{S,N₁}(map(n -> space(t, n), p₁)) - dom = ProductSpace{S,N₂}(map(n -> dual(space(t, n)), p₂)) + space′ = permute(space(t), (p₁, p₂)) # share data if possible if !copy if p₁ === codomainind(t) && p₂ === domainind(t) return t elseif has_shared_permute(t, (p₁, p₂)) - return TensorMap(reshape(t.data, dim(cod), dim(dom)), cod, dom) + return TensorMap(reshape(t.data, dim(codomain(space′)), dim(domain(space′))), + codomain(space′), domain(space′)) end end # general case @inbounds begin - return permute!(similar(t, cod ← dom), t, (p₁, p₂)) + return permute!(similar(t, space′), t, (p₁, p₂)) end end function permute(t::AdjointTensorMap{S}, (p₁, p₂)::Index2Tuple; @@ -118,10 +118,9 @@ function braid(t::AbstractTensorMap{S}, (p₁, p₂)::Index2Tuple, levels::Index return t end # general case - cod = ProductSpace{S}(map(n -> space(t, n), p₁)) - dom = ProductSpace{S}(map(n -> dual(space(t, n)), p₂)) + space′ = permute(space(t), (p₁, p₂)) @inbounds begin - return braid!(similar(t, cod ← dom), t, (p₁, p₂), levels) + return braid!(similar(t, space′), t, (p₁, p₂), levels) end end # TODO: braid for `AdjointTensorMap`; think about how to map the `levels` argument. @@ -171,10 +170,9 @@ function LinearAlgebra.transpose(t::AbstractTensorMap{S}, return t end # general case - cod = ProductSpace{S}(map(n -> space(t, n), p₁)) - dom = ProductSpace{S}(map(n -> dual(space(t, n)), p₂)) + space′ = permute(space(t), (p₁, p₂)) @inbounds begin - return transpose!(similar(t, cod ← dom), t, (p₁, p₂)) + return transpose!(similar(t, space′), t, (p₁, p₂)) end end @@ -313,23 +311,20 @@ function add_transform!(tdst::AbstractTensorMap{S,N₁,N₂}, β::Number, backend::Backend...) where {S,N₁,N₂} @boundscheck begin - all(i -> space(tsrc, p₁[i]) == space(tdst, i), 1:N₁) || - throw(SpaceMismatch("source = $(codomain(tsrc))←$(domain(tsrc)), - dest = $(codomain(tdst))←$(domain(tdst)), p₁ = $(p₁), p₂ = $(p₂)")) - all(i -> space(tsrc, p₂[i]) == space(tdst, N₁ + i), 1:N₂) || + permute(space(tsrc), (p₁, p₂)) == space(tdst) || throw(SpaceMismatch("source = $(codomain(tsrc))←$(domain(tsrc)), dest = $(codomain(tdst))←$(domain(tdst)), p₁ = $(p₁), p₂ = $(p₂)")) end I = sectortype(S) if p₁ == codomainind(tsrc) && p₂ == domainind(tsrc) - add!(tdst, tsrc, α, β) + @inbounds add!(tdst, tsrc, α, β) elseif I === Trivial - _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) + @inbounds _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) elseif FusionStyle(I) isa UniqueFusion - _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) + @inbounds _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) else - _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) + @inbounds _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) end return tdst end diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 4678b780..7c75a932 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -20,7 +20,7 @@ LinearAlgebra.normalize(t::AbstractTensorMap, p::Real=2) = scale(t, inv(norm(t, function Base.:*(t1::AbstractTensorMap, t2::AbstractTensorMap) return mul!(similar(t1, promote_type(scalartype(t1), scalartype(t2)), - codomain(t1) ← domain(t2)), t1, t2) + space(t1) * space(t2)), t1, t2) end Base.exp(t::AbstractTensorMap) = exp!(copy(t)) function Base.:^(t::AbstractTensorMap, p::Integer) @@ -242,10 +242,9 @@ end function LinearAlgebra.mul!(tC::AbstractTensorMap, tA::AbstractTensorMap, tB::AbstractTensorMap, α=true, β=false) - if !(codomain(tC) == codomain(tA) && domain(tC) == domain(tB) && - domain(tA) == codomain(tB)) + space(tA) * space(tB) == space(tC) || throw(SpaceMismatch("$(space(tC)) ≠ $(space(tA)) * $(space(tB))")) - end + for c in blocksectors(tC) if hasblock(tA, c) # then also tB should have such a block A = block(tA, c) diff --git a/src/tensors/tensoroperations.jl b/src/tensors/tensoroperations.jl index 9f8ed5a9..ed084a4d 100644 --- a/src/tensors/tensoroperations.jl +++ b/src/tensors/tensoroperations.jl @@ -59,9 +59,7 @@ end function TO.tensoradd_structure(pC::Index2Tuple{N₁,N₂}, A::AbstractTensorMap{S}, conjA::Symbol) where {S,N₁,N₂} if conjA == :N - cod = ProductSpace{S,N₁}(space.(Ref(A), pC[1])) - dom = ProductSpace{S,N₂}(dual.(space.(Ref(A), pC[2]))) - return dom → cod + return permute(space(A), pC) else return TO.tensoradd_structure(adjointtensorindices(A, pC), adjoint(A), :N) end @@ -128,12 +126,9 @@ function TO.tensorcontract_structure(pC::Index2Tuple{N₁,N₂}, A::AbstractTensorMap{S}, pA::Index2Tuple, conjA, B::AbstractTensorMap{S}, pB::Index2Tuple, conjB) where {S,N₁,N₂} - spaces1 = TO.flag2op(conjA).(space.(Ref(A), pA[1])) - spaces2 = TO.flag2op(conjB).(space.(Ref(B), pB[2])) - spaces = (spaces1..., spaces2...) - cod = ProductSpace{S,N₁}(getindex.(Ref(spaces), pC[1])) - dom = ProductSpace{S,N₂}(dual.(getindex.(Ref(spaces), pC[2]))) - return dom → cod + sA = TO.tensoradd_structure(pA, A, conjA) + sB = TO.tensoradd_structure(pB, B, conjB) + return permute(sA * sB, pC) end function TO.checkcontractible(tA::AbstractTensorMap{S}, iA::Int, conjA::Symbol, @@ -165,10 +160,7 @@ function trace_permute!(tdst::AbstractTensorMap{S,N₁,N₂}, throw(SectorMismatch("only tensors with symmetric braiding rules can be contracted; try `@planar` instead")) end @boundscheck begin - all(i -> space(tsrc, p₁[i]) == space(tdst, i), 1:N₁) || - throw(SpaceMismatch("trace: tsrc = $(codomain(tsrc))←$(domain(tsrc)), - tdst = $(codomain(tdst))←$(domain(tdst)), p₁ = $(p₁), p₂ = $(p₂)")) - all(i -> space(tsrc, p₂[i]) == space(tdst, N₁ + i), 1:N₂) || + space(tdst) == permute(space(tsrc), (p₁, p₂)) || throw(SpaceMismatch("trace: tsrc = $(codomain(tsrc))←$(domain(tsrc)), tdst = $(codomain(tdst))←$(domain(tdst)), p₁ = $(p₁), p₂ = $(p₂)")) all(i -> space(tsrc, q₁[i]) == dual(space(tsrc, q₂[i])), 1:N₃) || diff --git a/test/spaces.jl b/test/spaces.jl index e2171d0a..48b9e51d 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -410,5 +410,8 @@ println("------------------------------------") @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 W * W' end end From c2047679449006cd0a02b7c8720805de27c2b573 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Sun, 5 May 2024 18:03:13 +0200 Subject: [PATCH 2/9] Add ComplexSpace and CartesianSpace constructors for generators --- src/spaces/cartesianspace.jl | 1 + src/spaces/complexspace.jl | 1 + 2 files changed, 2 insertions(+) diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index 87822d59..dbe1a534 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -27,6 +27,7 @@ function CartesianSpace(dims::AbstractDict; kwargs...) throw(SectorMismatch(msg)) end end +CartesianSpace(g::Base.Generator; kwargs...) = CartesianSpace(g...; kwargs...) field(::Type{CartesianSpace}) = ℝ InnerProductStyle(::Type{CartesianSpace}) = EuclideanProduct() diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index a9929f9a..d266744b 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -28,6 +28,7 @@ function ComplexSpace(dims::AbstractDict; kwargs...) throw(SectorMismatch(msg)) end end +ComplexSpace(g::Base.Generator; kwargs...) = ComplexSpace(g...; kwargs...) field(::Type{ComplexSpace}) = ℂ InnerProductStyle(::Type{ComplexSpace}) = EuclideanProduct() From 4427596e123e9b1e8aaed56065a4dc0db5773e12 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Sun, 5 May 2024 18:05:13 +0200 Subject: [PATCH 3/9] Formatter --- src/tensors/indexmanipulations.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index dea30cdd..0ae30c7f 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -320,11 +320,14 @@ function add_transform!(tdst::AbstractTensorMap{S,N₁,N₂}, if p₁ == codomainind(tsrc) && p₂ == domainind(tsrc) @inbounds add!(tdst, tsrc, α, β) elseif I === Trivial - @inbounds _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) + @inbounds _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, + backend...) elseif FusionStyle(I) isa UniqueFusion - @inbounds _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) + @inbounds _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, + backend...) else - @inbounds _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) + @inbounds _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, + backend...) end return tdst end From 260626d3439240289061ceb7b2b3e4d20e0ee7d5 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 6 May 2024 08:25:47 +0200 Subject: [PATCH 4/9] Retry `@inbounds` implementation --- src/tensors/indexmanipulations.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index 0ae30c7f..ce2fd599 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -318,15 +318,15 @@ function add_transform!(tdst::AbstractTensorMap{S,N₁,N₂}, I = sectortype(S) if p₁ == codomainind(tsrc) && p₂ == domainind(tsrc) - @inbounds add!(tdst, tsrc, α, β) + add!(tdst, tsrc, α, β) elseif I === Trivial - @inbounds _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, + _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) elseif FusionStyle(I) isa UniqueFusion - @inbounds _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, + _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) else - @inbounds _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, + _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) end return tdst @@ -355,7 +355,7 @@ end function _add_abelian_block!(tdst, tsrc, p, fusiontreetransform, f₁, f₂, α, β, backend...) (f₁′, f₂′), coeff = first(fusiontreetransform(f₁, f₂)) - TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, β, backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, β, backend...) return nothing end @@ -373,7 +373,7 @@ function _add_general_kernel!(tdst, tsrc, p, fusiontreetransform, α, β, backen else for (f₁, f₂) in fusiontrees(tsrc) for ((f₁′, f₂′), coeff) in fusiontreetransform(f₁, f₂) - TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, backend...) end end @@ -386,7 +386,7 @@ function _add_nonabelian_sector!(tdst, tsrc, p, fusiontreetransform, s₁, s₂, for (f₁, f₂) in fusiontrees(tsrc) (f₁.uncoupled == s₁ && f₂.uncoupled == s₂) || continue for ((f₁′, f₂′), coeff) in fusiontreetransform(f₁, f₂) - TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, backend...) end end return nothing From db85d6d346b856fa5f08f6d2548069a9c21ec560 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 6 May 2024 08:32:57 +0200 Subject: [PATCH 5/9] Rename `*(HomSpace...)` to `compose` --- src/spaces/homspace.jl | 8 +++++++- src/tensors/linalg.jl | 4 ++-- src/tensors/tensoroperations.jl | 2 +- test/spaces.jl | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 5f6d62a1..c1c4e6af 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -124,7 +124,13 @@ function permute(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁,N₂}) where {S, return cod ← dom end -function Base.:*(W::HomSpace{S}, V::HomSpace{S}) where {S} +""" + compose(W::HomSpace, V::HomSpace) + +Obtain the HomSpace that is obtained from composing the morphisms in `W` and `V`. For this +to be possible, the domain of `W` must match the codomain of `V`. +""" +function compose(W::HomSpace{S}, V::HomSpace{S}) where {S} domain(W) == codomain(V) || throw(SpaceMismatch("$(domain(W)) ≠ $(codomain(V))")) return HomSpace(codomain(W), domain(V)) end diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 7c75a932..e00b1ee7 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -20,7 +20,7 @@ LinearAlgebra.normalize(t::AbstractTensorMap, p::Real=2) = scale(t, inv(norm(t, function Base.:*(t1::AbstractTensorMap, t2::AbstractTensorMap) return mul!(similar(t1, promote_type(scalartype(t1), scalartype(t2)), - space(t1) * space(t2)), t1, t2) + compose(space(t1), space(t2))), t1, t2) end Base.exp(t::AbstractTensorMap) = exp!(copy(t)) function Base.:^(t::AbstractTensorMap, p::Integer) @@ -242,7 +242,7 @@ end function LinearAlgebra.mul!(tC::AbstractTensorMap, tA::AbstractTensorMap, tB::AbstractTensorMap, α=true, β=false) - space(tA) * space(tB) == space(tC) || + compose(space(tA), space(tB)) == space(tC) || throw(SpaceMismatch("$(space(tC)) ≠ $(space(tA)) * $(space(tB))")) for c in blocksectors(tC) diff --git a/src/tensors/tensoroperations.jl b/src/tensors/tensoroperations.jl index ed084a4d..a2b6f171 100644 --- a/src/tensors/tensoroperations.jl +++ b/src/tensors/tensoroperations.jl @@ -128,7 +128,7 @@ function TO.tensorcontract_structure(pC::Index2Tuple{N₁,N₂}, conjB) where {S,N₁,N₂} sA = TO.tensoradd_structure(pA, A, conjA) sB = TO.tensoradd_structure(pB, B, conjB) - return permute(sA * sB, pC) + return permute(compose(sA, sB), pC) end function TO.checkcontractible(tA::AbstractTensorMap{S}, iA::Int, conjA::Symbol, diff --git a/test/spaces.jl b/test/spaces.jl index 48b9e51d..42f4b922 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -412,6 +412,6 @@ println("------------------------------------") @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 W * W' + @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred compose(W, W') end end From d3e7f72f208d65bcb595e4c76455199be3a2b375 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 6 May 2024 08:36:00 +0200 Subject: [PATCH 6/9] Add promote call for `fuse` --- src/spaces/vectorspaces.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index b2b7a454..4155e743 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -160,6 +160,7 @@ individual spaces `V₁`, `V₂`, ..., or the spaces contained in `P`. """ function fuse end fuse(V::ElementarySpace) = isdual(V) ? flip(V) : V +fuse(V::ElementarySpace, W::ElementarySpace) = fuse(promote(V, W)...) function fuse(V₁::VectorSpace, V₂::VectorSpace, V₃::VectorSpace...) return fuse(fuse(fuse(V₁), fuse(V₂)), V₃...) end From 17236639a922f830220eb00aab9a90af9675eadf Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 6 May 2024 08:37:58 +0200 Subject: [PATCH 7/9] Formatter --- src/tensors/indexmanipulations.jl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index ce2fd599..0c202245 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -320,14 +320,11 @@ function add_transform!(tdst::AbstractTensorMap{S,N₁,N₂}, if p₁ == codomainind(tsrc) && p₂ == domainind(tsrc) add!(tdst, tsrc, α, β) elseif I === Trivial - _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, - backend...) + _add_trivial_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) elseif FusionStyle(I) isa UniqueFusion - _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, - backend...) + _add_abelian_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) else - _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, - backend...) + _add_general_kernel!(tdst, tsrc, (p₁, p₂), fusiontreetransform, α, β, backend...) end return tdst end @@ -373,8 +370,8 @@ function _add_general_kernel!(tdst, tsrc, p, fusiontreetransform, α, β, backen else for (f₁, f₂) in fusiontrees(tsrc) for ((f₁′, f₂′), coeff) in fusiontreetransform(f₁, f₂) - @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, - backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, + true, backend...) end end end @@ -386,7 +383,8 @@ function _add_nonabelian_sector!(tdst, tsrc, p, fusiontreetransform, s₁, s₂, for (f₁, f₂) in fusiontrees(tsrc) (f₁.uncoupled == s₁ && f₂.uncoupled == s₂) || continue for ((f₁′, f₂′), coeff) in fusiontreetransform(f₁, f₂) - @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, backend...) + @inbounds TO.tensoradd!(tdst[f₁′, f₂′], p, tsrc[f₁, f₂], :N, α * coeff, true, + backend...) end end return nothing From 247335ef067df8e767f78516a39a4746d8801fa8 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 6 May 2024 09:35:49 +0200 Subject: [PATCH 8/9] Fix unexported function name --- test/spaces.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spaces.jl b/test/spaces.jl index 42f4b922..5b591bca 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -412,6 +412,6 @@ println("------------------------------------") @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 compose(W, W') + @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TensorKit.compose(W, W') end end From 424d4fe81ad1e3a4a9c0bc77c64c5ad2b82fe857 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Tue, 7 May 2024 15:22:28 +0200 Subject: [PATCH 9/9] Add `compose(AbstractTensorMap, AbstractTensorMap)` --- src/tensors/linalg.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index e00b1ee7..46bbb285 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -18,10 +18,18 @@ Base.:\(α::Number, t::AbstractTensorMap) = *(t, one(scalartype(t)) / α) LinearAlgebra.normalize!(t::AbstractTensorMap, p::Real=2) = scale!(t, inv(norm(t, p))) LinearAlgebra.normalize(t::AbstractTensorMap, p::Real=2) = scale(t, inv(norm(t, p))) -function Base.:*(t1::AbstractTensorMap, t2::AbstractTensorMap) +""" + compose(t1::AbstractTensorMap, t2::AbstractTensorMap) -> AbstractTensorMap + +Return the `AbstractTensorMap` that implements the composition of the two tensor maps `t1` +and `t2`. +""" +function compose(t1::AbstractTensorMap, t2::AbstractTensorMap) return mul!(similar(t1, promote_type(scalartype(t1), scalartype(t2)), compose(space(t1), space(t2))), t1, t2) end +Base.:*(t1::AbstractTensorMap, t2::AbstractTensorMap) = compose(t1, t2) + Base.exp(t::AbstractTensorMap) = exp!(copy(t)) function Base.:^(t::AbstractTensorMap, p::Integer) return p < 0 ? Base.power_by_squaring(inv(t), -p) : Base.power_by_squaring(t, p)