Assume my NamedTuple contains only floating point values T and SVector{N, T} entries, e.g.,
using StaticArrays
nt = ( a = rand(), b = SA[1.0, 2.0 ,3.0] )
Taking such a NamedTuple and interpreting it as an SVector is straightforward
v = reinterpret(SVector{4, Float64}, nt)
I can write a @generated function that does the reverse, but it is awful (see below) and I just wondered whether is a more straightforward way to achieve this?
@generated function _svec2nt(v::SVector, x::NamedTuple)
SYMS = fieldnames(x)
TT = fieldtypes(x)
_len(::Type{T}) where {T <: Number} = 1
_len(::Type{SVector{N, T}}) where {N, T <: Number} = N
i0 = Int[]
idx = 1
for T in TT
push!(i0, idx)
idx += _len(T)
end
push!(i0, idx)
# indexing into v::SVector
inds = []
for i = 1:length(TT)
rg = i0[i]:i0[i+1]-1
if length(rg) == 1
push!(inds, "$(first(rg))")
else
rg = SVector(rg...)
push!(inds, "SA$rg")
end
end
code = "(; "
for (sym, ind) in zip(SYMS, inds)
code *= "$sym = v[$ind], "
end
code *= ")"
return quote
$(Meta.parse(code))
end
end
julia> nt = ( a = rand(), b = SA[1.0, 2.0 ,3.0] )
(a = 0.4808058471551182, b = [1.0, 2.0, 3.0])
julia> v = reinterpret(SVector{4, Float64}, nt)
4-element SVector{4, Float64} with indices SOneTo(4):
0.4808058471551182
1.0
2.0
3.0
julia> reinterpret(typeof(nt), Tuple(v))
(a = 0.4808058471551182, b = [1.0, 2.0, 3.0])
For some reason, one has to use Tuple(v) instead of v.
reinterpret(T::DataType, A::AbstractArray) tries to lazily reinterpret an array as an array of a different element type, rather than an instance of said type.
If we try to index it though, at some point it tries to convert the reinterpreted axes 1:1 to the type of the parent SVector’s axes SOneTo{4}, which is only compatible with 1:4. Not really sure why that convert happens, maybe for automatic offset indices?