How to mutate static arrays?

From the README:

The package provides a range of different useful built-in StaticArray types, which include mutable and immutable arrays based upon tuples, arrays based upon structs, and wrappers of Array. There is a relatively simple interface for creating your own, custom StaticArray types, too.

I tried I to mutate a simple SVector, but there is no setindex! defined for it:

using StaticArrays

v = @SVector [1,2,3]
v[1] = 0
julia> v[1] = 0

ERROR: setindex!(::SVector{3,Int64}, value, ::Int) is not defined.
Stacktrace:
 [1] setindex!(::SVector{3,Int64}, ::Int64, ::Int64) at /home/juliohm/.julia/v0.6/StaticArrays/src/indexing.jl:3
2 Likes

SVector is immutable. If you want a mutable container, you need an MVector

6 Likes

There is actually a setindex method for SVectors. It’s not in-place, but replaces the entire vector (note the absense of a !).

For short vectors setindex(::SVector, val, ind) has similar performance to setindex!(::MVector, ...), but as the vector lengths grow performance degrades.

Try it out:

v = @SVector [1,2,3]
v = setindex(v, 0, 1)

This one is as fast as an MVector, so you can stick to SVectors.

6 Likes

Thank you @DNF, can you advise on the choice between SVector and MVector? What is their different in terms of application?

In my application, these vectors will be of very small length like 3 in most cases. I have a normalization operation that I would like to perform in place, so I picked MVector for now:

"""
    Composition{D}(parts)
A D-part composition as defined by Aitchison 1986.
"""
struct Composition{D}
  parts::MVector{D,Float64}
end

Composition(parts) = Composition{length(parts)}(parts)

"""
Normalize composition `c` in place.
"""
function normalize!(c::Composition)
  p = c.parts
  s = sum(p)
  for i in eachindex(p)
    p[i] /= s
  end
end

Perhaps this helps

1 Like

If you have many of these points stored in something like a Vector then I would use static vectors and just update in place as a[i] = a[i] / sum(a) , where a is the collection of points. Static vectors are stored inline and fetching memory is often a bottleneck in computation heavy code.

4 Likes

The big advantage of SVectors is that they are isbits types, so they can be cheaply allocated on the stack and stored inline in arrays. MVectors are mutable, and thus not isbits, so allocating them is more expensive. MVectors can still be useful, since their fixed size enables lots of specialized algorithms and loop unrolling, but I find that I almost always end up using SVectors in my work.

9 Likes

But, how can this work? There is no setindex! for a staticarray. This causes an error. There is no way around immutibility that I can see.

a is a normal Vector and a[i] is a static vector. a is what is being mutated.

2 Likes

That’s clearer than the docs. Shows why we can do comprehensions and zeros, ones, etc—that all replace the vector in place.

And do we replace the whole enchilada in place very quickly without allocations?

1 Like

Yes.

2 Likes

I don’t understand this point, how can we mutate a static array??? without using MArray…

What he meant is this:

julia> using StaticArrays

julia> x = [ rand(SVector{2,Float64}) for _ in 1:2 ] # x is mutable
2-element Vector{SVector{2, Float64}}:
 [0.8394020601377467, 0.028511506698374878]
 [0.4393314594143619, 0.021740546734858035]

julia> x[1] = zeros(SVector{2,Float64}) # mutating the first element of x
2-element SVector{2, Float64} with indices SOneTo(2):
 0.0
 0.0

julia> x
2-element Vector{SVector{2, Float64}}:
 [0.0, 0.0]
 [0.4393314594143619, 0.021740546734858035]


x[1] was mutated, but the static array inside it was not, it was just replaced by another one.

2 Likes