From 21636f954a2e66ff9f37c50909650a3eed2b04b0 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 14:37:10 +0200 Subject: [PATCH 01/20] change TensorKitSectors compat --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ab7d6ed0..aacd69f6 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ PackageExtensionCompat = "1" Random = "1" ScopedValues = "1.3.0" Strided = "2" -TensorKitSectors = "0.1.4, 0.2" +TensorKitSectors = "0.3" TensorOperations = "5.1" Test = "1" TestExtras = "0.2,0.3" From 755f56fb911b73d0d3d267b74736b91562b63460 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 14:43:28 +0200 Subject: [PATCH 02/20] add `unitspace` --- docs/src/lib/spaces.md | 2 +- docs/src/man/spaces.md | 17 ++++++++--------- src/spaces/cartesianspace.jl | 2 +- src/spaces/complexspace.jl | 2 +- src/spaces/generalspace.jl | 2 +- src/spaces/gradedspace.jl | 6 +++--- src/spaces/productspace.jl | 25 ++++++++++++------------- src/spaces/vectorspaces.jl | 10 ++++++---- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/src/lib/spaces.md b/docs/src/lib/spaces.md index 205301d8..6f60cda4 100644 --- a/docs/src/lib/spaces.md +++ b/docs/src/lib/spaces.md @@ -92,7 +92,7 @@ flip ⊕ ⊖ zero(::ElementarySpace) -oneunit +unitspace supremum infimum ``` diff --git a/docs/src/man/spaces.md b/docs/src/man/spaces.md index 503730bf..ff2d5990 100644 --- a/docs/src/man/spaces.md +++ b/docs/src/man/spaces.md @@ -316,19 +316,18 @@ flip(ℂ^4) == ℂ^4 ``` We also define the direct sum `V1` and `V2` as `V1 ⊕ V2`, where `⊕` is obtained by typing -`\oplus`+TAB. This is possible only if `isdual(V1) == isdual(V2)`. With a little pun on -Julia Base, `oneunit` applied to an elementary space (in the value or type domain) returns -the one-dimensional space, which is isomorphic to the scalar field of the space itself. Some -examples illustrate this better +`\oplus`+TAB. This is possible only if `isdual(V1) == isdual(V2)`. `unitspace` applied to an elementary space +(in the value or type domain) returns the one-dimensional space, which is isomorphic to the +scalar field of the space itself. Some examples illustrate this better. ```@repl tensorkit ℝ^5 ⊕ ℝ^3 ℂ^5 ⊕ ℂ^3 ℂ^5 ⊕ (ℂ^3)' -oneunit(ℝ^3) -ℂ^5 ⊕ oneunit(ComplexSpace) -oneunit((ℂ^3)') -(ℂ^5) ⊕ oneunit((ℂ^5)) -(ℂ^5)' ⊕ oneunit((ℂ^5)') +unitspace(ℝ^3) +ℂ^5 ⊕ unitspace(ComplexSpace) +unitspace((ℂ^3)') +(ℂ^5) ⊕ unitspace((ℂ^5)) +(ℂ^5)' ⊕ unitspace((ℂ^5)') ``` Finally, while spaces have a partial order, there is no unique infimum or supremum of a two diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index 778fa498..5f7d60c7 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -47,7 +47,7 @@ hassector(V::CartesianSpace, ::Trivial) = dim(V) != 0 sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{CartesianSpace}) = Trivial -Base.oneunit(::Type{CartesianSpace}) = CartesianSpace(1) +unitspace(::Type{CartesianSpace}) = CartesianSpace(1) Base.zero(::Type{CartesianSpace}) = CartesianSpace(0) ⊕(V₁::CartesianSpace, V₂::CartesianSpace) = CartesianSpace(V₁.d + V₂.d) diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index 00e4f2a3..ee3498f0 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -48,7 +48,7 @@ sectortype(::Type{ComplexSpace}) = Trivial Base.conj(V::ComplexSpace) = ComplexSpace(dim(V), !isdual(V)) -Base.oneunit(::Type{ComplexSpace}) = ComplexSpace(1) +unitspace(::Type{ComplexSpace}) = ComplexSpace(1) Base.zero(::Type{ComplexSpace}) = ComplexSpace(0) function ⊕(V₁::ComplexSpace, V₂::ComplexSpace) diff --git a/src/spaces/generalspace.jl b/src/spaces/generalspace.jl index fa9db1ba..d1f84d8f 100644 --- a/src/spaces/generalspace.jl +++ b/src/spaces/generalspace.jl @@ -35,7 +35,7 @@ sectortype(::Type{<:GeneralSpace}) = Trivial field(::Type{GeneralSpace{𝔽}}) where {𝔽} = 𝔽 InnerProductStyle(::Type{<:GeneralSpace}) = NoInnerProduct() -Base.oneunit(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) +unitspace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) Base.zero(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V), isconj(V)) diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index d55a71d6..8cfff0b3 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -133,9 +133,9 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} end return (offset + 1):(offset + dim(c) * dim(V, c)) end - -Base.oneunit(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(one(I) => 1) -Base.zero(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(one(I) => 0) +#TODO: one -> unit +unitspace(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(one(I) => 1) +Base.zero(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(one(I) => 0) # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 0911a5f5..2af9bb0c 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -230,6 +230,7 @@ end Return a tensor product of zero spaces of type `S`, i.e. this is the unit object under the tensor product operation, such that `V ⊗ one(V) == V`. """ +#TODO: unit(V::S)? Base.one(V::VectorSpace) = one(typeof(V)) Base.one(::Type{<:ProductSpace{S}}) where {S <: ElementarySpace} = ProductSpace{S, 0}(()) Base.one(::Type{S}) where {S <: ElementarySpace} = ProductSpace{S, 0}(()) @@ -240,8 +241,8 @@ function Base.literal_pow(::typeof(^), V::ElementarySpace, p::Val{N}) where {N} return ProductSpace{typeof(V), N}(ntuple(n -> V, p)) end -fuse(P::ProductSpace{S, 0}) where {S <: ElementarySpace} = oneunit(S) -fuse(P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) +fuse(P::ProductSpace{S,0}) where {S<:ElementarySpace} = unitspace(S) +fuse(P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...) """ insertleftunit(P::ProductSpace, i::Int=length(P) + 1; conj=false, dual=false) @@ -252,10 +253,9 @@ More specifically, adds a left monoidal unit or its dual. See also [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ -function insertleftunit( - P::ProductSpace, ::Val{i} = Val(length(P) + 1); conj::Bool = false, dual::Bool = false - ) where {i} - u = oneunit(spacetype(P)) +function insertleftunit(P::ProductSpace, ::Val{i}=Val(length(P) + 1); + conj::Bool=false, dual::Bool=false) where {i} + u = unitspace(spacetype(P)) if dual u = TensorKit.dual(u) end @@ -274,10 +274,9 @@ More specifically, adds a right monoidal unit or its dual. See also [`insertleftunit`](@ref insertleftunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ -function insertrightunit( - P::ProductSpace, ::Val{i} = Val(length(P)); conj::Bool = false, dual::Bool = false - ) where {i} - u = oneunit(spacetype(P)) +function insertrightunit(P::ProductSpace, ::Val{i}=Val(length(P)); + conj::Bool=false, dual::Bool=false) where {i} + u = unitspace(spacetype(P)) if dual u = TensorKit.dual(u) end @@ -299,7 +298,7 @@ and [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i} """ function removeunit(P::ProductSpace, ::Val{i}) where {i} 1 ≤ i ≤ length(P) || _boundserror(P, i) - isisomorphic(P[i], oneunit(P[i])) || _nontrivialspaceerror(P, i) + isisomorphic(P[i], unitspace(P[i])) || _nontrivialspaceerror(P, i) return ProductSpace{spacetype(P)}(TupleTools.deleteat(P.spaces, i)) end @@ -326,8 +325,8 @@ function Base.promote_rule(::Type{S}, ::Type{<:ProductSpace{S}}) where {S <: Ele end # ProductSpace to ElementarySpace -Base.convert(::Type{S}, P::ProductSpace{S, 0}) where {S <: ElementarySpace} = oneunit(S) -Base.convert(::Type{S}, P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) +Base.convert(::Type{S}, P::ProductSpace{S,0}) where {S<:ElementarySpace} = unitspace(S) +Base.convert(::Type{S}, P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...) # ElementarySpace to ProductSpace Base.convert(::Type{<:ProductSpace}, V::S) where {S <: ElementarySpace} = ⊗(V) diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index b15216aa..6fb59ea9 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -80,7 +80,7 @@ Base.adjoint(V::VectorSpace) = dual(V) """ isdual(V::ElementarySpace) -> Bool -Return wether an ElementarySpace `V` is normal or rather a dual space. Always returns +Return whether an ElementarySpace `V` is normal or rather a dual space. Always returns `false` for spaces where `V == dual(V)`. """ function isdual end @@ -120,14 +120,15 @@ Return the sum of all degeneracy dimensions of the vector space `V`. reduceddim(V::ElementarySpace) = sum(Base.Fix1(dim, V), sectors(V); init = 0) """ - oneunit(V::S) where {S<:ElementarySpace} -> S + unitspace(V::S) where {S<:ElementarySpace} -> S Return the corresponding vector space of type `S` that represents the trivial one-dimensional space, i.e. the space that is isomorphic to the corresponding field. Note that this is different from `one(V::S)`, which returns the empty product space -`ProductSpace{S,0}(())`. +`ProductSpace{S,0}(())`. `Base.oneunit` falls back to `unitspace`. """ -Base.oneunit(V::ElementarySpace) = oneunit(typeof(V)) +unitspace(V::ElementarySpace) = unitspace(typeof(V)) +Base.oneunit(V::ElementarySpace) = unitspace(V) """ zero(V::S) where {S<:ElementarySpace} -> S @@ -135,6 +136,7 @@ Base.oneunit(V::ElementarySpace) = oneunit(typeof(V)) Return the corresponding vector space of type `S` that represents the zero-dimensional or empty space. This is, with a slight abuse of notation, the zero element of the direct sum of vector spaces. """ +#TODO: zerospace? Base.zero(V::ElementarySpace) = zero(typeof(V)) """ From fdf8271ed3e8ec41efa17444cf41dbc6631c294a Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 16:01:20 +0200 Subject: [PATCH 03/20] one -> unit + left/rightone -> left/rightunit + isone -> isunit when concerning sectors + some more unitspace --- src/fusiontrees/fusiontrees.jl | 18 +++++++------- src/fusiontrees/iterator.jl | 8 +++--- src/fusiontrees/manipulations.jl | 22 ++++++++--------- src/spaces/deligne.jl | 8 +++--- src/spaces/gradedspace.jl | 8 +++--- src/spaces/productspace.jl | 22 ++++++++++------- src/tensors/braidingtensor.jl | 8 +++--- src/tensors/linalg.jl | 4 +-- src/tensors/tensor.jl | 4 +-- test/fusiontrees.jl | 6 ++--- test/runtests.jl | 4 +-- test/spaces.jl | 42 ++++++++++++++++---------------- 12 files changed, 79 insertions(+), 75 deletions(-) diff --git a/src/fusiontrees/fusiontrees.jl b/src/fusiontrees/fusiontrees.jl index 419a01c9..0408c64e 100644 --- a/src/fusiontrees/fusiontrees.jl +++ b/src/fusiontrees/fusiontrees.jl @@ -34,7 +34,7 @@ struct FusionTree{I <: Sector, N, M, L} ) where {I <: Sector, N, M, L} # if N == 0 - # @assert coupled == one(coupled) + # @assert coupled == unit(coupled) # elseif N == 1 # @assert coupled == uncoupled[1] # elseif N == 2 @@ -89,7 +89,7 @@ function FusionTree( end function FusionTree{I}( - uncoupled::NTuple{N}, coupled = one(I), isdual = ntuple(n -> false, N) + uncoupled::NTuple{N}, coupled = unit(I), isdual = ntuple(Returns(false), N) ) where {I <: Sector, N} FusionStyle(I) isa UniqueFusion || error("fusion tree requires inner lines if `FusionStyle(I) <: MultipleFusion`") @@ -103,7 +103,7 @@ function FusionTree( ) where {N, I <: Sector} return FusionTree{I}(uncoupled, coupled, isdual) end -FusionTree(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} = FusionTree(uncoupled, one(I)) +FusionTree(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} = FusionTree(uncoupled, unit(I)) # Properties sectortype(::Type{<:FusionTree{I}}) where {I <: Sector} = I @@ -159,17 +159,17 @@ end # converting to actual array function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 0}) where {I} - X = convert(A, fusiontensor(one(I), one(I), one(I)))[1, 1, :] + X = convert(A, fusiontensor(unit(I), unit(I), unit(I)))[1, 1, :] return X end function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 1}) where {I} c = f.coupled if f.isdual[1] - sqrtdc = sqrtdim(c) - Zcbartranspose = sqrtdc * convert(A, fusiontensor(conj(c), c, one(c)))[:, :, 1, 1] + sqrtdc = sqrtdim(c) # TODO: change conj to dual + Zcbartranspose = sqrtdc * convert(A, fusiontensor(conj(c), c, unit(c)))[:, :, 1, 1] X = conj!(Zcbartranspose) # we want Zcbar^† else - X = convert(A, fusiontensor(c, one(c), c))[:, 1, :, 1, 1] + X = convert(A, fusiontensor(c, unit(c), c))[:, 1, :, 1, 1] end return X end @@ -257,13 +257,13 @@ include("iterator.jl") # _abelianinner: generate the inner indices for given outer indices in the abelian case _abelianinner(outer::Tuple{}) = () function _abelianinner(outer::Tuple{I}) where {I <: Sector} - return isone(outer[1]) ? () : throw(SectorMismatch()) + return isunit(outer[1]) ? () : throw(SectorMismatch()) end function _abelianinner(outer::Tuple{I, I}) where {I <: Sector} return outer[1] == dual(outer[2]) ? () : throw(SectorMismatch()) end function _abelianinner(outer::Tuple{I, I, I}) where {I <: Sector} - return isone(first(⊗(outer...))) ? () : throw(SectorMismatch()) + return isunit(first(⊗(outer...))) ? () : throw(SectorMismatch()) end function _abelianinner(outer::Tuple{I, I, I, I, Vararg{I}}) where {I <: Sector} c = first(outer[1] ⊗ outer[2]) diff --git a/src/fusiontrees/iterator.jl b/src/fusiontrees/iterator.jl index 4284b78e..50f0781f 100644 --- a/src/fusiontrees/iterator.jl +++ b/src/fusiontrees/iterator.jl @@ -1,6 +1,6 @@ """ fusiontrees(uncoupled::NTuple{N,I}[, - coupled::I=one(I)[, isdual::NTuple{N,Bool}=ntuple(n -> false, length(uncoupled))]]) + coupled::I=unit(I)[, isdual::NTuple{N,Bool}=ntuple(n -> false, length(uncoupled))]]) where {N,I<:Sector} -> FusionTreeIterator{I,N,I} Return an iterator over all fusion trees with a given coupled sector label `coupled` and @@ -17,7 +17,7 @@ function fusiontrees(uncoupled::Tuple{Vararg{I}}, coupled::I) where {I <: Sector return fusiontrees(uncoupled, coupled, isdual) end function fusiontrees(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} - coupled = one(I) + coupled = unit(I) isdual = ntuple(n -> false, length(uncoupled)) return fusiontrees(uncoupled, coupled, isdual) end @@ -38,7 +38,7 @@ Base.IteratorEltype(::FusionTreeIterator) = Base.HasEltype() Base.eltype(::Type{<:FusionTreeIterator{I, N}}) where {I <: Sector, N} = fusiontreetype(I, N) Base.length(iter::FusionTreeIterator) = _fusiondim(iter.uncouplediterators, iter.coupled) -_fusiondim(::Tuple{}, c::I) where {I <: Sector} = Int(isone(c)) +_fusiondim(::Tuple{}, c::I) where {I <: Sector} = Int(isunit(c)) _fusiondim(iters::NTuple{1}, c::I) where {I <: Sector} = Int(c ∈ iters[1]) function _fusiondim(iters::NTuple{2}, c::I) where {I <: Sector} d = 0 @@ -60,7 +60,7 @@ end # * Iterator methods: # Start with special cases: -function Base.iterate(it::FusionTreeIterator{I, 0}, state = !isone(it.coupled)) where {I <: Sector} +function Base.iterate(it::FusionTreeIterator{I, 0}, state = !isunit(it.coupled)) where {I <: Sector} state && return nothing tree = FusionTree{I}((), it.coupled, (), (), ()) return tree, true diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index 6455e9d1..08688fea 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -165,7 +165,7 @@ operation is the inverse of `insertat` in the sense that if f₂ = FusionTree{I}(f.uncoupled, f.coupled, isdual2, f.innerlines, f.vertices) return f₁, f₂ elseif M === 0 - u = leftone(f.uncoupled[1]) + u = leftunit(f.uncoupled[1]) f₁ = FusionTree{I}((), u, (), ()) uncoupled2 = (u, f.uncoupled...) coupled2 = f.coupled @@ -286,7 +286,7 @@ function bendright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where { # map final splitting vertex (a, b)<-c to fusion vertex a<-(c, dual(b)) @assert N₁ > 0 c = f₁.coupled - a = N₁ == 1 ? leftone(f₁.uncoupled[1]) : + a = N₁ == 1 ? leftunit(f₁.uncoupled[1]) : (N₁ == 2 ? f₁.uncoupled[1] : f₁.innerlines[end]) b = f₁.uncoupled[N₁] @@ -358,7 +358,7 @@ function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where { hasmultiplicities = FusionStyle(a) isa GenericFusion local newtrees if N₁ == 1 - cset = (leftone(c1),) # or rightone(a) + cset = (leftunit(c1),) # or rightunit(a) elseif N₁ == 2 cset = (f₁.uncoupled[2],) else @@ -369,7 +369,7 @@ function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where { for μ in 1:Nsymbol(c1, c2, c) fc = FusionTree((c1, c2), c, (!isduala, false), (), (μ,)) for (fl′, coeff1) in insertat(fc, 2, f₁) - N₁ > 1 && !isone(fl′.innerlines[1]) && continue + N₁ > 1 && !isunit(fl′.innerlines[1]) && continue coupled = fl′.coupled uncoupled = Base.tail(Base.tail(fl′.uncoupled)) isdual = Base.tail(Base.tail(fl′.isdual)) @@ -722,7 +722,7 @@ corresponding coefficients. function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} (N > 1 && 1 <= i <= N) || throw(ArgumentError("Cannot trace outputs i=$i and i+1 out of only $N outputs")) - i < N || isone(f.coupled) || + i < N || isunit(f.coupled) || throw(ArgumentError("Cannot trace outputs i=$N and 1 of fusion tree that couples to non-trivial sector")) T = sectorscalartype(I) @@ -735,7 +735,7 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} # if trace is zero, return empty dict (b == dual(b′) && f.isdual[i] != f.isdual[j]) || return newtrees if i < N - inner_extended = (leftone(f.uncoupled[1]), f.uncoupled[1], f.innerlines..., f.coupled) + inner_extended = (leftunit(f.uncoupled[1]), f.uncoupled[1], f.innerlines..., f.coupled) a = inner_extended[i] d = inner_extended[i + 2] a == d || return newtrees @@ -759,11 +759,11 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} if i > 1 c = f.innerlines[i - 1] if FusionStyle(I) isa MultiplicityFreeFusion - coeff *= Fsymbol(a, b, dual(b), a, c, rightone(a)) + coeff *= Fsymbol(a, b, dual(b), a, c, rightunit(a)) else μ = f.vertices[i - 1] ν = f.vertices[i] - coeff *= Fsymbol(a, b, dual(b), a, c, rightone(a))[μ, ν, 1, 1] + coeff *= Fsymbol(a, b, dual(b), a, c, rightunit(a))[μ, ν, 1, 1] end end if f.isdual[i] @@ -772,7 +772,7 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} push!(newtrees, f′ => coeff) return newtrees else # i == N - unit = leftone(b) + unit = leftunit(b) if N == 2 f′ = FusionTree{I}((), unit, (), (), ()) coeff = sqrtdim(b) @@ -838,14 +838,14 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sect vertices = f.vertices oneT = one(sectorscalartype(I)) - if isone(uncoupled[i]) || isone(uncoupled[i + 1]) + if isunit(a) || isunit(b) # braiding with trivial sector: simple and always possible inner′ = inner vertices′ = vertices if i > 1 # we also need to alter innerlines and vertices inner′ = TupleTools.setindex( inner, - inner_extended[isone(a) ? (i + 1) : (i - 1)], + inner_extended[isunit(a) ? (i + 1) : (i - 1)], i - 1 ) vertices′ = TupleTools.setindex(vertices′, vertices[i], i - 1) diff --git a/src/spaces/deligne.jl b/src/spaces/deligne.jl index 926d59e4..5a5a2d71 100644 --- a/src/spaces/deligne.jl +++ b/src/spaces/deligne.jl @@ -24,7 +24,7 @@ function ⊠(V::GradedSpace, P₀::ProductSpace{<:ElementarySpace, 0}) I₁ = sectortype(V) I₂ = sectortype(P₀) return Vect[I₁ ⊠ I₂]( - ifelse(isdual(V), dual(c), c) ⊠ one(I₂) => dim(V, c) + ifelse(isdual(V), dual(c), c) ⊠ unit(I₂) => dim(V, c) for c in sectors(V); dual = isdual(V) ) end @@ -34,7 +34,7 @@ function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, V::GradedSpace) I₁ = sectortype(P₀) I₂ = sectortype(V) return Vect[I₁ ⊠ I₂]( - one(I₁) ⊠ ifelse(isdual(V), dual(c), c) => dim(V, c) + unit(I₁) ⊠ ifelse(isdual(V), dual(c), c) => dim(V, c) for c in sectors(V); dual = isdual(V) ) end @@ -42,13 +42,13 @@ end function ⊠(V::ComplexSpace, P₀::ProductSpace{<:ElementarySpace, 0}) field(V) == field(P₀) || throw_incompatible_fields(V, P₀) I₂ = sectortype(P₀) - return Vect[I₂](one(I₂) => dim(V); dual = isdual(V)) + return Vect[I₂](unit(I₂) => dim(V); dual = isdual(V)) end function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, V::ComplexSpace) field(P₀) == field(V) || throw_incompatible_fields(P₀, V) I₁ = sectortype(P₀) - return Vect[I₁](one(I₁) => dim(V); dual = isdual(V)) + return Vect[I₁](unit(I₁) => dim(V); dual = isdual(V)) end function ⊠(P::ProductSpace{<:ElementarySpace, 0}, P₀::ProductSpace{<:ElementarySpace, 0}) diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index 8cfff0b3..80e08f70 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -94,7 +94,7 @@ InnerProductStyle(::Type{<:GradedSpace}) = EuclideanInnerProduct() function dim(V::GradedSpace) return reduce( +, dim(V, c) * dim(c) for c in sectors(V); - init = zero(dim(one(sectortype(V)))) + init = zero(dim(unit(sectortype(V)))) ) end function dim(V::GradedSpace{I, <:AbstractDict}, c::I) where {I <: Sector} @@ -133,9 +133,9 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} end return (offset + 1):(offset + dim(c) * dim(V, c)) end -#TODO: one -> unit -unitspace(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(one(I) => 1) -Base.zero(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(one(I) => 0) + +unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 1) +Base.zero(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 0) # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 2af9bb0c..0122c1be 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -147,7 +147,7 @@ function blocksectors(P::ProductSpace{S, N}) where {S, N} end bs = Vector{I}() if N == 0 - push!(bs, one(I)) + push!(bs, unit(I)) elseif N == 1 for s in sectors(P) push!(bs, first(s)) @@ -241,8 +241,8 @@ function Base.literal_pow(::typeof(^), V::ElementarySpace, p::Val{N}) where {N} return ProductSpace{typeof(V), N}(ntuple(n -> V, p)) end -fuse(P::ProductSpace{S,0}) where {S<:ElementarySpace} = unitspace(S) -fuse(P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...) +fuse(P::ProductSpace{S, 0}) where {S <: ElementarySpace} = unitspace(S) +fuse(P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) """ insertleftunit(P::ProductSpace, i::Int=length(P) + 1; conj=false, dual=false) @@ -253,8 +253,10 @@ More specifically, adds a left monoidal unit or its dual. See also [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ -function insertleftunit(P::ProductSpace, ::Val{i}=Val(length(P) + 1); - conj::Bool=false, dual::Bool=false) where {i} +function insertleftunit( + P::ProductSpace, ::Val{i} = Val(length(P) + 1); + conj::Bool = false, dual::Bool = false + ) where {i} u = unitspace(spacetype(P)) if dual u = TensorKit.dual(u) @@ -274,8 +276,10 @@ More specifically, adds a right monoidal unit or its dual. See also [`insertleftunit`](@ref insertleftunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ -function insertrightunit(P::ProductSpace, ::Val{i}=Val(length(P)); - conj::Bool=false, dual::Bool=false) where {i} +function insertrightunit( + P::ProductSpace, ::Val{i} = Val(length(P)); + conj::Bool = false, dual::Bool = false + ) where {i} u = unitspace(spacetype(P)) if dual u = TensorKit.dual(u) @@ -325,8 +329,8 @@ function Base.promote_rule(::Type{S}, ::Type{<:ProductSpace{S}}) where {S <: Ele end # ProductSpace to ElementarySpace -Base.convert(::Type{S}, P::ProductSpace{S,0}) where {S<:ElementarySpace} = unitspace(S) -Base.convert(::Type{S}, P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...) +Base.convert(::Type{S}, P::ProductSpace{S, 0}) where {S <: ElementarySpace} = unitspace(S) +Base.convert(::Type{S}, P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) # ElementarySpace to ProductSpace Base.convert(::Type{<:ProductSpace}, V::S) where {S <: ElementarySpace} = ⊗(V) diff --git a/src/tensors/braidingtensor.jl b/src/tensors/braidingtensor.jl index 44b1673c..2a88288a 100644 --- a/src/tensors/braidingtensor.jl +++ b/src/tensors/braidingtensor.jl @@ -366,7 +366,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # f₀ = FusionTree{I}((), u, (), (), ()) # braidingtensor_levels = A.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindA[2]] > braidingtensor_levels[cindA[3]] @@ -434,7 +434,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # f₀ = FusionTree{I}((), u, (), (), ()) # braidingtensor_levels = B.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindB[2]] > braidingtensor_levels[cindB[3]] @@ -501,7 +501,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # braidingtensor_levels = A.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindA[2]] > braidingtensor_levels[cindA[3]] # for (f₁, f₂) in fusiontrees(B) @@ -563,7 +563,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # braidingtensor_levels = B.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindB[2]] > braidingtensor_levels[cindB[3]] # for (f₁, f₂) in fusiontrees(A) diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 43758951..4681a97d 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -608,13 +608,13 @@ function ⊠(t1::AbstractTensorMap, t2::AbstractTensorMap) dom1 = domain(t1) ⊠ one(S2) t1′ = similar(t1, codom1 ← dom1) for (c, b) in blocks(t1) - copy!(block(t1′, c ⊠ one(I2)), b) + copy!(block(t1′, c ⊠ unit(I2)), b) end codom2 = one(S1) ⊠ codomain(t2) dom2 = one(S1) ⊠ domain(t2) t2′ = similar(t2, codom2 ← dom2) for (c, b) in blocks(t2) - copy!(block(t2′, one(I1) ⊠ c), b) + copy!(block(t2′, unit(I1) ⊠ c), b) end return t1′ ⊗ t2′ end diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index accada77..3f28f168 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -548,9 +548,9 @@ since it assumes a uniquely defined coupled charge. throw(ArgumentError("Number of sectors does not match.")) s₁ = TupleTools.getindices(sectors, codomainind(t)) s₂ = map(dual, TupleTools.getindices(sectors, domainind(t))) - c1 = length(s₁) == 0 ? one(I) : (length(s₁) == 1 ? s₁[1] : first(⊗(s₁...))) + c1 = length(s₁) == 0 ? unit(I) : (length(s₁) == 1 ? s₁[1] : first(⊗(s₁...))) @boundscheck begin - c2 = length(s₂) == 0 ? one(I) : (length(s₂) == 1 ? s₂[1] : first(⊗(s₂...))) + c2 = length(s₂) == 0 ? unit(I) : (length(s₂) == 1 ? s₂[1] : first(⊗(s₂...))) c2 == c1 || throw(SectorMismatch("Not a valid sector for this tensor")) hassector(codomain(t), s₁) && hassector(domain(t), s₂) end diff --git a/test/fusiontrees.jl b/test/fusiontrees.jl index 0e0920eb..b62ce6ab 100644 --- a/test/fusiontrees.jl +++ b/test/fusiontrees.jl @@ -26,7 +26,7 @@ ti = time() @test eval(Meta.parse(sprint(show, f))) == f end @testset "Fusion tree $Istr: constructor properties" begin - u = one(I) + u = unit(I) @constinferred FusionTree((), u, (), (), ()) @constinferred FusionTree((u,), u, (false,), (), ()) @constinferred FusionTree((u, u), u, (false, false), (), (1,)) @@ -130,7 +130,7 @@ ti = time() outgoing = (s, dual(s), s, dual(s), s, dual(s)) for bool in (true, false) isdual = (bool, !bool, bool, !bool, bool, !bool) - for f in fusiontrees(outgoing, one(s), isdual) + for f in fusiontrees(outgoing, unit(s), isdual) af = convert(Array, f) T = eltype(af) @@ -570,7 +570,7 @@ ti = time() @testset "Double fusion tree $Istr: planar trace" begin d1 = transpose(f1, f1, (N + 1, 1:N..., ((2N):-1:(N + 3))...), (N + 2,)) f1front, = TK.split(f1, N - 1) - T = typeof(Fsymbol(one(I), one(I), one(I), one(I), one(I), one(I))[1, 1, 1, 1]) + T = TensorKitSectors._Fscalartype(I) d2 = Dict{typeof((f1front, f1front)), T}() for ((f1′, f2′), coeff′) in d1 for ((f1′′, f2′′), coeff′′) in diff --git a/test/runtests.jl b/test/runtests.jl index 8fa1600a..cc85cc24 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,14 +33,14 @@ end function randsector(::Type{I}) where {I <: Sector} s = collect(smallset(I)) a = rand(s) - while a == one(a) # don't use trivial label + while isunit(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)) + fusiontensor(unit(I), unit(I), unit(I)) return true catch e if e isa MethodError diff --git a/test/spaces.jl b/test/spaces.jl index 1ca8eb70..94642bd0 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -70,11 +70,11 @@ println("------------------------------------") @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) @test ℝ^d == ℝ[](d) == CartesianSpace(d) == typeof(V)(d) W = @constinferred ℝ^1 - @test @constinferred(oneunit(V)) == W == oneunit(typeof(V)) + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) @test @constinferred(zero(V)) == ℝ^0 == zero(typeof(V)) @test @constinferred(⊕(V, zero(V))) == V @test @constinferred(⊕(V, V)) == ℝ^(2d) - @test @constinferred(⊕(V, oneunit(V))) == ℝ^(d + 1) + @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) @@ -116,7 +116,7 @@ println("------------------------------------") @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(oneunit(V)) == W == oneunit(typeof(V)) + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) @test @constinferred(zero(V)) == ℂ^0 == zero(typeof(V)) @test @constinferred(⊕(V, zero(V))) == V @test @constinferred(⊕(V, V)) == ℂ^(2d) @@ -126,7 +126,7 @@ println("------------------------------------") # @test_throws promote_except (⊕(ℝ^d, ℂ^d)) @test_throws ErrorException (⊗(ℝ^d, ℂ^d)) @test @constinferred(⊕(V, V)) == ℂ^(2d) - @test @constinferred(⊕(V, oneunit(V))) == ℂ^(d + 1) + @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) @@ -170,7 +170,7 @@ println("------------------------------------") @timedtestset "ElementarySpace: $(TensorKit.type_repr(Vect[I]))" for I in sectorlist if Base.IteratorSize(values(I)) === Base.IsInfinite() - set = unique(vcat(one(I), [randsector(I) for k in 1:10])) + 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))) @@ -204,12 +204,12 @@ println("------------------------------------") # space with no sectors @test dim(@constinferred(zero(V))) == 0 # space with a single sector - W = @constinferred GradedSpace(one(I) => 1) - @test W == GradedSpace(one(I) => 1, randsector(I) => 0) - @test @constinferred(oneunit(V)) == W == oneunit(typeof(V)) - @test @constinferred(zero(V)) == GradedSpace(one(I) => 0) + 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(zero(V)) == GradedSpace(unit(I) => 0) # randsector never returns trivial sector, so this cannot error - @test_throws ArgumentError GradedSpace(one(I) => 1, randsector(I) => 0, one(I) => 3) + @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) @@ -231,7 +231,7 @@ println("------------------------------------") @test @constinferred(⊕(V, zero(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 => isone(c) + dim(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) @@ -248,7 +248,7 @@ println("------------------------------------") @test V == @constinferred infimum(V, ⊕(V, V)) @test V ≺ ⊕(V, V) @test !(V ≻ ⊕(V, V)) - @test infimum(V, GradedSpace(one(I) => 3)) == GradedSpace(one(I) => 2) + @test infimum(V, GradedSpace(unit(I) => 3)) == GradedSpace(unit(I) => 2) @test_throws SpaceMismatch (⊕(V, V')) end @@ -273,10 +273,10 @@ println("------------------------------------") @test @constinferred(⊗(V1 ⊗ V2, V3 ⊗ V4)) == P @test @constinferred(⊗(V1, V2, V3 ⊗ V4)) == P @test @constinferred(⊗(V1, V2 ⊗ V3, V4)) == P - @test V1 * V2 * oneunit(V1) * V3 * V4 == + @test V1 * V2 * unitspace(V1) * V3 * V4 == @constinferred(insertleftunit(P, 3)) == @constinferred(insertrightunit(P, 2)) - @test @constinferred(removeunit(V1 * V2 * oneunit(V1)' * V3 * V4, 3)) == P + @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 @@ -334,7 +334,7 @@ println("------------------------------------") @test @constinferred(*(V1, V2, V3)) == P @test @constinferred(⊗(V1, V2, V3)) == P @test @constinferred(adjoint(P)) == dual(P) == V3' ⊗ V2' ⊗ V1' - @test V1 * V2 * oneunit(V1)' * V3 == + @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)) @@ -415,22 +415,22 @@ println("------------------------------------") @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 ⊗ oneunit(V5)) == + @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 ⊗ oneunit(V5)') == + @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)') == @constinferred(insertleftunit(W; conj = true)) == @constinferred(insertrightunit(W; conj = true)) - @test (oneunit(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == + @test (unitspace(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == @constinferred(insertleftunit(W, 1)) == @constinferred(insertrightunit(W, 0)) - @test (V1 ⊗ V2 ⊗ oneunit(V1) ← V3 ⊗ V4 ⊗ V5) == + @test (V1 ⊗ V2 ⊗ unitspace(V1) ← V3 ⊗ V4 ⊗ V5) == @constinferred(insertrightunit(W, 2)) - @test (V1 ⊗ V2 ← oneunit(V1) ⊗ V3 ⊗ V4 ⊗ V5) == + @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)) == (oneunit(V1) ← V1) + @test @constinferred(insertrightunit(one(V1) ← V1, 0)) == (unitspace(V1) ← V1) @test_throws BoundsError insertleftunit(one(V1) ← V1, 0) end end From bef4790ccf786a8656fa5657f37737888d3792e7 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 16:07:20 +0200 Subject: [PATCH 04/20] change conj to dual for sectors --- src/fusiontrees/fusiontrees.jl | 4 ++-- src/fusiontrees/manipulations.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fusiontrees/fusiontrees.jl b/src/fusiontrees/fusiontrees.jl index 0408c64e..1665cdb2 100644 --- a/src/fusiontrees/fusiontrees.jl +++ b/src/fusiontrees/fusiontrees.jl @@ -165,8 +165,8 @@ end function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 1}) where {I} c = f.coupled if f.isdual[1] - sqrtdc = sqrtdim(c) # TODO: change conj to dual - Zcbartranspose = sqrtdc * convert(A, fusiontensor(conj(c), c, unit(c)))[:, :, 1, 1] + sqrtdc = sqrtdim(c) + Zcbartranspose = sqrtdc * convert(A, fusiontensor(dual(c), c, unit(c)))[:, :, 1, 1] X = conj!(Zcbartranspose) # we want Zcbar^† else X = convert(A, fusiontensor(c, unit(c), c))[:, 1, :, 1, 1] diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index 08688fea..ddf0b751 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -903,7 +903,7 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sect return fusiontreedict(I)(f′ => coeff) elseif FusionStyle(I) isa SimpleFusion local newtrees - for c′ in intersect(a ⊗ d, e ⊗ conj(b)) + for c′ in intersect(a ⊗ d, e ⊗ dual(b)) coeff = oftype( oneT, if inv @@ -924,7 +924,7 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sect return newtrees else # GenericFusion local newtrees - for c′ in intersect(a ⊗ d, e ⊗ conj(b)) + for c′ in intersect(a ⊗ d, e ⊗ dual(b)) Rmat1 = inv ? Rsymbol(d, c, e)' : Rsymbol(c, d, e) Rmat2 = inv ? Rsymbol(d, a, c′)' : Rsymbol(a, d, c′) Fmat = Fsymbol(d, a, b, e, c′, c) From 3237aa71676bf33250dd9495dca5f09f7eda15fe Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 16:08:26 +0200 Subject: [PATCH 05/20] export new functions from TensorKitSectors --- src/TensorKit.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index e80de65f..fae064cc 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -10,14 +10,17 @@ module TensorKit # Types: export Sector, AbstractIrrep, Irrep export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, - SimpleFusion, GenericFusion + SimpleFusion, GenericFusion +export UnitStyle, SimpleUnit, GenericUnit export BraidingStyle, SymmetricBraiding, Bosonic, Fermionic, Anyonic, NoBraiding export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep export ProductSector export FermionParity, FermionNumber, FermionSpin export FibonacciAnyon, IsingAnyon +export unit, rightunit, leftunit, allunits, isunit export VectorSpace, Field, ElementarySpace # abstract vector spaces +export unitspace export InnerProductStyle, NoInnerProduct, HasInnerProduct, EuclideanInnerProduct export ComplexSpace, CartesianSpace, GeneralSpace, GradedSpace # concrete spaces export ZNSpace, Z2Space, Z3Space, Z4Space, U1Space, CU1Space, SU2Space From 0b5a95402e45b898eaa47d862de7198bb1627df0 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 16:33:57 +0200 Subject: [PATCH 06/20] introduce `zerospace` to replace `zero` of a space --- src/TensorKit.jl | 2 +- src/spaces/cartesianspace.jl | 3 +-- src/spaces/complexspace.jl | 3 +-- src/spaces/generalspace.jl | 2 +- src/spaces/gradedspace.jl | 4 ++-- src/spaces/homspace.jl | 2 +- src/spaces/vectorspaces.jl | 11 +++++++---- test/spaces.jl | 22 +++++++++++----------- test/tensors.jl | 2 +- 9 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index fae064cc..6ef874c6 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -20,7 +20,7 @@ export FibonacciAnyon, IsingAnyon export unit, rightunit, leftunit, allunits, isunit export VectorSpace, Field, ElementarySpace # abstract vector spaces -export unitspace +export unitspace, zerospace export InnerProductStyle, NoInnerProduct, HasInnerProduct, EuclideanInnerProduct export ComplexSpace, CartesianSpace, GeneralSpace, GradedSpace # concrete spaces export ZNSpace, Z2Space, Z3Space, Z4Space, U1Space, CU1Space, SU2Space diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index 5f7d60c7..3ee4b6ca 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -48,8 +48,7 @@ sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{CartesianSpace}) = Trivial unitspace(::Type{CartesianSpace}) = CartesianSpace(1) -Base.zero(::Type{CartesianSpace}) = CartesianSpace(0) - +zerospace(::Type{CartesianSpace}) = CartesianSpace(0) ⊕(V₁::CartesianSpace, V₂::CartesianSpace) = CartesianSpace(V₁.d + V₂.d) function ⊖(V::CartesianSpace, W::CartesianSpace) V ≿ W || throw(ArgumentError("$(W) is not a subspace of $(V)")) diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index ee3498f0..87e9e1dd 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -49,8 +49,7 @@ sectortype(::Type{ComplexSpace}) = Trivial Base.conj(V::ComplexSpace) = ComplexSpace(dim(V), !isdual(V)) unitspace(::Type{ComplexSpace}) = ComplexSpace(1) -Base.zero(::Type{ComplexSpace}) = ComplexSpace(0) - +zerospace(::Type{ComplexSpace}) = ComplexSpace(0) function ⊕(V₁::ComplexSpace, V₂::ComplexSpace) return isdual(V₁) == isdual(V₂) ? ComplexSpace(dim(V₁) + dim(V₂), isdual(V₁)) : diff --git a/src/spaces/generalspace.jl b/src/spaces/generalspace.jl index d1f84d8f..62b16d65 100644 --- a/src/spaces/generalspace.jl +++ b/src/spaces/generalspace.jl @@ -36,7 +36,7 @@ field(::Type{GeneralSpace{𝔽}}) where {𝔽} = 𝔽 InnerProductStyle(::Type{<:GeneralSpace}) = NoInnerProduct() unitspace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) -Base.zero(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) +zerospace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V), isconj(V)) Base.conj(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), isdual(V), !isconj(V)) diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index 80e08f70..c0f3f9b1 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -134,8 +134,8 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} return (offset + 1):(offset + dim(c) * dim(V, c)) end -unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 1) -Base.zero(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 0) +unitspace(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(unit(I) => 1) +zerospace(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(unit(I) => 0) # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 3958030b..f020535c 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -22,7 +22,7 @@ end function HomSpace(codomain::S, domain::S) where {S <: ElementarySpace} return HomSpace(⊗(codomain), ⊗(domain)) end -HomSpace(codomain::VectorSpace) = HomSpace(codomain, zero(codomain)) +HomSpace(codomain::VectorSpace) = HomSpace(codomain, zerospace(codomain)) codomain(W::HomSpace) = W.codomain domain(W::HomSpace) = W.domain diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index 6fb59ea9..dca2e990 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -129,15 +129,18 @@ that this is different from `one(V::S)`, which returns the empty product space """ unitspace(V::ElementarySpace) = unitspace(typeof(V)) Base.oneunit(V::ElementarySpace) = unitspace(V) +#TODO: add for type """ - zero(V::S) where {S<:ElementarySpace} -> S + zerospace(V::S) where {S<:ElementarySpace} -> S Return the corresponding vector space of type `S` that represents the zero-dimensional or empty space. -This is, with a slight abuse of notation, the zero element of the direct sum of vector spaces. +This is, with a slight abuse of notation, the zero element of the direct sum of vector spaces. +`Base.zero` falls back to `zerospace`. """ -#TODO: zerospace? -Base.zero(V::ElementarySpace) = zero(typeof(V)) +zerospace(V::ElementarySpace) = zerospace(typeof(V)) +Base.zero(V::ElementarySpace) = zerospace(V) +Base.zero(::Type{V}) where {V<:ElementarySpace} = zerospace(V) """ ⊕(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} -> S diff --git a/test/spaces.jl b/test/spaces.jl index 94642bd0..bd680d81 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -65,14 +65,14 @@ println("------------------------------------") @test length(sectors(V)) == 1 @test @constinferred(TensorKit.hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test dim(@constinferred(zero(V))) == 0 - @test (sectors(zero(V))...,) == () + @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(zero(V)) == ℝ^0 == zero(typeof(V)) - @test @constinferred(⊕(V, zero(V))) == 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) @@ -111,14 +111,14 @@ println("------------------------------------") @test length(sectors(V)) == 1 @test @constinferred(TensorKit.hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test dim(@constinferred(zero(V))) == 0 - @test (sectors(zero(V))...,) == () + @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(zero(V)) == ℂ^0 == zero(typeof(V)) - @test @constinferred(⊕(V, zero(V))) == 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 " * @@ -202,12 +202,12 @@ println("------------------------------------") @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(zero(V))) == 0 + @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(zero(V)) == GradedSpace(unit(I) => 0) + @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 @@ -228,7 +228,7 @@ println("------------------------------------") if hasfusiontensor(I) @test @constinferred(TensorKit.axes(V)) == Base.OneTo(dim(V)) end - @test @constinferred(⊕(V, zero(V))) == V + @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)) diff --git a/test/tensors.jl b/test/tensors.jl index 73462306..555aebdd 100644 --- a/test/tensors.jl +++ b/test/tensors.jl @@ -80,7 +80,7 @@ for V in spacelist end end for T in (Int, Float32, ComplexF64) - t = randn(T, V1 ⊗ V2 ← zero(V1)) + t = randn(T, V1 ⊗ V2 ← zerospace(V1)) a = convert(Array, t) @test norm(a) == 0 end From c4618ae3d6469ea0c44bfee6abbd9c67296fdd18 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 16:35:07 +0200 Subject: [PATCH 07/20] add `oneunit` for type of space --- src/spaces/vectorspaces.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index dca2e990..5120f9b3 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -129,7 +129,7 @@ that this is different from `one(V::S)`, which returns the empty product space """ unitspace(V::ElementarySpace) = unitspace(typeof(V)) Base.oneunit(V::ElementarySpace) = unitspace(V) -#TODO: add for type +Base.oneunit(::Type{V}) where {V<:ElementarySpace} = unitspace(V) """ zerospace(V::S) where {S<:ElementarySpace} -> S From 07f57fcb0d983dc912c33d2d6f8f9bc366d39826 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Thu, 25 Sep 2025 16:57:45 +0200 Subject: [PATCH 08/20] format --- test/spaces.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/spaces.jl b/test/spaces.jl index bd680d81..c3b3db2f 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -209,7 +209,8 @@ println("------------------------------------") @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_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) From 0fb4d64818d8d9c367e69186d886abc56574efa2 Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 26 Sep 2025 12:13:28 +0200 Subject: [PATCH 09/20] minor changes from #263 --- src/fusiontrees/manipulations.jl | 4 ++-- test/fusiontrees.jl | 6 +++--- test/runtests.jl | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index ddf0b751..8b81bdf8 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -789,13 +789,13 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} vertices_ = TupleTools.front(f.vertices) f_ = FusionTree(uncoupled_, coupled_, isdual_, inner_, vertices_) fs = FusionTree((b,), b, (!f.isdual[1],), (), ()) - for (f_′, coeff) in merge(fs, f_, unit, 1) # coloring gets reversed here, should be the other unit + for (f_′, coeff) in merge(fs, f_, unit, 1) f_′.innerlines[1] == unit || continue uncoupled′ = Base.tail(Base.tail(f_′.uncoupled)) isdual′ = Base.tail(Base.tail(f_′.isdual)) inner′ = N <= 4 ? () : Base.tail(Base.tail(f_′.innerlines)) vertices′ = N <= 3 ? () : Base.tail(Base.tail(f_′.vertices)) - f′ = FusionTree(uncoupled′, unit, isdual′, inner′, vertices′) # and this one? + f′ = FusionTree(uncoupled′, unit, isdual′, inner′, vertices′) coeff *= sqrtdim(b) if !(f.isdual[N]) coeff *= conj(frobeniusschur(b)) diff --git a/test/fusiontrees.jl b/test/fusiontrees.jl index b62ce6ab..68746249 100644 --- a/test/fusiontrees.jl +++ b/test/fusiontrees.jl @@ -216,7 +216,7 @@ ti = time() end end end - @testset "Fusion tree $Istr: elementy artin braid" begin + @testset "Fusion tree $Istr: elementary artin braid" begin N = length(out) isdual = ntuple(n -> rand(Bool), N) for in in ⊗(out...) @@ -273,7 +273,7 @@ ti = time() end end @testset "Fusion tree $Istr: braiding and permuting" begin - f = rand(collect(fusiontrees(out, in, isdual))) + f = rand(collect(it)) p = tuple(randperm(N)...) ip = invperm(p) @@ -389,7 +389,7 @@ ti = time() f1 = rand(collect(fusiontrees(out, incoming, ntuple(n -> rand(Bool), N)))) f2 = rand(collect(fusiontrees(out[randperm(N)], incoming, ntuple(n -> rand(Bool), N)))) - @testset "Double fusion tree $Istr: repartioning" begin + @testset "Double fusion tree $Istr: repartitioning" begin for n in 0:(2 * N) d = @constinferred TK.repartition(f1, f2, $n) @test dim(incoming) ≈ diff --git a/test/runtests.jl b/test/runtests.jl index cc85cc24..2dee2d33 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,7 +3,7 @@ using TestExtras using Random using TensorKit using Combinatorics -using TensorKit: ProductSector, fusiontensor, pentagon_equation, hexagon_equation +using TensorKit: ProductSector, fusiontensor using TensorOperations using Base.Iterators: take, product # using SUNRepresentations: SUNIrrep From 6efcb053637f40a3f1ac4deec86e98b04f77a1cc Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 26 Sep 2025 12:22:09 +0200 Subject: [PATCH 10/20] use the const `TK` in tests where appropriate --- test/ad.jl | 6 +++--- test/diagonal.jl | 2 +- test/fusiontrees.jl | 10 +++++----- test/spaces.jl | 34 +++++++++++++++++----------------- test/tensors.jl | 8 ++++---- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/test/ad.jl b/test/ad.jl index 39bc9b16..553bca74 100644 --- a/test/ad.jl +++ b/test/ad.jl @@ -33,7 +33,7 @@ function ChainRulesTestUtils.test_approx( end # make sure that norms are computed correctly: -function FiniteDifferences.to_vec(t::TensorKit.SectorDict) +function FiniteDifferences.to_vec(t::TK.SectorDict) T = scalartype(valtype(t)) vec = mapreduce(vcat, t; init = T[]) do (c, b) return reshape(b, :) .* sqrt(dim(c)) @@ -43,7 +43,7 @@ function FiniteDifferences.to_vec(t::TensorKit.SectorDict) function from_vec(x_real) x = T <: Real ? x_real : reinterpret(T, x_real) ctr = 0 - return TensorKit.SectorDict( + return TK.SectorDict( c => ( n = length(b); b′ = reshape(view(x, ctr .+ (1:n)), size(b)) ./ sqrt(dim(c)); @@ -197,7 +197,7 @@ spacelist = ( for V in spacelist I = sectortype(eltype(V)) - Istr = TensorKit.type_repr(I) + Istr = TK.type_repr(I) eltypes = isreal(sectortype(eltype(V))) ? (Float64, ComplexF64) : (ComplexF64,) symmetricbraiding = BraidingStyle(sectortype(eltype(V))) isa SymmetricBraiding println("---------------------------------------") diff --git a/test/diagonal.jl b/test/diagonal.jl index 74214949..5a358236 100644 --- a/test/diagonal.jl +++ b/test/diagonal.jl @@ -32,7 +32,7 @@ diagspacelist = ( b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TensorKit.blocktype(t) + @test typeof(b1) === TK.blocktype(t) # basic linear algebra @test isa(@constinferred(norm(t)), real(T)) @test norm(t)^2 ≈ dot(t, t) diff --git a/test/fusiontrees.jl b/test/fusiontrees.jl index 68746249..75bb76e9 100644 --- a/test/fusiontrees.jl +++ b/test/fusiontrees.jl @@ -2,9 +2,9 @@ println("------------------------------------") println("Fusion Trees") println("------------------------------------") ti = time() -@timedtestset "Fusion trees for $(TensorKit.type_repr(I))" verbose = true for I in +@timedtestset "Fusion trees for $(TK.type_repr(I))" verbose = true for I in sectorlist - Istr = TensorKit.type_repr(I) + Istr = TK.type_repr(I) N = 5 out = ntuple(n -> randsector(I), N) isdual = ntuple(n -> rand(Bool), N) @@ -446,12 +446,12 @@ ti = time() ip = invperm(p) ip1, ip2 = ip[1:N], ip[(N + 1):(2N)] - d = @constinferred TensorKit.permute(f1, f2, p1, p2) + d = @constinferred TK.permute(f1, f2, p1, p2) @test dim(incoming) ≈ sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) d2 = Dict{typeof((f1, f2)), valtype(d)}() for ((f1′, f2′), coeff) in d - d′ = TensorKit.permute(f1′, f2′, ip1, ip2) + d′ = TK.permute(f1′, f2′, ip1, ip2) for ((f1′′, f2′′), coeff2) in d′ d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff @@ -590,7 +590,7 @@ ti = time() end end end - TensorKit.empty_globalcaches!() + TK.empty_globalcaches!() end tf = time() printstyled( diff --git a/test/spaces.jl b/test/spaces.jl index c3b3db2f..be37a668 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -63,11 +63,11 @@ println("------------------------------------") @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @test length(sectors(V)) == 1 - @test @constinferred(TensorKit.hassector(V, Trivial())) + @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(TensorKit.axes(V)) == Base.OneTo(d) + @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)) @@ -109,11 +109,11 @@ println("------------------------------------") @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @test length(sectors(V)) == 1 - @test @constinferred(TensorKit.hassector(V, Trivial())) + @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(TensorKit.axes(V)) == Base.OneTo(d) + @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)) @@ -151,10 +151,10 @@ println("------------------------------------") @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 !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) @@ -163,12 +163,12 @@ println("------------------------------------") @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(TK.hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test @constinferred(TK.axes(V)) == Base.OneTo(d) end - @timedtestset "ElementarySpace: $(TensorKit.type_repr(Vect[I]))" for I in sectorlist + @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) @@ -176,7 +176,7 @@ println("------------------------------------") 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(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) @@ -222,12 +222,12 @@ println("------------------------------------") @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == I slist = @constinferred sectors(V) - @test @constinferred(TensorKit.hassector(V, first(slist))) + @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(TensorKit.axes(V)) == Base.OneTo(dim(V)) + @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)) @@ -398,7 +398,7 @@ println("------------------------------------") @timedtestset "HomSpace" begin for (V1, V2, V3, V4, V5) in (Vtr, Vℤ₃, VSU₂) - W = TensorKit.HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) + 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) @@ -415,7 +415,7 @@ 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 TensorKit.compose(W, W') + @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TK.compose(W, W') @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)) == @constinferred(insertleftunit(W)) == @constinferred(insertrightunit(W)) @@ -435,5 +435,5 @@ println("------------------------------------") @test_throws BoundsError insertleftunit(one(V1) ← V1, 0) end end - TensorKit.empty_globalcaches!() + TK.empty_globalcaches!() end diff --git a/test/tensors.jl b/test/tensors.jl index 555aebdd..c0c1d90b 100644 --- a/test/tensors.jl +++ b/test/tensors.jl @@ -17,7 +17,7 @@ end for V in spacelist I = sectortype(first(V)) - Istr = TensorKit.type_repr(I) + Istr = TK.type_repr(I) println("---------------------------------------") println("Tensors with symmetry: $Istr") println("---------------------------------------") @@ -42,7 +42,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) === TensorKit.blocktype(t) + @test typeof(b1) === TK.blocktype(t) @test typeof(c) === sectortype(t) end end @@ -104,7 +104,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) === TensorKit.blocktype(t') + @test typeof(b1) === TK.blocktype(t') @test typeof(c) === sectortype(t) # linear algebra @test isa(@constinferred(norm(t)), real(T)) @@ -550,7 +550,7 @@ for V in spacelist @test t3 ≈ t4 end end - TensorKit.empty_globalcaches!() + TK.empty_globalcaches!() end @timedtestset "Deligne tensor product: test via conversion" begin From 454779a49afef5e8c6f850cb4d7ffdae0b75360f Mon Sep 17 00:00:00 2001 From: Boris De Vos Date: Fri, 26 Sep 2025 12:54:00 +0200 Subject: [PATCH 11/20] `otimes` between tensormaps to account for `sectorscalartype` --- src/tensors/linalg.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 4681a97d..c4839fd0 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -569,10 +569,17 @@ function ⊗(t1::AbstractTensorMap, t2::AbstractTensorMap) throw(SpaceMismatch("spacetype(t1) ≠ spacetype(t2)")) cod1, cod2 = codomain(t1), codomain(t2) dom1, dom2 = domain(t1), domain(t2) - cod = cod1 ⊗ cod2 - dom = dom1 ⊗ dom2 + p12 = ((codomainind(t1)..., (codomainind(t2) .+ numind(t1))...), + (domainind(t1)..., (domainind(t2) .+ numind(t1))...)) + T = promote_type(scalartype(t1), scalartype(t2)) - t = zerovector!(similar(t1, T, cod ← dom)) + TC = promote_type(sectorscalartype(sectortype(t1)), T) + t = TO.tensoralloc_contract(TC, + t1, ((codomainind(t1)..., domainind(t1)...), ()), false, + t2, ((), (codomainind(t2)..., domainind(t2)...)), false, + p12, Val(false)) + + zerovector!(t) for (f1l, f1r) in fusiontrees(t1) for (f2l, f2r) in fusiontrees(t2) c1 = f1l.coupled # = f1r.coupled From 792d28f299b5a012204160fa5bf4b1e02edc3197 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 14 Oct 2025 14:49:59 -0400 Subject: [PATCH 12/20] formatter --- src/TensorKit.jl | 3 +-- src/spaces/gradedspace.jl | 4 ++-- src/spaces/vectorspaces.jl | 4 ++-- src/tensors/linalg.jl | 16 ++++++++++------ test/spaces.jl | 3 +-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index 6ef874c6..7ef58da4 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -9,8 +9,7 @@ module TensorKit #--------- # Types: export Sector, AbstractIrrep, Irrep -export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, - SimpleFusion, GenericFusion +export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, SimpleFusion, GenericFusion export UnitStyle, SimpleUnit, GenericUnit export BraidingStyle, SymmetricBraiding, Bosonic, Fermionic, Anyonic, NoBraiding export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index c0f3f9b1..687c79ac 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -134,8 +134,8 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} return (offset + 1):(offset + dim(c) * dim(V, c)) end -unitspace(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(unit(I) => 1) -zerospace(S::Type{<:GradedSpace{I}}) where {I<:Sector} = S(unit(I) => 0) +unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 1) +zerospace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 0) # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index 5120f9b3..686265eb 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -129,7 +129,7 @@ that this is different from `one(V::S)`, which returns the empty product space """ unitspace(V::ElementarySpace) = unitspace(typeof(V)) Base.oneunit(V::ElementarySpace) = unitspace(V) -Base.oneunit(::Type{V}) where {V<:ElementarySpace} = unitspace(V) +Base.oneunit(::Type{V}) where {V <: ElementarySpace} = unitspace(V) """ zerospace(V::S) where {S<:ElementarySpace} -> S @@ -140,7 +140,7 @@ This is, with a slight abuse of notation, the zero element of the direct sum of """ zerospace(V::ElementarySpace) = zerospace(typeof(V)) Base.zero(V::ElementarySpace) = zerospace(V) -Base.zero(::Type{V}) where {V<:ElementarySpace} = zerospace(V) +Base.zero(::Type{V}) where {V <: ElementarySpace} = zerospace(V) """ ⊕(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} -> S diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index c4839fd0..234640a5 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -569,15 +569,19 @@ function ⊗(t1::AbstractTensorMap, t2::AbstractTensorMap) throw(SpaceMismatch("spacetype(t1) ≠ spacetype(t2)")) cod1, cod2 = codomain(t1), codomain(t2) dom1, dom2 = domain(t1), domain(t2) - p12 = ((codomainind(t1)..., (codomainind(t2) .+ numind(t1))...), - (domainind(t1)..., (domainind(t2) .+ numind(t1))...)) + p12 = ( + (codomainind(t1)..., (codomainind(t2) .+ numind(t1))...), + (domainind(t1)..., (domainind(t2) .+ numind(t1))...), + ) T = promote_type(scalartype(t1), scalartype(t2)) TC = promote_type(sectorscalartype(sectortype(t1)), T) - t = TO.tensoralloc_contract(TC, - t1, ((codomainind(t1)..., domainind(t1)...), ()), false, - t2, ((), (codomainind(t2)..., domainind(t2)...)), false, - p12, Val(false)) + t = TO.tensoralloc_contract( + TC, + t1, ((codomainind(t1)..., domainind(t1)...), ()), false, + t2, ((), (codomainind(t2)..., domainind(t2)...)), false, + p12, Val(false) + ) zerovector!(t) for (f1l, f1r) in fusiontrees(t1) diff --git a/test/spaces.jl b/test/spaces.jl index be37a668..3d1730f2 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -209,8 +209,7 @@ println("------------------------------------") @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_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) From e49062d2e57112a775e31559ddce10583b51610b Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 14 Oct 2025 15:55:11 -0400 Subject: [PATCH 13/20] update frobeniusschur --- src/TensorKit.jl | 3 ++- src/fusiontrees/manipulations.jl | 14 +++++++------- src/tensors/braidingtensor.jl | 12 ++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index 7ef58da4..2d38883c 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -41,7 +41,8 @@ export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic # methods for sectors and properties thereof export sectortype, sectors, hassector, Nsymbol, Fsymbol, Rsymbol, Bsymbol, - frobeniusschur, twist, otimes, sectorscalartype, deligneproduct + frobeniusschur, frobenius_schur_phase, frobenius_schur_indicator, + twist, otimes, sectorscalartype, deligneproduct export fusiontrees, braid, permute, transpose export ZNSpace, SU2Irrep, U1Irrep, CU1Irrep # other fusion tree manipulations, should not be exported: diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index 8b81bdf8..1564b1b6 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -246,7 +246,7 @@ function flip(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, i::Int; inv: @assert 0 < i ≤ N₁ + N₂ if i ≤ N₁ a = f₁.uncoupled[i] - χₐ = frobeniusschur(a) + χₐ = frobenius_schur_phase(a) θₐ = twist(a) if !inv factor = f₁.isdual[i] ? χₐ * θₐ : one(θₐ) @@ -259,7 +259,7 @@ function flip(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, i::Int; inv: else i -= N₁ a = f₂.uncoupled[i] - χₐ = frobeniusschur(a) + χₐ = frobenius_schur_phase(a) θₐ = twist(a) if !inv factor = f₂.isdual[i] ? χₐ * one(θₐ) : θₐ @@ -302,7 +302,7 @@ function bendright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where { coeff₀ = sqrtdim(c) * invsqrtdim(a) if f₁.isdual[N₁] - coeff₀ *= conj(frobeniusschur(dual(b))) + coeff₀ *= conj(frobenius_schur_phase(dual(b))) end if FusionStyle(I) isa MultiplicityFreeFusion coeff = coeff₀ * Bsymbol(a, b, c) @@ -343,7 +343,7 @@ function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where { isduala = f₁.isdual[1] factor = sqrtdim(a) if !isduala - factor *= conj(frobeniusschur(a)) + factor *= conj(frobenius_schur_phase(a)) end c1 = dual(a) c2 = f₁.coupled @@ -767,7 +767,7 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} end end if f.isdual[i] - coeff *= frobeniusschur(b) + coeff *= frobenius_schur_phase(b) end push!(newtrees, f′ => coeff) return newtrees @@ -777,7 +777,7 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} f′ = FusionTree{I}((), unit, (), (), ()) coeff = sqrtdim(b) if !(f.isdual[N]) - coeff *= conj(frobeniusschur(b)) + coeff *= conj(frobenius_schur_phase(b)) end push!(newtrees, f′ => coeff) return newtrees @@ -798,7 +798,7 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N} f′ = FusionTree(uncoupled′, unit, isdual′, inner′, vertices′) coeff *= sqrtdim(b) if !(f.isdual[N]) - coeff *= conj(frobeniusschur(b)) + coeff *= conj(frobenius_schur_phase(b)) end newtrees[f′] = get(newtrees, f′, zero(coeff)) + coeff end diff --git a/src/tensors/braidingtensor.jl b/src/tensors/braidingtensor.jl index 2a88288a..9c92c67a 100644 --- a/src/tensors/braidingtensor.jl +++ b/src/tensors/braidingtensor.jl @@ -385,10 +385,10 @@ end # f₁′′.innerlines[1] == u || continue # coeff = coeff′ * coeff′′ * sqrtdim(a) * sqrtdim(b) # if f₁′′.isdual[1] -# coeff *= frobeniusschur(a) +# coeff *= frobenius_schur_phase(a) # end # if f₁′′.isdual[3] -# coeff *= frobeniusschur(b) +# coeff *= frobenius_schur_phase(b) # end # f12 = (f₀, f₂′) # if @isdefined newtrees @@ -453,10 +453,10 @@ end # f₂′′.innerlines[1] == u || continue # coeff = coeff′ * conj(coeff′′ * sqrtdim(a) * sqrtdim(b)) # if f₂′′.isdual[1] -# coeff *= conj(frobeniusschur(a)) +# coeff *= conj(frobenius_schur_phase(a)) # end # if f₂′′.isdual[3] -# coeff *= conj(frobeniusschur(b)) +# coeff *= conj(frobenius_schur_phase(b)) # end # f12 = (f₁′, f₀) # if @isdefined newtrees @@ -517,7 +517,7 @@ end # f₁′′.innerlines[1] == u || continue # coeff = coeff′ * coeff′′ * sqrtdim(a) # if f₁′′.isdual[1] -# coeff *= frobeniusschur(a) +# coeff *= frobenius_schur_phase(a) # end # f₁′′′ = FusionTree{I}((b,), b, (f₁′′.isdual[3],), (), ()) # f12 = (f₁′′′, f₂′) @@ -579,7 +579,7 @@ end # f₂′′.innerlines[1] == u || continue # coeff = coeff′ * conj(coeff′′ * sqrtdim(a)) # if f₂′′.isdual[1] -# coeff *= conj(frobeniusschur(a)) +# coeff *= conj(frobenius_schur_phase(a)) # end # f₂′′′ = FusionTree{I}((b,), b, (f₂′′.isdual[3],), (), ()) # f12 = (f₁′, f₂′′′) From 8c19c245aeb47b1f23eaf1e7c3e8c59c2a1c72ab Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 14 Oct 2025 15:55:44 -0400 Subject: [PATCH 14/20] reimplement otimes --- src/tensors/linalg.jl | 65 ++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 234640a5..d09609ed 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -564,49 +564,44 @@ Compute the tensor product between two `AbstractTensorMap` instances, which resu new `TensorMap` instance whose codomain is `codomain(t1) ⊗ codomain(t2)` and whose domain is `domain(t1) ⊗ domain(t2)`. """ -function ⊗(t1::AbstractTensorMap, t2::AbstractTensorMap) - (S = spacetype(t1)) === spacetype(t2) || - throw(SpaceMismatch("spacetype(t1) ≠ spacetype(t2)")) - cod1, cod2 = codomain(t1), codomain(t2) - dom1, dom2 = domain(t1), domain(t2) - p12 = ( - (codomainind(t1)..., (codomainind(t2) .+ numind(t1))...), - (domainind(t1)..., (domainind(t2) .+ numind(t1))...), +function ⊗(A::AbstractTensorMap, B::AbstractTensorMap) + (S = spacetype(A)) === spacetype(B) || throw(SpaceMismatch("incompatible space types")) + + # allocate destination with correct scalartype + pA = ((codomainind(A)..., domainind(A)...), ()) + pB = ((), (codomainind(B)..., domainind(B)...)) + NA = numind(A) + pAB = ( + (codomainind(A)..., (codomainind(B) .+ NA)...), + (domainind(A)..., (domainind(B) .+ NA)...), ) - - T = promote_type(scalartype(t1), scalartype(t2)) - TC = promote_type(sectorscalartype(sectortype(t1)), T) - t = TO.tensoralloc_contract( - TC, - t1, ((codomainind(t1)..., domainind(t1)...), ()), false, - t2, ((), (codomainind(t2)..., domainind(t2)...)), false, - p12, Val(false) - ) - - zerovector!(t) - for (f1l, f1r) in fusiontrees(t1) - for (f2l, f2r) in fusiontrees(t2) + TC = TO.promote_contract(scalartype(A), scalartype(B)) + C = TO.tensoralloc_contract(TC, A, pA, false, B, pB, false, pAB, Val(false)) + zerovector!(C) + + # implement tensor product + for (f1l, f1r) in fusiontrees(A) + @inbounds a = A[f1l, f1r] + for (f2l, f2r) in fusiontrees(B) + @inbounds b = B[f2l, f2r] c1 = f1l.coupled # = f1r.coupled c2 = f2l.coupled # = f2r.coupled - for c in c1 ⊗ c2 - for μ in 1:Nsymbol(c1, c2, c) - for (fl, coeff1) in merge(f1l, f2l, c, μ) - for (fr, coeff2) in merge(f1r, f2r, c, μ) - d1 = dim(cod1, f1l.uncoupled) - d2 = dim(cod2, f2l.uncoupled) - d3 = dim(dom1, f1r.uncoupled) - d4 = dim(dom2, f2r.uncoupled) - m1 = sreshape(t1[f1l, f1r], (d1, 1, d3, 1)) - m2 = sreshape(t2[f2l, f2r], (1, d2, 1, d4)) - m = sreshape(t[fl, fr], (d1, d2, d3, d4)) - m .+= coeff1 .* conj(coeff2) .* m1 .* m2 - end + for c in c1 ⊗ c2, μ in 1:Nsymbol(c1, c2, c) + for (fl, coeff1) in merge(f1l, f2l, c, μ) + for (fr, coeff2) in merge(f1r, f2r, c, μ) + TO.tensorcontract!( + C[fl, fr], + A[f1l, f1r], pA, false, + B[f2l, f2r], pB, false, + pAB, + coeff1 * conj(coeff2), One() + ) end end end end end - return t + return C end # deligne product of tensors From 648643144bceed475bedff527898be7746dc958a Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 14 Oct 2025 15:55:59 -0400 Subject: [PATCH 15/20] remove todo comment that breaks docstring --- src/spaces/productspace.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 0122c1be..08e7011b 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -230,7 +230,6 @@ end Return a tensor product of zero spaces of type `S`, i.e. this is the unit object under the tensor product operation, such that `V ⊗ one(V) == V`. """ -#TODO: unit(V::S)? Base.one(V::VectorSpace) = one(typeof(V)) Base.one(::Type{<:ProductSpace{S}}) where {S <: ElementarySpace} = ProductSpace{S, 0}(()) Base.one(::Type{S}) where {S <: ElementarySpace} = ProductSpace{S, 0}(()) From e3c44dddfb7fd43a33df38df326b68f683f298ca Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 14 Oct 2025 16:11:40 -0400 Subject: [PATCH 16/20] fix test --- test/runtests.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/runtests.jl b/test/runtests.jl index 2dee2d33..3ae62e9b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,7 @@ using Random using TensorKit using Combinatorics using TensorKit: ProductSector, fusiontensor +using TensorKitSectors: TensorKitSectors using TensorOperations using Base.Iterators: take, product # using SUNRepresentations: SUNIrrep From 705ca7cb294a38da46716ac965a02a3cf3c83697 Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Tue, 14 Oct 2025 16:43:01 -0400 Subject: [PATCH 17/20] docs fixes --- docs/src/lib/sectors.md | 6 +++--- docs/src/lib/spaces.md | 4 ++-- docs/src/man/sectors.md | 2 +- src/spaces/vectorspaces.jl | 5 ++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/src/lib/sectors.md b/docs/src/lib/sectors.md index 4029b68c..931bd917 100644 --- a/docs/src/lib/sectors.md +++ b/docs/src/lib/sectors.md @@ -31,15 +31,15 @@ Irrep ## Methods for defining and characterizing `Sector` subtypes ```@docs -Base.one(::Sector) -dual(::Sector) +unit(::Sector) +dual Nsymbol ⊗ Fsymbol Rsymbol Bsymbol dim(::Sector) -frobeniusschur +frobenius_schur_phase twist(::Sector) Base.isreal(::Type{<:Sector}) TensorKitSectors.sectorscalartype diff --git a/docs/src/lib/spaces.md b/docs/src/lib/spaces.md index 6f60cda4..10d2bf4f 100644 --- a/docs/src/lib/spaces.md +++ b/docs/src/lib/spaces.md @@ -86,12 +86,12 @@ The following methods act specifically on `ElementarySpace` spaces: ```@docs isdual -dual +dual(::VectorSpace) conj flip ⊕ ⊖ -zero(::ElementarySpace) +zerospace unitspace supremum infimum diff --git a/docs/src/man/sectors.md b/docs/src/man/sectors.md index db7913f1..73c1630a 100644 --- a/docs/src/man/sectors.md +++ b/docs/src/man/sectors.md @@ -171,7 +171,7 @@ For practical reasons, we also require some additional methods to be defined: Further information, such as the quantum dimensions ``d_a`` and Frobenius-Schur indicator ``χ_a`` (only if ``a == \overline{a}``) are encoded in the F-symbol. They are obtained as -[`dim(a)`](@ref) and [`frobeniusschur(a)`](@ref). These functions have default definitions +[`dim(a)`](@ref) and [`frobenius_schur_phase(a)`](@ref). These functions have default definitions which extract the requested data from `Fsymbol(a, conj(a), a, a, one(a), one(a))`, but they can be overloaded in case the value can be computed more efficiently. diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index 686265eb..60766673 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -66,13 +66,12 @@ function space end Return the total dimension of the vector space `V` as an Int. """ dim(::VectorSpace) -""" +@doc """ dual(V::VectorSpace) -> VectorSpace Return the dual space of `V`; also obtained via `V'`. This should satisfy `dual(dual(V)) == V`. It is assumed that `typeof(V) == typeof(V')`. -""" -function dual end +""" dual(::VectorSpace) # convenience definitions: Base.adjoint(V::VectorSpace) = dual(V) From 5b2d027ade94cf108fdb80fb4f55b96636bf7431 Mon Sep 17 00:00:00 2001 From: Jutho Date: Wed, 15 Oct 2025 14:51:42 +0200 Subject: [PATCH 18/20] Reorganize exports in TensorKit.jl --- src/TensorKit.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index 2d38883c..00a257b3 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -16,10 +16,8 @@ export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep export ProductSector export FermionParity, FermionNumber, FermionSpin export FibonacciAnyon, IsingAnyon -export unit, rightunit, leftunit, allunits, isunit export VectorSpace, Field, ElementarySpace # abstract vector spaces -export unitspace, zerospace export InnerProductStyle, NoInnerProduct, HasInnerProduct, EuclideanInnerProduct export ComplexSpace, CartesianSpace, GeneralSpace, GradedSpace # concrete spaces export ZNSpace, Z2Space, Z3Space, Z4Space, U1Space, CU1Space, SU2Space @@ -33,13 +31,15 @@ export TruncationScheme export SpaceMismatch, SectorMismatch, IndexError # error types # general vector space methods -export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual, oplus, ominus, - insertleftunit, insertrightunit, removeunit +export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual +export unitspace, zerospace, oplus, ominus, +export insertleftunit, insertrightunit, removeunit # partial order for vector spaces export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic # methods for sectors and properties thereof +export unit, rightunit, leftunit, allunits, isunit export sectortype, sectors, hassector, Nsymbol, Fsymbol, Rsymbol, Bsymbol, frobeniusschur, frobenius_schur_phase, frobenius_schur_indicator, twist, otimes, sectorscalartype, deligneproduct From 8577e2fea8a5905e97a6a551a77b1dd3f4665bb2 Mon Sep 17 00:00:00 2001 From: Jutho Date: Wed, 15 Oct 2025 14:59:19 +0200 Subject: [PATCH 19/20] More reorganization of exports --- src/TensorKit.jl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index 00a257b3..69e2ce22 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -7,7 +7,7 @@ module TensorKit # Exports #--------- -# Types: +# Reexport common sector types: export Sector, AbstractIrrep, Irrep export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, SimpleFusion, GenericFusion export UnitStyle, SimpleUnit, GenericUnit @@ -17,6 +17,7 @@ export ProductSector export FermionParity, FermionNumber, FermionSpin export FibonacciAnyon, IsingAnyon +# Export common vector space, fusion tree and tensor types export VectorSpace, Field, ElementarySpace # abstract vector spaces export InnerProductStyle, NoInnerProduct, HasInnerProduct, EuclideanInnerProduct export ComplexSpace, CartesianSpace, GeneralSpace, GradedSpace # concrete spaces @@ -30,21 +31,19 @@ export DiagonalTensorMap, BraidingTensor export TruncationScheme export SpaceMismatch, SectorMismatch, IndexError # error types -# general vector space methods +# Export general vector space methods export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual export unitspace, zerospace, oplus, ominus, export insertleftunit, insertrightunit, removeunit - -# partial order for vector spaces export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic -# methods for sectors and properties thereof -export unit, rightunit, leftunit, allunits, isunit -export sectortype, sectors, hassector, Nsymbol, Fsymbol, Rsymbol, Bsymbol, - frobeniusschur, frobenius_schur_phase, frobenius_schur_indicator, - twist, otimes, sectorscalartype, deligneproduct +# Reexport methods for sectors and properties thereof +export sectortype, sectors, hassector +export unit, rightunit, leftunit, allunits, isunit, otimes +export Nsymbol, Fsymbol, Rsymbol, Bsymbol, frobenius_schur_phase, frobenius_schur_indicator, twist, sectorscalartype, deligneproduct + +# Export methods for fusion trees export fusiontrees, braid, permute, transpose -export ZNSpace, SU2Irrep, U1Irrep, CU1Irrep # other fusion tree manipulations, should not be exported: # export insertat, split, merge, repartition, artin_braid, # bendleft, bendright, foldleft, foldright, cycleclockwise, cycleanticlockwise @@ -55,9 +54,9 @@ export ℤ₂, ℤ₃, ℤ₄, U₁, SU, SU₂, CU₁ export fℤ₂, fU₁, fSU₂ export ℤ₂Space, ℤ₃Space, ℤ₄Space, U₁Space, CU₁Space, SU₂Space -# tensor maps +# Export tensor map methods export domain, codomain, numind, numout, numin, domainind, codomainind, allind -export spacetype, sectortype, storagetype, scalartype, tensormaptype +export spacetype, storagetype, scalartype, tensormaptype export blocksectors, blockdim, block, blocks # random methods for constructor From 9f494c9f8fafe805d79bf71c22b5d0d380075766 Mon Sep 17 00:00:00 2001 From: Jutho Date: Wed, 15 Oct 2025 15:11:17 +0200 Subject: [PATCH 20/20] Fix typo in export reorganization --- src/TensorKit.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TensorKit.jl b/src/TensorKit.jl index 69e2ce22..6b59dac9 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -33,7 +33,7 @@ export SpaceMismatch, SectorMismatch, IndexError # error types # Export general vector space methods export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual -export unitspace, zerospace, oplus, ominus, +export unitspace, zerospace, oplus, ominus export insertleftunit, insertrightunit, removeunit export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic