Vector of MVectors

What am I doing wrong? I want to have a vector of MVectors, but each of them shall be different.

using StaticArrays
vec=zeros(MVector{3, Float64}, 4)
vec[1] .= [1,2,3]
vec

has as output:

 [1.0, 2.0, 3.0]
 [1.0, 2.0, 3.0]
 [1.0, 2.0, 3.0]
 [1.0, 2.0, 3.0]

but I expected:

 [1.0, 2.0, 3.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]

How can I fix this?

1 Like

Use vec[1] = [1,2,3]

Not the answer I am looking for. I want to create an array of distinct MVectors at the very beginning to allocated the required memory before logging my real-time data.

This approach would also fail in reality because I am not assigning [1,2,3], but a vector that is always the same vector, just with different values.

Side question: why a vector of MVector? If you have an array of SVector it will typically be more efficient and you can still mutate the array contents:

julia> using StaticArrays

julia> vec = zeros(SVector{3, Float64}, 4)
4-element Vector{SVector{3, Float64}}:
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]

julia> vec[1] = @SVector[1,2,3];

julia> vec
4-element Vector{SVector{3, Float64}}:
 [1.0, 2.0, 3.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]

This is analogous to the fact that numbers are immutable (you can’t change the value of 4), but if you have an array of numbers you can mutate the container to replace an element with a different number.

PS. vec[1] = [1,2,3] also works, but unnecessarily heap-allocates a Vector for the right-hand-side before converting it to an SVector to store in the array vec.

This approach would also fail in reality because I am not assigning [1,2,3], but a vector that is always the same vector, just with different values.

PPS. If the vector is an SVector, you don’t need to worry about pre-allocating it and then overwriting it in place. Part of the whole point of using StaticArrays is that “allocating” them as local variables is essentially free, in the same way that you don’t worry about “allocating” new integers when you compute n = n + 1 in a loop.

3 Likes

I use now:

using StaticArrays
vec=zeros(MVector{3, Float64}, 4)
vec[1] = copy([1,2,3])
vec

Which works as expected.

But the behavior shown in the first post is still unexpected. Can anybody explain what is going on there? Is that a bug?

This does seem like a bug. Perhaps better demonstrated by vec[1][1] = 99 – you want to mutate one element.

@less zeros(MVector{3, Float64}, (4,)) shows that it’s doing fill!(vec, zero(T)). StaticArrays could overload things to avoid this. But perhaps Base shouldn’t be assuming fill! is safe?

It certainly seems like it would be friendlier for the zeros function to have a specialized implementation for MVector, but I’m not sure it’s a bug: your code is equivalent to fill(zero(MVector{3, Float64}), 4), which only allocates a single MVector and re-uses it for all elements, which leads to what you saw if you mutate it. (You’d see the same problem if you tried vec[1][1] = 3.)

Do:

vec = [zero(MVector{3, Float64}) for _=1:4]

to ensure that each element of vec is a distinct MVector.

PS. See also Current behavior of fill and the like..? and many other discussions.

1 Like

This question has come up before. See this post.

1 Like

To answer the original question, use

vec = [MVector(0.,0.,0.) for _ in 1:4]

Then vec[1] .= [1,2,3] produces

julia> vec
4-element Vector{MVector{3, Float64}}:
 [1.0, 2.0, 3.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0]
1 Like

zeros is fill-like. You did actually broadcast over the 3 elements of vec[1], but vec had 4 of the same objects so you see it everywhere.

julia> x = zeros(BigInt, 2); x[1] === x[2] # mutable type
true

julia> x = [zero(BigInt) for i in 1:2]; x[1] === x[2]
false

IMO it should be documented and perhaps have an option for separate instantiation.

This is the old issue debating it: zeros etc. is arguably broken for mutable element types · Issue #29168 · JuliaLang/julia. I would’ve definitely been on the side of “if I wanted the same instance, I’d use fill(zero(T), dims...),” but that’s a subjective expectation and enough people went the other way. There also seemed to be an expectation that each element has a numeric type with immutable API (even the technically mutably implemented BigInt), which StaticArrays is deliberately flying in the face of; think about it, would we ever zero(Vector{Float64})? It’s understandable because there isn’t an API for filling arbitrary AbstractArrays (zeros/ones/fill only instantiate Array, and zeros/ones document so), and StaticArrays straddle the line between scalar and array.

5 Likes