Skip to content

Commit 0c0a967

Browse files
committed
upgraded TupleVector implementation
1 parent 1a16d45 commit 0c0a967

File tree

10 files changed

+374
-73
lines changed

10 files changed

+374
-73
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "AbstractTensors"
22
uuid = "a8e43f4a-99b7-5565-8bf1-0165161caaea"
33
authors = ["Michael Reed"]
4-
version = "0.6.6"
4+
version = "0.6.7"
55

66
[deps]
77
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

src/AbstractTensors.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ Base.cosc(t::T) where T<:TensorAlgebra = iszero(t) ? zero(Manifold(t)) : (x=(1π
262262
# absolute value norm
263263

264264
@inline Base.abs(t::T) where T<:TensorAlgebra = Base.sqrt(Base.abs2(t))
265-
@inline Base.abs2(t::T) where T<:TensorAlgebra = (a=contraction(t,t); isscalar(a) ? scalar(a) : a)
265+
@inline Base.abs2(t::T) where T<:TensorAlgebra = (a=(~t)*t; isscalar(a) ? scalar(a) : a)
266266
@inline Base.abs2(t::T) where T<:TensorGraded = contraction(t,t)
267267
@inline norm(z) = LinearAlgebra.norm(z)
268268
@inline LinearAlgebra.norm(t::T) where T<:TensorAlgebra = norm(value(t))

src/FixedVector.jl

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,31 @@
22
# This file is adapted from JuliaArrays/StaticArrays.jl License is MIT:
33
# https://github.com/JuliaArrays/StaticArrays.jl/blob/master/LICENSE.md
44

5-
struct FixedVector{N,T} <: TupleVector{N,T}
6-
v::Vector{T}
7-
function FixedVector{N,T}(a::Vector) where {N,T}
5+
require_one_based_indexing(A...) = !Base.has_offset_axes(A...) ||
6+
throw(ArgumentError("offset arrays are not supported but got an array with index other than 1"))
7+
8+
struct FixedVector{N,T,TData<:AbstractVector{T}} <: TupleVector{N,T}
9+
v::TData
10+
function FixedVector{N,T,TData}(a::TData) where {N,T,TData<:AbstractVector{T}}
11+
require_one_based_indexing(a)
812
if length(a) != N
913
throw(DimensionMismatch("Dimensions $(size(a)) don't match static size $S"))
1014
end
11-
new{N,T}(a)
15+
new{N,T,TData}(a)
1216
end
13-
function FixedVector{N,T}(::UndefInitializer) where {N,T}
14-
new{N,T}(Vector{T}(undef,N))
17+
function FixedVector{N,T,TData}(::UndefInitializer) where {N,T,TData<:AbstractVector{T}}
18+
new{N,T,TData}(TData(undef,N))
1519
end
1620
end
1721

18-
@inline FixedVector{N}(a::Vector{T}) where {N,T} = FixedVector{N,T}(a)
19-
20-
@generated function FixedVector{N,T}(x::NTuple{N,Any}) where {N,T}
22+
@inline function FixedVector{N,T}(a::TData) where {N,T,TData<:AbstractVector{T}}
23+
return FixedVector{N,T,TData}(a)
24+
end
25+
@inline function FixedVector{N}(a::TData) where {N,T,TData<:AbstractVector{T}}
26+
return FixedVector{N,T,TData}(a)
27+
end
28+
@inline FixedVector{N,T}(::UndefInitializer) where {N,T} = FixedVector{N,T,Vector{T}}(undef)
29+
@generated function (::Type{FixedVector{N,T,TData}})(x::NTuple{N,Any}) where {N,T,TData<:AbstractVector{T}}
2130
exprs = [:(a[$i] = x[$i]) for i = 1:N]
2231
return quote
2332
$(Expr(:meta, :inline))
@@ -27,27 +36,79 @@ end
2736
end
2837
end
2938

30-
@inline FixedVector{N,T}(x::Tuple) where {N,T} = FixedVector{N,T}(x)
39+
@inline FixedVector{N,T}(x::Tuple) where {N,T} = FixedVector{N,T,Vector{T}}(x)
3140
@inline FixedVector{N}(x::NTuple{N,T}) where {N,T} = FixedVector{N,T}(x)
3241

3342
# Overide some problematic default behaviour
34-
@inline Base.convert(::Type{SA}, sa::FixedVector) where {SA<:FixedVector} = SA(sa.v)
35-
@inline Base.convert(::Type{SA}, sa::SA) where {SA<:FixedVector} = sa
43+
@inline Base.convert(::Type{TV}, tv::FixedVector) where {TV<:FixedVector} = TV(tv.v)
44+
@inline Base.convert(::Type{TV}, tv::TV) where {TV<:FixedVector} = tv
3645

37-
# Back to Array (unfortunately need both convert and construct to overide other methods)
38-
@inline Base.Array(sa::FixedVector) = Vector(sa.v)
39-
@inline Base.Array{T}(sa::FixedVector{N,T}) where {N,T} = Vector{T}(sa.v)
40-
@inline Base.Array{T,1}(sa::FixedVector{N,T}) where {N,T} = Vector{T}(sa.v)
46+
# Back to Vector (unfortunately need both convert and construct to overide other methods)
47+
@inline Base.Vector(tv::FixedVector) = Vector(tv.v)
48+
@inline Base.Vector{T}(tv::FixedVector{N,T}) where {N,T} = Vector{T}(tv.v)
4149

42-
@inline Base.convert(::Type{Array}, sa::FixedVector) = sa.v
43-
@inline Base.convert(::Type{Array{T}}, sa::FixedVector{N,T}) where {N,T} = sa.v
44-
@inline Base.convert(::Type{Array{T,1}}, sa::FixedVector{N,T}) where {N,T} = sa.v
50+
@inline function Base.convert(::Type{Vector}, tv::FixedVector{N}) where N
51+
return Vector(tv.v)
52+
end
53+
@inline function Base.convert(::Type{Vector}, tv::FixedVector{N,T,Vector{T}}) where {N,T}
54+
return tv.v
55+
end
56+
@inline function Base.convert(::Type{Vector{T}}, tv::FixedVector{N,T}) where {N,T}
57+
return Vector(tv.v)
58+
end
59+
@inline function Base.convert(::Type{Vector{T}}, tv::FixedVector{N,T,Vector{T}}) where {N,T}
60+
return tv.v
61+
end
4562

4663
@propagate_inbounds Base.getindex(a::FixedVector, i::Int) = getindex(a.v, i)
4764
@propagate_inbounds Base.setindex!(a::FixedVector, v, i::Int) = setindex!(a.v, v, i)
4865

66+
Base.parent(tv::FixedVector) = tv.v
67+
68+
Base.pointer(tv::FixedVector) = pointer(tv.v)
69+
70+
Base.unsafe_convert(::Type{Ptr{T}}, tv::FixedVector) where T = Base.unsafe_convert(Ptr{T}, tv.v)
71+
Base.elsize(::Type{FixedVector{S,T}}) where {S,T} = Base.elsize(A)
72+
73+
FixedVector(a::TupleVector{N,T}) where {N,T} = FixedVector{N,T}(a)
74+
4975
Base.dataids(sa::FixedVector) = Base.dataids(sa.v)
5076

51-
function Base.promote_rule(::Type{<:FixedVector{N,T}}, ::Type{<:FixedVector{N,U}}) where {N,T,U}
52-
FixedVector{N,promote_type(T,U)}
77+
function Base.promote_rule(
78+
::Type{FixedVector{N,T,TDataA}},
79+
::Type{FixedVector{N,U,TDataB}},
80+
) where {N,T,U,TDataA,TDataB}
81+
return FixedVector{N, promote_type(T, U), promote_type(TDataA, TDataB)}
82+
end
83+
84+
function promote_rule(::Type{FixedVector{N,T}},::Type{FixedVector{N,U}}) where {N,T,U}
85+
return FixedVector{N, promote_type(T, U)}
86+
end
87+
88+
### Code that makes views of statically sized arrays also statically sized (where possible)
89+
90+
# Note, _get_tuple_vector_length is used in a generated function so it's strictly internal and can't be extended
91+
_get_tuple_vector_length(::Type{<:TupleVector{N}}) where N = N
92+
93+
@generated function new_out_size(::Type{Val{N}}, ind) where N
94+
if ind <: Integer
95+
Tuple{1} # dimension is fixed
96+
elseif ind <: TupleVector
97+
Tuple{_get_tuple_vector_length(ind)}
98+
elseif ind == Colon || ind <: Base.Slice
99+
Tuple{N}
100+
elseif ind <: SOneTo
101+
Tuple{ind.parameters[1]}
102+
else
103+
error("Unknown index type: $ind")
104+
end
105+
end
106+
107+
@generated new_out_size(::Type{Val{N}}, ::Colon) where N = Tuple{N}
108+
109+
function Base.view(
110+
a::FixedVector{N},
111+
index::Union{Integer, Colon, TupleVector, Base.Slice, SOneTo},
112+
) where N
113+
return FixedVector{new_out_size(Val(N), index)}(view(a.v, index))
53114
end

src/Values.jl

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,77 @@ end
1717
end
1818
end
1919

20+
@noinline function generator_too_short_error(inds::CartesianIndices, i::CartesianIndex)
21+
error("Generator produced too few elements: Expected exactly $(shape_string(inds)) elements, but generator stopped at $(shape_string(i))")
22+
end
23+
@noinline function generator_too_long_error(inds::CartesianIndices)
24+
error("Generator produced too many elements: Expected exactly $(shape_string(inds)) elements, but generator yields more")
25+
end
26+
27+
shape_string(inds::CartesianIndices) = join(length.(inds.indices), '×')
28+
shape_string(inds::CartesianIndex) = join(Tuple(inds), '×')
29+
30+
@inline throw_if_nothing(x, inds, i) =
31+
(x === nothing && generator_too_short_error(inds, i); x)
32+
33+
@generated function tvcollect(::Type{TV}, gen) where {TV <: TupleVector{N}} where N
34+
stmts = [:(Base.@_inline_meta)]
35+
args = []
36+
iter = :(iterate(gen))
37+
inds = CartesianIndices(Tuple(N))
38+
for i in inds
39+
el = Symbol(:el, i)
40+
push!(stmts, :(($el,st) = throw_if_nothing($iter, $inds, $i)))
41+
push!(args, el)
42+
iter = :(iterate(gen,st))
43+
end
44+
push!(stmts, :($iter === nothing || generator_too_long_error($inds)))
45+
push!(stmts, :(TV($(args...))))
46+
Expr(:block, stmts...)
47+
end
48+
"""
49+
tvcollect(TV, gen)
50+
51+
Construct a statically-sized vector of type `TV`.from a generator
52+
`gen`. `TV` needs to have a size parameter since the length of `vec`
53+
is unknown to the compiler. `TV` can optionally specify the element
54+
type as well.
55+
56+
Example:
57+
58+
tvcollect(Values{3, Int}, 2i+1 for i in 1:3)
59+
60+
This creates the same statically-sized vector as if the generator were
61+
collected in an array, but is more efficient since no array is
62+
allocated.
63+
64+
Equivalent:
65+
66+
Values{3, Int}([2i+1 for i in 1:3])
67+
"""
68+
tvcollect
69+
70+
@inline (::Type{TV})(gen::Base.Generator) where {TV <: TupleVector} =
71+
tvcollect(TV, gen)
72+
2073
@inline Values(a::TupleVector{N}) where N = Values{N}(Tuple(a))
2174
@propagate_inbounds Base.getindex(v::Values, i::Int) = v.v[i]
2275
@inline Tuple(v::Values) = v.v
2376
Base.dataids(::Values) = ()
2477

2578
# See #53
2679
Base.cconvert(::Type{Ptr{T}}, a::Values) where {T} = Base.RefValue(a)
27-
Base.unsafe_convert(::Type{Ptr{T}}, a::Base.RefValue{SA}) where {N,T,SA<:Values{N,T}} = Ptr{T}(Base.unsafe_convert(Ptr{Values{N,T}}, a))
80+
Base.unsafe_convert(::Type{Ptr{T}}, a::Base.RefValue{TV}) where {N,T,TV<:Values{N,T}} = Ptr{T}(Base.unsafe_convert(Ptr{Values{N,T}}, a))
2881

2982
# SVector.jl
3083

3184
@inline Values(x::NTuple{N,Any}) where N = Values{N}(x)
3285
@inline Values{N}(x::NTuple{N,T}) where {N,T} = Values{N,T}(x)
3386
@inline Values{N}(x::T) where {N,T<:Tuple} = Values{N,promote_tuple_eltype(T)}(x)
3487

88+
@inline Values{N, T}(gen::Base.Generator) where {N, T} = tvcollect(Values{N, T}, gen)
89+
@inline Values{N}(gen::Base.Generator) where {N} = tvcollect(Values{N}, gen)
90+
3591
# Some more advanced constructor-like functions
3692
@pure @inline Base.zeros(::Type{Values{N}}) where N = zeros(Values{N,Float64})
3793
@pure @inline Base.ones(::Type{Values{N}}) where N = ones(Values{N,Float64})

src/Variables.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mutable struct Variables{N,T} <: TupleVector{N,T}
1111
Variables{N,T}(::UndefInitializer) where {N,T} = new{N,T}()
1212
end
1313

14-
@inline Variables(a::TupleVector{N}) where N = Variables{N}(Tuple(a))
14+
@inline Variables(a::TupleVector{N,T}) where {N,T} = Variables{N,T}(Tuple(a))
1515
@generated function (::Type{Variables{N,T}})(x::Tuple) where {N,T}
1616
return quote
1717
$(Expr(:meta, :inline))
@@ -43,7 +43,7 @@ end
4343
# unsafe_store!(Base.unsafe_convert(Ptr{Ptr{Nothing}}, pointer_from_objref(v.data)), pointer_from_objref(val), i)
4444
error("setindex!() with non-isbitstype eltype is not supported by TupleVectors. Consider using FixedVector.")
4545
end
46-
return val
46+
return v
4747
end
4848

4949
@inline Base.Tuple(v::Variables) = v.v
@@ -57,8 +57,19 @@ function Base.promote_rule(::Type{<:Variables{N,T}}, ::Type{<:Variables{N,U}}) w
5757
Variables{N,promote_type(T,U)}
5858
end
5959

60+
function Base.view(
61+
a::Variables{N},
62+
index::Union{Integer, Colon, TupleVector, Base.Slice, SOneTo},
63+
) where N
64+
view_from_invoke = invoke(view, Tuple{AbstractVector, typeof(index)}, a, index)
65+
return FixedVector{new_out_size(Val(N), index)}(view_from_invoke)
66+
end
67+
68+
Base.elsize(::Type{<:Variables{S,T}}) where {S,T} = sizeof(T)
69+
6070
# MVector.jl
6171

72+
@inline Variables(x::Values{N,T}) where {N,T} = Variables{N,T}(x)
6273
@inline Variables(x::NTuple{N,Any}) where N = Variables{N}(x)
6374
@inline Variables{N}(x::NTuple{N,T}) where {N,T} = Variables{N,T}(x)
6475
@inline Variables{N}(x::NTuple{N,Any}) where N = Variables{N, promote_tuple_eltype(typeof(x))}(x)

src/abstractvector.jl

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,26 @@ similar_type(::Type{SA}) where {SA<:TupleVector} = similar_type(SA,eltype(SA))
2121
similar_type(::SA,::Type{T}) where {SA<:TupleVector{N},T} where N = similar_type(SA,T,Val(N))
2222
similar_type(::Type{SA},::Type{T}) where {SA<:TupleVector{N},T} where N = similar_type(SA,T,Val(N))
2323

24-
similar_type(::A,n::Val) where {A<:AbstractArray} = similar_type(A,eltype(A),n)
25-
similar_type(::Type{A},n::Val) where {A<:AbstractArray} = similar_type(A,eltype(A),n)
24+
similar_type(::A,n::Val) where {A<:AbstractVector} = similar_type(A,eltype(A),n)
25+
similar_type(::Type{A},n::Val) where {A<:AbstractVector} = similar_type(A,eltype(A),n)
2626

27-
similar_type(::A,::Type{T},n::Val) where {A<:AbstractArray,T} = similar_type(A,T,n)
27+
similar_type(::A,::Type{T},n::Val) where {A<:AbstractVector,T} = similar_type(A,T,n)
2828

2929
# We should be able to deal with SOneTo axes
3030
@pure similar_type(s::SOneTo) = similar_type(typeof(s))
3131
@pure similar_type(::Type{SOneTo{n}}) where n = similar_type(SOneTo{n}, Int, Val(n))
3232

3333
# Default types
34-
# Generally, use SArray
35-
similar_type(::Type{A},::Type{T},n::Val) where {A<:AbstractArray,T} = default_similar_type(T,n)
34+
# Generally, use TupleVector
35+
similar_type(::Type{A},::Type{T},n::Val) where {A<:AbstractVector,T} = default_similar_type(T,n)
3636
default_similar_type(::Type{T},::Val{N}) where {N,T} = Values{N,T}
3737

3838
similar_type(::Type{SA},::Type{T},n::Val) where {SA<:Variables,T} = mutable_similar_type(T,n)
3939

4040
mutable_similar_type(::Type{T},::Val{N}) where {N,T} = Variables{N,T}
4141

4242
similar_type(::Type{<:FixedVector},::Type{T},n::Val) where T = sizedarray_similar_type(T,n)
43-
# Should FixedVector also be used for normal Array?
43+
# Should FixedVector also be used for normal Vector?
4444
#similar_type(::Type{<:Array},::Type{T},n::Val) where T = sizedarray_similar_type(T,n)
4545

4646
sizedarray_similar_type(::Type{T},::Val{N}) where {N,T} = FixedVector{N,T}
@@ -52,17 +52,17 @@ Base.similar(::SA,::Type{T}) where {SA<:TupleVector{N},T} where N = similar(SA,T
5252
Base.similar(::Type{SA},::Type{T}) where {SA<:TupleVector{N},T} where N = similar(SA,T,Val(N))
5353

5454
# Cases where a Val is given as the dimensions
55-
Base.similar(::A,n::Val) where A<:AbstractArray = similar(A,eltype(A),n)
56-
Base.similar(::Type{A},n::Val) where A<:AbstractArray = similar(A,eltype(A),n)
55+
Base.similar(::A,n::Val) where A<:AbstractVector = similar(A,eltype(A),n)
56+
Base.similar(::Type{A},n::Val) where A<:AbstractVector = similar(A,eltype(A),n)
5757

58-
Base.similar(::A,::Type{T},n::Val) where {A<:AbstractArray,T} = similar(A,T,n)
58+
Base.similar(::A,::Type{T},n::Val) where {A<:AbstractVector,T} = similar(A,T,n)
5959

6060
# defaults to built-in mutable types
61-
Base.similar(::Type{A},::Type{T},n::Val) where {A<:AbstractArray,T} = mutable_similar_type(T,n)(undef)
61+
Base.similar(::Type{A},::Type{T},n::Val) where {A<:AbstractVector,T} = mutable_similar_type(T,n)(undef)
6262

6363
# both FixedVector and Array return FixedVector
6464
Base.similar(::Type{SA},::Type{T},n::Val) where {SA<:FixedVector,T} = sizedarray_similar_type(T,n)(undef)
65-
Base.similar(::Type{A},::Type{T},n::Val) where {A<:Array,T} = sizedarray_similar_type(T,n)(undef)
65+
Base.similar(::Type{A},::Type{T},n::Val) where {A<:Vector,T} = sizedarray_similar_type(T,n)(undef)
6666

6767
# Support tuples of mixtures of `SOneTo`s alongside the normal `Integer` and `OneTo` options
6868
# by simply converting them to either a tuple of Ints or a Val, re-dispatching to either one
@@ -105,3 +105,17 @@ end
105105
@inbounds return similar_type(a, promote_type(eltype(a), eltype(b)), Val($Snew))(tuple($(exprs...)))
106106
end
107107
end
108+
109+
if VERSION >= v"1.6.0-DEV.1334"
110+
# FIXME: This always assumes one-based linear indexing and that subtypes of TupleVector
111+
# don't overload iterate
112+
@inline function Base.rest(a::TupleVector{N}, (_, i) = (nothing, 0)) where N
113+
return similar_type(typeof(a), Val(N - i))(Base.rest(Tuple(a), i + 1))
114+
end
115+
end
116+
117+
# SArrays may avoid the SubArray wrapper and consequently an additional level of indirection
118+
# The output may use the broadcasting machinery defined for StaticArrays (see issue #892)
119+
function Base.view(S::Values, I::Union{Colon, Integer, SOneTo, TupleVector{N, Int} where N, CartesianIndex})
120+
getindex(S, I)
121+
end

src/convert.jl

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,25 @@
77

88
@inline (::Type{SA})(x...) where {SA <: TupleVector} = SA(x)
99
@inline (::Type{SA})(a::TupleVector) where {SA<:TupleVector} = SA(Tuple(a))
10-
@propagate_inbounds (::Type{SA})(a::AbstractArray) where {SA <: TupleVector} = convert(SA, a)
10+
@propagate_inbounds (::Type{SA})(a::AbstractVector) where {SA <: TupleVector} = convert(SA, a)
1111

1212
# this covers most conversions and "statically-sized reshapes"
1313
#@inline Base.convert(::Type{SA}, sa::TupleVector) where {SA<:TupleVector} = SA(Tuple(sa))
1414
#@inline Base.convert(::Type{SA}, sa::SA) where {SA<:TupleVector} = sa
1515
@inline Base.convert(::Type{SA}, x::Tuple) where {SA<:TupleVector} = SA(x) # convert -> constructor. Hopefully no loops...
1616

17-
# support conversion to AbstractArray
18-
Base.AbstractArray{T}(sa::TupleVector{N,T}) where {N,T} = sa
19-
Base.AbstractArray{T,1}(sa::TupleVector{N,T}) where {N,T} = sa
20-
Base.AbstractArray{T}(sa::TupleVector{N,U}) where {N,T,U} = similar_type(typeof(sa),T,Val(N))(sa)
21-
Base.AbstractArray{T,1}(sa::TupleVector{N,U}) where {N,T,U} = similar_type(typeof(sa),T,Val(N))(sa)
17+
# support conversion to AbstractVector
18+
Base.AbstractVector{T}(sa::TupleVector{N,T}) where {N,T} = sa
19+
Base.AbstractVector{T}(sa::TupleVector{N,U}) where {N,T,U} = similar_type(typeof(sa),T,Val(N))(sa)
2220

2321
# Constructing a Tuple from a TupleVector
2422
@inline Base.Tuple(a::TupleVector{N}) where N = unroll_tuple(a, Val(N))
2523

26-
@noinline function dimension_mismatch_fail(SA::Type, a::AbstractArray)
27-
throw(DimensionMismatch("expected input array of length $(length(SA)), got length $(length(a))"))
24+
@noinline function dimension_mismatch_fail(SA::Type, a::AbstractVector)
25+
throw(DimensionMismatch("expected input vector of length $(length(SA)), got length $(length(a))"))
2826
end
2927

30-
@propagate_inbounds function Base.convert(::Type{SA}, a::AbstractArray) where {SA<:TupleVector{N}} where N
28+
@propagate_inbounds function Base.convert(::Type{SA}, a::AbstractVector) where {SA<:TupleVector{N}} where N
3129
@boundscheck if length(a) != length(SA)
3230
dimension_mismatch_fail(SA, a)
3331
end
@@ -45,10 +43,14 @@ end
4543
@pure length_val(a::T) where {T <: TupleMatrixLike{S}} where S = Val(S)
4644
@pure length_val(a::Type{T}) where {T<:TupleMatrixLike{S}} where S = Val(S)
4745

48-
@generated function unroll_tuple(a::AbstractArray, ::Val{N}) where N
46+
@generated function unroll_tuple(a::AbstractVector, ::Val{N}) where N
4947
exprs = [:(a[$j]) for j = 1:N]
5048
quote
5149
@_inline_meta
5250
@inbounds return $(Expr(:tuple, exprs...))
5351
end
5452
end
53+
54+
# `float` and `real` of TupleVector types, analogously to application to scalars (issue 935)
55+
Base.float(::Type{TV}) where TV<:TupleVector{T,_N} where {T,_N} = similar_type(TV, float(T))
56+
Base.real(::Type{TV}) where TV<:TupleVector{T,_N} where {T,_N} = similar_type(TV, real(T))

0 commit comments

Comments
 (0)