Hi! Suppose I want to code a function that returns a SVector but each component is computed inside the function without knowing the size. Should I create a MVector and then cast that to a SVector? This is what I am thinking:
function test(::Val{D}) where {D}
mv = @MVector zeros(D)
for i in 1:D
mv[i] = i + 1
end
sv = convert(SVector, mv)
return sv
end
Then:
test(Val{4}())
does not allocate and is type stable. But I don’t know if this is the correct approach. Thanks!
Note that the original poster on Slack cannot see your response here on Discourse. Consider transcribing the appropriate answer back to Slack, or pinging the poster here on Discourse so they can follow this thread. (Original message )(More Info)
The key is that an SVector can be constructed from a tuple, and a tuple can be constructed efficiently using ntuple with a Val argument so that the size is known at compile time.
I have a more complicated case, where each value in the vector depends on more complicated expressions. This is the actual function that I am coding:
@inline function drift(u, p::LMMP{false,D,D,true,Terminal}, t) where {D}
@unpack Tenors, τ, ρ = p
σ = p.σ(t)
du = @MVector zeros(eltype(u), D)
@inbounds begin
# 'i' ranges from 2 to D-1 because L₁ and LN have μ = 0
for i in 2:D-1
if t ≤ Tenors[i]
for j in i+1:D
du[i] += (ρ[i,j] * τ[j] * σ[j] * u[j]) / (1 + τ[j] * u[j])
end
du[i] *= (-σ[i] * u[i])
end
end
end
return convert(SVector, m)
end
I have tested both and using MVector is faster… This is a MWE:
using BenchmarkTools
using StaticArrays
using UnPack
N = 10
τ = @SVector [0.25 for i in 1:N]
Tenors = vcat(zero(eltype(τ)), cumsum(τ))
σ(t) = @SVector ones(10)
ρ = @SMatrix rand(N,N)
u = @SVector ones(N)
p = (τ = τ, Tenors = Tenors, σ = σ, ρ = ρ)
t = 0.1
val = Val{N}()
@inline function driftlmm1(u, p, t, ::Val{D}) where {D}
@unpack Tenors, τ, ρ = p
σ = p.σ(t)
du = @MVector zeros(eltype(u), D)
@inbounds begin
# 'i' ranges from 2 to D-1 because L₁ and LD have μ = 0
for i in 2:D-1
if t ≤ Tenors[i]
for j in i+1:D
du[i] += (ρ[i,j] * τ[j] * σ[j] * u[j]) / (1 + τ[j] * u[j])
end
du[i] *= (-σ[i] * u[i])
end
end
end
return convert(SVector, du)
end
@btime driftlmm1($u, $p, $t, $val)
# 152.777 ns (0 allocations: 0 bytes)
@inline function driftlmm2(u, p, t, ::Val{D}) where {D}
@unpack Tenors, τ, ρ = p
σ = p.σ(t)
@inbounds begin
return SVector(ntuple(Val{D}()) do i
du = zero(eltype(u))
if t ≤ Tenors[i]
for j in i+1:D
du += (ρ[i,j] * τ[j] * σ[j] * u[j]) / (1 + τ[j] * u[j])
end
du *= (-σ[i] * u[i])
end
du
end)
end
end
@btime driftlmm2($u, $p, $t, $val)
# 178.298 ns (0 allocations: 0 bytes)