Skip to content

Commit f3c3102

Browse files
lkdvosJutho
andauthored
Bypass some overhead in TensorMap constructor, bypass overhead in sectorequal/sectorhash for trivial symmetry (#476)
* 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. * specialize sectorhash and sectorequal for trivial spaces * small fix by adding inner constructor * Apply suggestions from code review Co-authored-by: Jutho <Jutho@users.noreply.github.com> --------- Co-authored-by: Jutho <Jutho@users.noreply.github.com>
1 parent b9b8dfc commit f3c3102

3 files changed

Lines changed: 49 additions & 20 deletions

File tree

src/spaces/cartesianspace.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,6 @@ sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial())
7373
sectortype(::Type{CartesianSpace}) = Trivial
7474

7575
Base.show(io::IO, V::CartesianSpace) = print(io, "ℝ^$(V.d)")
76+
77+
sectorhash(V::CartesianSpace, h::UInt) = hash(dim(V) == 0, h)
78+
sectorequal(V₁::CartesianSpace, V₂::CartesianSpace) = iszero(dim(V₁)) == iszero(dim(V₂))

src/spaces/complexspace.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,10 @@ sectors(V::ComplexSpace) = OneOrNoneIterator(dim(V) != 0, Trivial())
8282
sectortype(::Type{ComplexSpace}) = Trivial
8383

8484
Base.show(io::IO, V::ComplexSpace) = print(io, isdual(V) ? "(ℂ^$(V.d))'" : "ℂ^$(V.d)")
85+
86+
function sectorhash(V::ComplexSpace, h::UInt)
87+
return hash(dim(V) == 0, hash(isdual(V), h))
88+
end
89+
function sectorequal(V₁::ComplexSpace, V₂::ComplexSpace)
90+
return isdual(V₁) == isdual(V₂) && iszero(dim(V₁)) == iszero(dim(V₂))
91+
end

src/tensors/tensor.jl

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,13 @@ struct TensorMap{T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} <: Abstrac
1111
data::A
1212
space::TensorMapSpace{S, N₁, N₂}
1313

14-
# uninitialized constructors
15-
function TensorMap{T, S, N₁, N₂, A}(
16-
::UndefInitializer, space::TensorMapSpace{S, N₁, N₂}
17-
) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}}
18-
data = A(undef, dim(space))
19-
if !isbitstype(T)
20-
zerovector!(data)
21-
end
22-
return TensorMap{T, S, N₁, N₂, A}(data, space)
23-
end
24-
# constructors from data
14+
# explicit inner constructor to prevent auto-generating TensorMap(data, space)
2515
function TensorMap{T, S, N₁, N₂, A}(
2616
data::A, space::TensorMapSpace{S, N₁, N₂}
2717
) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}}
28-
T field(S) || @warn("scalartype(data) = $T ⊈ $(field(S)))", maxlog = 1)
29-
I = sectortype(S)
30-
T <: Real && !(sectorscalartype(I) <: Real) &&
31-
@warn("Tensors with real data might be incompatible with sector type $I", maxlog = 1)
32-
length(data) == dim(space) || throw(DimensionMismatch(lazy"length of data ($(length(data))) does not match dimension of space ($(dim(space)))"))
3318
return new{T, S, N₁, N₂, A}(data, space)
3419
end
3520
end
36-
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))
3721

3822
"""
3923
Tensor{T, S, N, A<:DenseVector{T}} = TensorMap{T, S, N, 0, A}
@@ -46,11 +30,27 @@ i.e. a tensor map with only a non-trivial output space.
4630
"""
4731
const Tensor{T, S, N, A} = TensorMap{T, S, N, 0, A}
4832

33+
# Utility functions:
34+
# ------------------
4935
function tensormaptype(::Type{S}, N₁, N₂, ::Type{TorA}) where {S <: IndexSpace, TorA}
5036
A = similarstoragetype(TorA)
5137
return TensorMap{scalartype(A), S, N₁, N₂, A}
5238
end
5339

40+
function _warn_tensormap_compatibility(T, S)
41+
T field(S) || @warn("scalartype(data) = $T ⊈ $(field(S)))", maxlog = 1)
42+
I = sectortype(S)
43+
T <: Real && !(sectorscalartype(I) <: Real) &&
44+
@warn("Tensors with real data might be incompatible with sector type $I", maxlog = 1)
45+
return nothing
46+
end
47+
48+
function _check_tensormap_length(data, V)
49+
d = dim(V)
50+
length(data) == d || throw(DimensionMismatch(lazy"length of data ($(length(data))) does not match dimension of space ($d)"))
51+
return nothing
52+
end
53+
5454
# Basic methods for characterising a tensor:
5555
#--------------------------------------------
5656
space(t::TensorMap) = t.space
@@ -66,6 +66,8 @@ dim(t::TensorMap) = length(t.data)
6666

6767
# General TensorMap constructors
6868
# ==============================
69+
TensorMap{T, S, N₁, N₂, A}(t::TensorMap{T, S, N₁, N₂}) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}} =
70+
TensorMap{T, S, N₁, N₂, A}(A(t.data), space(t))
6971

7072
# INTERNAL: utility type alias that makes constructors also work for type aliases that specify
7173
# different storage types. (i.e. CuTensorMap = TensorMapWithStorage{T, CuVector{T}, ...})
@@ -89,6 +91,14 @@ TensorMap{T}(::UndefInitializer, V::TensorMapSpace) where {T} =
8991
TensorMap{T}(::UndefInitializer, codomain::TensorSpace, domain::TensorSpace) where {T} =
9092
TensorMap{T}(undef, codomain domain)
9193
Tensor{T}(::UndefInitializer, V::TensorSpace) where {T} = TensorMap{T}(undef, V one(V))
94+
function TensorMap{T, S, N₁, N₂, A}(
95+
::UndefInitializer, space::TensorMapSpace{S, N₁, N₂}
96+
) where {T, S <: IndexSpace, N₁, N₂, A <: DenseVector{T}}
97+
_warn_tensormap_compatibility(T, S)
98+
data = A(undef, dim(space))
99+
isbitstype(T) || zerovector!(data) # ensure initialized entries
100+
return TensorMap{T, S, N₁, N₂, A}(data, space)
101+
end
92102

93103
"""
94104
TensorMapWithStorage{T, A}(undef, codomain, domain) where {T, A}
@@ -122,10 +132,13 @@ TensorMap(t::TensorMap) = copy(t)
122132
Construct a `TensorMap` from the given raw data.
123133
This constructor takes ownership of the provided vector, and will not make an independent copy.
124134
"""
125-
TensorMap{T}(data::DenseVector{T}, V::TensorMapSpace) where {T} =
126-
tensormaptype(spacetype(V), numout(V), numin(V), typeof(data))(data, V)
127135
TensorMap{T}(data::DenseVector{T}, codomain::TensorSpace, domain::TensorSpace) where {T} =
128136
TensorMap{T}(data, codomain domain)
137+
function TensorMap{T}(data::DenseVector{T}, V::TensorMapSpace) where {T}
138+
_warn_tensormap_compatibility(T, spacetype(V))
139+
_check_tensormap_length(data, V)
140+
return tensormaptype(spacetype(V), numout(V), numin(V), typeof(data))(data, V)
141+
end
129142

130143
"""
131144
TensorMapWithStorage{T, A}(data::A, codomain, domain) where {T, A<:DenseVector{T}}
@@ -136,6 +149,8 @@ Construct a `TensorMap` from the given raw data.
136149
This constructor takes ownership of the provided vector, and will not make an independent copy.
137150
"""
138151
function TensorMapWithStorage{T, A}(data::A, V::TensorMapSpace) where {T, A}
152+
_warn_tensormap_compatibility(T, spacetype(V))
153+
_check_tensormap_length(data, V)
139154
return tensormaptype(spacetype(V), numout(V), numin(V), typeof(data))(data, V)
140155
end
141156
TensorMapWithStorage{T, A}(data::A, codomain::TensorSpace, domain::TensorSpace) where {T, A} =
@@ -213,13 +228,17 @@ end
213228
function TensorMapWithStorage{T, A}(
214229
data::AbstractArray, V::TensorMapSpace; tol = sqrt(eps(real(float(eltype(data)))))
215230
) where {T, A}
231+
_warn_tensormap_compatibility(T, spacetype(V))
232+
216233
# refer to specific raw data constructors if input is a vector of the correct length
217234
ndims(data) == 1 && length(data) == dim(V) &&
218235
return tensormaptype(spacetype(V), numout(V), numin(V), A)(data, V)
219236

220237
# special case trivial: refer to same method, but now with vector argument
221-
sectortype(V) === Trivial &&
238+
if sectortype(V) === Trivial
239+
_check_tensormap_length(data, V)
222240
return tensormaptype(spacetype(V), numout(V), numin(V), A)(reshape(data, length(data)), V)
241+
end
223242

224243
return project_symmetric_and_check(T, A, data, V; tol)
225244
end

0 commit comments

Comments
 (0)