From 62225adaffd4fe83174468c7791078a729fad106 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Wed, 8 Jul 2026 16:11:31 -0400 Subject: [PATCH 1/4] refactor TensorMap constructors This removes the checks in the inner constructors to allow TensorMap construction without any overhead. To keep some of the warnings, I've added this back to some of the outer constructors. --- src/tensors/tensor.jl | 64 +++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index 69f860eb6..8e2b1e580 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -10,30 +10,7 @@ a tensor category), where the data is stored in a dense vector. struct TensorMap{T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} <: AbstractTensorMap{T, S, N₁, N₂} data::A space::TensorMapSpace{S, N₁, N₂} - - # uninitialized constructors - function TensorMap{T, S, N₁, N₂, A}( - ::UndefInitializer, space::TensorMapSpace{S, N₁, N₂} - ) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} - data = A(undef, dim(space)) - if !isbitstype(T) - zerovector!(data) - end - return TensorMap{T, S, N₁, N₂, A}(data, space) - end - # constructors from data - function TensorMap{T, S, N₁, N₂, A}( - data::A, space::TensorMapSpace{S, N₁, N₂} - ) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} - T ⊆ field(S) || @warn("scalartype(data) = $T ⊈ $(field(S)))", maxlog = 1) - I = sectortype(S) - T <: Real && !(sectorscalartype(I) <: Real) && - @warn("Tensors with real data might be incompatible with sector type $I", maxlog = 1) - length(data) == dim(space) || throw(DimensionMismatch(lazy"length of data ($(length(data))) does not match dimension of space ($(dim(space)))")) - return new{T, S, N₁, N₂, A}(data, space) - end end -TensorMap{T, S, N₁, N₂, A}(t::TensorMap{T, S, N₁, N₂}) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} = TensorMap(A(t.data), space(t)) """ Tensor{T, S, N, A<:DenseVector{T}} = TensorMap{T, S, N, 0, A} @@ -46,11 +23,27 @@ i.e. a tensor map with only a non-trivial output space. """ const Tensor{T, S, N, A} = TensorMap{T, S, N, 0, A} +# Utility functions: +# ------------------ function tensormaptype(::Type{S}, N₁, N₂, ::Type{TorA}) where {S <: IndexSpace, TorA} A = similarstoragetype(TorA) return TensorMap{scalartype(A), S, N₁, N₂, A} end +function _warn_tensormap_compatibility(T, S) + T ⊆ field(S) || @warn("scalartype(data) = $T ⊈ $(field(S)))", maxlog = 1) + I = sectortype(S) + T <: Real && !(sectorscalartype(I) <: Real) && + @warn("Tensors with real data might be incompatible with sector type $I", maxlog = 1) + return nothing +end + +function _check_tensormap_length(data, V) + d = dim(V) + length(data) == d || throw(DimensionMismatch(lazy"length of data ($(length(data))) does not match dimension of space ($d)")) + return nothing +end + # Basic methods for characterising a tensor: #-------------------------------------------- space(t::TensorMap) = t.space @@ -66,6 +59,8 @@ dim(t::TensorMap) = length(t.data) # General TensorMap constructors # ============================== +TensorMap{T, S, N₁, N₂, A}(t::TensorMap{T, S, N₁, N₂}) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} = + TensorMap{T, S, N₁, N₂, A}(A(t.data), space(t)) # INTERNAL: utility type alias that makes constructors also work for type aliases that specify # different storage types. (i.e. CuTensorMap = TensorMapWithStorage{T, CuVector{T}, ...}) @@ -89,6 +84,14 @@ TensorMap{T}(::UndefInitializer, V::TensorMapSpace) where {T} = TensorMap{T}(::UndefInitializer, codomain::TensorSpace, domain::TensorSpace) where {T} = TensorMap{T}(undef, codomain ← domain) Tensor{T}(::UndefInitializer, V::TensorSpace) where {T} = TensorMap{T}(undef, V ← one(V)) +function TensorMap{T, S, N₁, N₂, A}( + ::UndefInitializer, space::TensorMapSpace{S, N₁, N₂} + ) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} + _warn_tensormap_compatibility(T, S) + data = A(undef, dim(space)) + isbitstype(T) || zerovector!(data) # ensure initialized entries + return TensorMap{T, S, N₁, N₂, A}(data, space) +end """ TensorMapWithStorage{T, A}(undef, codomain, domain) where {T, A} @@ -122,10 +125,13 @@ TensorMap(t::TensorMap) = copy(t) Construct a `TensorMap` from the given raw data. This constructor takes ownership of the provided vector, and will not make an independent copy. """ -TensorMap{T}(data::DenseVector{T}, V::TensorMapSpace) where {T} = - tensormaptype(spacetype(V), numout(V), numin(V), typeof(data))(data, V) TensorMap{T}(data::DenseVector{T}, codomain::TensorSpace, domain::TensorSpace) where {T} = TensorMap{T}(data, codomain ← domain) +function TensorMap{T}(data::DenseVector{T}, V::TensorMapSpace) where {T} + _warn_tensormap_compatibility(T, spacetype(V)) + _check_tensormap_length(data, V) + return tensormaptype(spacetype(V), numout(V), numin(V), typeof(data))(data, V) +end """ TensorMapWithStorage{T, A}(data::A, codomain, domain) where {T, A<:DenseVector{T}} @@ -136,6 +142,8 @@ Construct a `TensorMap` from the given raw data. This constructor takes ownership of the provided vector, and will not make an independent copy. """ function TensorMapWithStorage{T, A}(data::A, V::TensorMapSpace) where {T, A} + _warn_tensormap_compatibility(T, spacetype(V)) + _check_tensormap_length(data, V) return tensormaptype(spacetype(V), numout(V), numin(V), typeof(data))(data, V) end TensorMapWithStorage{T, A}(data::A, codomain::TensorSpace, domain::TensorSpace) where {T, A} = @@ -213,13 +221,17 @@ end function TensorMapWithStorage{T, A}( data::AbstractArray, V::TensorMapSpace; tol = sqrt(eps(real(float(eltype(data))))) ) where {T, A} + _warn_tensormap_compatibility(T, spacetype(V)) + # refer to specific raw data constructors if input is a vector of the correct length ndims(data) == 1 && length(data) == dim(V) && return tensormaptype(spacetype(V), numout(V), numin(V), A)(data, V) # special case trivial: refer to same method, but now with vector argument - sectortype(V) === Trivial && + if sectortype(V) === Trivial + _check_tensormap_length(data, V) return tensormaptype(spacetype(V), numout(V), numin(V), A)(reshape(data, length(data)), V) + end return project_symmetric_and_check(T, A, data, V; tol) end From c90cb0958624072121d8a4d17b743504e0549f4f Mon Sep 17 00:00:00 2001 From: lkdvos Date: Wed, 8 Jul 2026 16:14:59 -0400 Subject: [PATCH 2/4] specialize sectorhash and sectorequal for trivial spaces --- src/spaces/cartesianspace.jl | 3 +++ src/spaces/complexspace.jl | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index 834c6f252..eb9d6b058 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -73,3 +73,6 @@ sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{CartesianSpace}) = Trivial Base.show(io::IO, V::CartesianSpace) = print(io, "ℝ^$(V.d)") + +sectorhash(::CartesianSpace, h::UInt) = h +sectorequal(::CartesianSpace, ::CartesianSpace) = true diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index b10e8fb0d..ab3e5893a 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -82,3 +82,6 @@ sectors(V::ComplexSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) sectortype(::Type{ComplexSpace}) = Trivial Base.show(io::IO, V::ComplexSpace) = print(io, isdual(V) ? "(ℂ^$(V.d))'" : "ℂ^$(V.d)") + +sectorhash(V::ComplexSpace, h::UInt) = hash(isdual(V), h) +sectorequal(V₁::ComplexSpace, V₂::ComplexSpace) = isdual(V₁) == isdual(V₂) From a5ea2350b7e6eb1b7ba762427c6d82db58932f59 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Wed, 8 Jul 2026 19:10:34 -0400 Subject: [PATCH 3/4] small fix by adding inner constructor --- src/tensors/tensor.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index 8e2b1e580..ed5682367 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -10,6 +10,13 @@ a tensor category), where the data is stored in a dense vector. struct TensorMap{T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} <: AbstractTensorMap{T, S, N₁, N₂} data::A space::TensorMapSpace{S, N₁, N₂} + + # explicit inner constructor to prevent auto-generating TensorMap(data, space) + function TensorMap{T, S, N₁, N₂, A}( + data::A, space::TensorMapSpace{S, N₁, N₂} + ) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} + return new{T, S, N₁, N₂, A}(data, space) + end end """ From 41743a7a3420307a4bec46b6cc3237c2cc058a4c Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Thu, 9 Jul 2026 08:01:18 -0400 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Jutho --- src/spaces/cartesianspace.jl | 4 ++-- src/spaces/complexspace.jl | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index eb9d6b058..6f60e9802 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -74,5 +74,5 @@ sectortype(::Type{CartesianSpace}) = Trivial Base.show(io::IO, V::CartesianSpace) = print(io, "ℝ^$(V.d)") -sectorhash(::CartesianSpace, h::UInt) = h -sectorequal(::CartesianSpace, ::CartesianSpace) = true +sectorhash(V::CartesianSpace, h::UInt) = hash(dim(V) == 0, h) +sectorequal(V₁::CartesianSpace, V₂::CartesianSpace) = iszero(dim(V₁)) == iszero(dim(V₂)) diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index ab3e5893a..4b648432f 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -83,5 +83,9 @@ sectortype(::Type{ComplexSpace}) = Trivial Base.show(io::IO, V::ComplexSpace) = print(io, isdual(V) ? "(ℂ^$(V.d))'" : "ℂ^$(V.d)") -sectorhash(V::ComplexSpace, h::UInt) = hash(isdual(V), h) -sectorequal(V₁::ComplexSpace, V₂::ComplexSpace) = isdual(V₁) == isdual(V₂) +function sectorhash(V::ComplexSpace, h::UInt) + return hash(dim(V) == 0, hash(isdual(V), h)) +end +function sectorequal(V₁::ComplexSpace, V₂::ComplexSpace) + return isdual(V₁) == isdual(V₂) && iszero(dim(V₁)) == iszero(dim(V₂)) +end