8
8
[ ![ Build status] ( https://ci.appveyor.com/api/projects/status/xabgh1yhsjxlp30d?svg=true )] ( https://ci.appveyor.com/project/JuliaArrays/staticarrays-jl )
9
9
[ ![ Coverage Status] ( https://coveralls.io/repos/github/JuliaArrays/StaticArrays.jl/badge.svg?branch=master )] ( https://coveralls.io/github/JuliaArrays/StaticArrays.jl?branch=master )
10
10
[ ![ codecov.io] ( http://codecov.io/github/JuliaArrays/StaticArrays.jl/coverage.svg?branch=master )] ( http://codecov.io/github/JuliaArrays/StaticArrays.jl?branch=master )
11
+ [ ![ ] ( https://img.shields.io/badge/docs-latest-blue.svg )] ( https://JuliaArrays.github.io/StaticArrays.jl/latest )
12
+ [ ![ ] ( https://img.shields.io/badge/docs-stable-blue.svg )] ( https://JuliaArrays.github.io/StaticArrays.jl/stable )
11
13
12
14
** StaticArrays** provides a framework for implementing statically sized arrays
13
15
in Julia (≥ 0.5), using the abstract type ` StaticArray{Size,T,N} <: AbstractArray{T,N} ` .
@@ -22,6 +24,7 @@ Mutable versions `MVector`, `MMatrix` and `MArray` are also exported, as well
22
24
as ` SizedArray ` for annotating standard ` Array ` s with static size information.
23
25
Further, the abstract ` FieldVector ` can be used to make fast ` StaticVector ` s
24
26
out of any uniform Julia "struct".
27
+ Full documentation can be found [ here] ( https://JuliaArrays.github.io/StaticArrays.jl/stable/ ) .
25
28
26
29
## Speed
27
30
@@ -141,309 +144,6 @@ precise dimensions of the input. In combination with intelligent fallbacks to
141
144
the methods in Base, we seek to provide a comprehensive support for statically
142
145
sized arrays, large or small, that hopefully "just works".
143
146
144
- ## API Details
145
-
146
- ### The ` Size ` trait
147
-
148
- The size of a statically sized array is a static parameter associated with the
149
- type of the array. The ` Size ` trait is provided as an abstract representation of
150
- the dimensions of a static array. An array ` sa::SA ` of size ` (dims...) ` is
151
- associated with ` Size{(dims...)}() ` . The following are equivalent (` @pure ` )
152
- constructors:
153
- ``` julia
154
- Size {(dims...)} ()
155
- Size (dims... )
156
- Size (sa:: StaticArray )
157
- Size (SA) # SA <: StaticArray
158
- ```
159
- This is extremely useful for (a) performing dispatch depending on the size of an
160
- array, and (b) passing array dimensions that the compiler can reason about.
161
-
162
- An example of size-based dispatch for the determinant of a matrix would be:
163
- ``` julia
164
- det (x:: StaticMatrix ) = _det (Size (x), x)
165
- _det (:: Size{(1,1)} , x:: StaticMatrix ) = x[1 ,1 ]
166
- _det (:: Size{(2,2)} , x:: StaticMatrix ) = x[1 ,1 ]* x[2 ,2 ] - x[1 ,2 ]* x[2 ,1 ]
167
- # and other definitions as necessary
168
- ```
169
-
170
- Examples of using ` Size ` as a compile-time constant include
171
- ``` julia
172
- reshape (svector, Size (2 ,2 )) # Convert SVector{4} to SMatrix{2,2}
173
- Size (3 ,3 )(rand (3 ,3 )) # Construct a random 3×3 SizedArray (see below)
174
- ```
175
-
176
- ### Indexing
177
-
178
- Statically sized indexing can be realized by indexing each dimension by a
179
- scalar, a ` StaticVector ` or ` : ` . Indexing in this way will result a statically
180
- sized array (even if the input was dynamically sized, in the case of
181
- ` StaticVector ` indices) of the closest type (as defined by ` similar_type ` ).
182
-
183
- Conversely, indexing a statically sized array with a dynamically sized index
184
- (such as a ` Vector{Integer} ` or ` UnitRange{Integer} ` ) will result in a standard
185
- (dynamically sized) ` Array ` .
186
-
187
- ### ` similar_type() `
188
-
189
- Since immutable arrays need to be constructed "all-at-once", we need a way of
190
- obtaining an appropriate constructor if the element type or dimensions of the
191
- output array differs from the input. To this end, ` similar_type ` is introduced,
192
- behaving just like ` similar ` , except that it returns a type. Relevant methods
193
- are:
194
-
195
- ``` julia
196
- similar_type {A <: StaticArray} (:: Type{A} ) # defaults to A
197
- similar_type {A <: StaticArray, ElType} (:: Type{A} , :: Type{ElType} ) # Change element type
198
- similar_type {A <: AbstractArray} (:: Type{A} , size:: Size ) # Change size
199
- similar_type {A <: AbstractArray, ElType} (:: Type{A} , :: Type{ElType} , size:: Size ) # Change both
200
- ```
201
-
202
- These setting will affect everything, from indexing, to matrix multiplication
203
- and ` broadcast ` . Users wanting introduce a new array type should * only* overload
204
- the last method in the above.
205
-
206
- Use of ` similar ` will fall back to a mutable container, such as a ` MVector `
207
- (see below), and it requires use of the ` Size ` trait if you wish to set a new
208
- static size (or else a dynamically sized ` Array ` will be generated when
209
- specifying the size as plain integers).
210
-
211
- ### ` SVector `
212
-
213
- The simplest static array is the type ` SVector{N,T} ` , which provides an
214
- immutable vector of fixed length ` N ` and type ` T ` .
215
-
216
- ` SVector ` defines a series of convenience constructors, so you can just type
217
- e.g. ` SVector(1,2,3) ` . Alternatively there is an intelligent ` @SVector ` macro
218
- where you can use native Julia array literals syntax, comprehensions, and the
219
- ` zeros() ` , ` ones() ` , ` fill() ` , ` rand() ` and ` randn() ` functions, such as ` @SVector [1,2,3] ` ,
220
- ` @SVector Float64[1,2,3] ` , ` @SVector [f(i) for i = 1:10] ` , ` @SVector zeros(3) ` ,
221
- ` @SVector randn(Float32, 4) ` , etc (Note: the range of a comprehension is evaluated at global scope by the
222
- macro, and must be made of combinations of literal values, functions, or global
223
- variables, but is not limited to just simple ranges. Extending this to
224
- (hopefully statically known by type-inference) local-scope variables is hoped
225
- for the future. The ` zeros() ` , ` ones() ` , ` fill() ` , ` rand() ` and ` randn() ` functions do not have this
226
- limitation.)
227
-
228
- ### ` SMatrix `
229
-
230
- Statically sized ` N×M ` matrices are provided by ` SMatrix{N,M,T,L} ` .
231
-
232
- Here ` L ` is the ` length ` of the matrix, such that ` N × M = L ` . However,
233
- convenience constructors are provided, so that ` L ` , ` T ` and even ` M ` are
234
- unnecessary. At minimum, you can type ` SMatrix{2}(1,2,3,4) ` to create a 2×2
235
- matrix (the total number of elements must divide evenly into ` N ` ). A
236
- convenience macro ` @SMatrix [1 2; 3 4] ` is provided (which also accepts
237
- comprehensions and the ` zeros() ` , ` ones() ` , ` fill() ` , ` rand() ` , ` randn() ` and ` eye() `
238
- functions).
239
-
240
- ### ` SArray `
241
-
242
- A container with arbitrarily many dimensions is defined as
243
- ` struct SArray{Size,T,N,L} <: StaticArray{Size,T,N} ` , where
244
- ` Size = Tuple{S1, S2, ...} ` is a tuple of ` Int ` s. You can easily construct one with
245
- the ` @SArray ` macro, supporting all the features of ` @SVector ` and ` @SMatrix `
246
- (but with arbitrary dimension).
247
-
248
- The main reason ` SVector ` and ` SMatrix ` are defined is to make it easier to
249
- define the types without the extra tuple characters (compare ` SVector{3} ` to
250
- ` SArray{Tuple{3}} ` ).
251
-
252
- ### ` Scalar `
253
-
254
- Sometimes you want to broadcast an operation, but not over one of your inputs.
255
- A classic example is attempting to displace a collection of vectors by the
256
- same vector. We can now do this with the ` Scalar ` type:
257
-
258
- ``` julia
259
- [[1 ,2 ,3 ], [4 ,5 ,6 ]] .+ Scalar ([1 ,0 ,- 1 ]) # [[2,2,2], [5,5,5]]
260
- ```
261
-
262
- ` Scalar ` is simply an implementation of an immutable, 0-dimensional ` StaticArray ` .
263
-
264
- ### Mutable arrays: ` MVector ` , ` MMatrix ` and ` MArray `
265
-
266
- These statically sized arrays are identical to the above, but are defined as
267
- ` mutable struct ` s, instead of immutable ` struct ` s. Because they are mutable, they
268
- allow ` setindex! ` to be defined (achieved through pointer manipulation, into a
269
- tuple).
270
-
271
- As a consequence of Julia's internal implementation, these mutable containers
272
- live on the heap, not the stack. Their memory must be allocated and tracked by
273
- the garbage collector. Nevertheless, there is opportunity for speed
274
- improvements relative to ` Base.Array ` because (a) there may be one less
275
- pointer indirection, (b) their (typically small) static size allows for
276
- additional loop unrolling and inlining, and consequentially (c) their mutating
277
- methods like ` map! ` are extremely fast. Benchmarking shows that operations such
278
- as addition and matrix multiplication are faster for ` MMatrix ` than ` Matrix ` ,
279
- at least for sizes up to 14 × 14, though keep in mind that optimal speed will
280
- be obtained by using mutating functions (like ` map! ` or ` A_mul_B! ` ) where
281
- possible, rather than reallocating new memory.
282
-
283
- Mutable static arrays also happen to be very useful containers that can be
284
- constructed on the heap (with the ability to use ` setindex! ` , etc), and later
285
- copied as e.g. an immutable ` SVector ` to the stack for use, or into e.g. an
286
- ` Array{SVector} ` for storage.
287
-
288
- Convenience macros ` @MVector ` , ` @MMatrix ` and ` @MArray ` are provided.
289
-
290
- ### ` SizedArray ` : a decorate size wrapper for ` Array `
291
-
292
- Another convenient mutable type is the ` SizedArray ` , which is just a wrapper-type
293
- about a standard Julia ` Array ` which declares its knwon size. For example, if
294
- we knew that ` a ` was a 2×2 ` Matrix ` , then we can type ` sa = SizedArray{Tuple{2,2}}(a) `
295
- to construct a new object which knows the type (the size will be verified
296
- automatically). A more convenient syntax for obtaining a ` SizedArray ` is by calling
297
- a ` Size ` object, e.g. ` sa = Size(2,2)(a) ` .
298
-
299
- Then, methods on ` sa ` will use the specialized code provided by the * StaticArrays*
300
- pacakge, which in many cases will be much, much faster. For example, calling
301
- ` eig(sa) ` will be signficantly faster than ` eig(a) ` since it will perform a
302
- specialized 2×2 matrix diagonalization rather than a general algorithm provided
303
- by Julia and * LAPACK* .
304
-
305
- In some cases it will make more sense to use a ` SizedArray ` , and in other cases
306
- an ` MArray ` might be preferable.
307
-
308
- ### ` FieldVector `
309
-
310
- Sometimes it might be useful to imbue your own types, having multiple fields,
311
- with vector-like properties. * StaticArrays* can take care of this for you by
312
- allowing you to inherit from ` FieldVector{N, T} ` . For example, consider:
313
-
314
- ``` julia
315
- struct Point3D <: FieldVector{3, Float64}
316
- x:: Float64
317
- y:: Float64
318
- z:: Float64
319
- end
320
- ```
321
-
322
- With this type, users can easily access fields to ` p = Point3D(x,y,z) ` using
323
- ` p.x ` , ` p.y ` or ` p.z ` , or alternatively via ` p[1] ` , ` p[2] ` , or ` p[3] ` . You may
324
- even permute the coordinates with ` p[SVector(3,2,1)] ` ). Furthermore, ` Point3D `
325
- is a complete ` AbstractVector ` implementation where you can add, subtract or
326
- scale vectors, multiply them by matrices, etc.
327
-
328
- It is also worth noting that ` FieldVector ` s may be mutable or immutable, and
329
- that ` setindex! ` is defined for use on mutable types. For immutable containers,
330
- you may want to define a method for ` similar_type ` so that operations leave the
331
- type constant (otherwise they may fall back to ` SVector ` ). For mutable
332
- containers, you may want to define a default constructor (no inputs) and an
333
- appropriate method for ` similar ` ,
334
-
335
- ### Implementing your own types
336
-
337
- You can easily create your own ` StaticArray ` type, by defining linear
338
- ` getindex ` (and optionally ` setindex! ` for mutable types - see
339
- ` setindex(::MArray, val, i) ` in * MArray.jl* for an example of how to
340
- achieve this through pointer manipulation). Your type should define a constructor
341
- that takes a tuple of the data (and mutable containers may want to define a
342
- default constructor).
343
-
344
- Other useful functions to overload may be ` similar_type ` (and ` similar ` for
345
- mutable containers).
346
-
347
- ### Conversions from ` Array `
348
-
349
- In order to convert from a dynamically sized ` AbstractArray ` to one of the
350
- statically sized array types, you must specify the size explicitly. For
351
- example,
352
-
353
- ``` julia
354
- v = [1 ,2 ]
355
-
356
- m = [1 2 ;
357
- 3 4 ]
358
-
359
- # ... a lot of intervening code
360
-
361
- sv = SVector {2} (v)
362
- sm = SMatrix {2,2} (m)
363
- sa = SArray {(2,2)} (m)
364
-
365
- sized_v = Size (2 )(v) # SizedArray{(2,)}(v)
366
- sized_m = Size (2 ,2 )(m) # SizedArray{(2,2)}(m)
367
- ```
368
-
369
- We have avoided adding ` SVector(v::AbstractVector) ` as a valid constructor to
370
- help users avoid the type instability (and potential performance disaster, if
371
- used without care) of this innocuous looking expression. However, the simplest
372
- way to deal with an ` Array ` is to create a ` SizedArray ` by calling a ` Size `
373
- instance, e.g. ` Size(2)(v) ` .
374
-
375
- ### Arrays of static arrays
376
-
377
- Storing a large number of static arrays is convenient as an array of static
378
- arrays. For example, a collection of positions (3D coordinates - ` SVector{3,Float64} ` )
379
- could be represented as a ` Vector{SVector{3,Float64}} ` .
380
-
381
- Another common way of storing the same data is as a 3×` N ` ` Matrix{Float64} ` .
382
- Rather conveniently, such types have * exactly* the same binary layout in memory,
383
- and therefore we can use ` reinterpret ` to convert between the two formats
384
- ``` julia
385
- function svectors (x:: Matrix{Float64} )
386
- @assert size (x,1 ) == 3
387
- reinterpret (SVector{3 ,Float64}, x, (size (x,2 ),))
388
- end
389
- ```
390
- Such a conversion does not copy the data, rather it refers to the * same* memory
391
- referenced by two different Julia ` Array ` s. Arguably, a ` Vector ` of ` SVector ` s
392
- is preferable to a ` Matrix ` because (a) it provides a better abstraction of the
393
- objects contained in the array and (b) it allows the fast * StaticArrays* methods
394
- to act on elements.
395
-
396
- ### Working with mutable and immutable arrays
397
-
398
- Generally, it is performant to rebind an * immutable* array, such as
399
- ``` julia
400
- function average_position (positions:: Vector{SVector{3,Float64}} )
401
- x = zeros (SVector{3 ,Float64})
402
- for pos ∈ positions
403
- x = x + pos
404
- end
405
- return x / length (positions)
406
- end
407
- ```
408
- so long as the ` Type ` of the rebound variable (` x ` , above) does not change.
409
-
410
- On the other hand, the above code for mutable containers like ` Array ` , ` MArray `
411
- or ` SizedArray ` is * not* very efficient. Mutable containers in Julia 0.5 must
412
- be * allocated* and later * garbage collected* , and for small, fixed-size arrays
413
- this can be a leading contribution to the cost. In the above code, a new array
414
- will be instantiated and allocated on each iteration of the loop. In order to
415
- avoid unnecessary allocations, it is best to allocate an array only once and
416
- apply mutating functions to it:
417
- ``` julia
418
- function average_position (positions:: Vector{SVector{3,Float64}} )
419
- x = zeros (MVector{3 ,Float64})
420
- for pos ∈ positions
421
- # Take advantage of Julia 0.5 broadcast fusion
422
- x .= (+ ). (x, pos) # same as broadcast!(+, x, x, positions[i])
423
- end
424
- x .= (/ ). (x, length (positions))
425
- return x
426
- end
427
- ```
428
- Keep in mind that Julia 0.5 does not fuse calls to ` .+ ` , etc (or ` .+= ` etc),
429
- however the ` .= ` and ` (+).() ` syntaxes are fused into a single, efficient call
430
- to ` broadcast! ` . The simpler syntax ` x .+= pos ` is expected to be non-allocating
431
- (and therefore faster) in Julia 0.6.
432
-
433
- The functions ` setindex ` , ` push ` , ` pop ` , ` shift ` , ` unshift ` , ` insert ` and ` deleteat `
434
- are provided for performing certain specific operations on static arrays, in
435
- analogy with the standard functions ` setindex! ` , ` push! ` , ` pop! ` , etc. (Note that
436
- if the size of the static array changes, the type of the output will differ from
437
- the input.)
438
-
439
- ### SIMD optimizations
440
-
441
- It seems Julia and LLVM are smart enough to use processor vectorization
442
- extensions like SSE and AVX - however they are currently partially disabled by
443
- default. Run Julia with ` julia -O ` or ` julia -O3 ` to enable these optimizations,
444
- and many of your (immutable) ` StaticArray ` methods * should* become significantly
445
- faster!
446
-
447
147
## Relationship to * FixedSizeArrays* and * ImmutableArrays*
448
148
449
149
Several existing packages for statically sized arrays have been developed for
0 commit comments