StatProfilerHTML.jl report
Generated on Mon, 01 Apr 2024 21:01:18
File source code
Line Exclusive Inclusive Code
1 module StaticArraysCore
2
3 export SArray, SMatrix, SVector
4 export MArray, MMatrix, MVector
5 export SizedArray, SizedMatrix, SizedVector
6 export FieldArray, FieldMatrix, FieldVector
7 export Size
8
9 """
10 abstract type StaticArray{S, T, N} <: AbstractArray{T, N} end
11 StaticScalar{T} = StaticArray{Tuple{}, T, 0}
12 StaticVector{N,T} = StaticArray{Tuple{N}, T, 1}
13 StaticMatrix{N,M,T} = StaticArray{Tuple{N,M}, T, 2}
14
15 `StaticArray`s are Julia arrays with fixed, known size.
16
17 ## Dev docs
18
19 They must define the following methods:
20 - Constructors that accept a flat tuple of data.
21 - `getindex()` with an integer (linear indexing) (preferably `@inline` with `@boundscheck`).
22 - `Tuple()`, returning the data in a flat Tuple.
23
24 It may be useful to implement:
25
26 - `similar_type(::Type{MyStaticArray}, ::Type{NewElType}, ::Size{NewSize})`, returning a
27 type (or type constructor) that accepts a flat tuple of data.
28
29 For mutable containers you may also need to define the following:
30
31 - `setindex!` for a single element (linear indexing).
32 - `similar(::Type{MyStaticArray}, ::Type{NewElType}, ::Size{NewSize})`.
33 - In some cases, a zero-parameter constructor, `MyStaticArray{...}()` for unintialized data
34 is assumed to exist.
35
36 (see also `SVector`, `SMatrix`, `SArray`, `MVector`, `MMatrix`, `MArray`, `SizedArray`, `FieldVector`, `FieldMatrix` and `FieldArray`)
37 """
38 abstract type StaticArray{S <: Tuple, T, N} <: AbstractArray{T, N} end
39 const StaticScalar{T} = StaticArray{Tuple{}, T, 0}
40 const StaticVector{N, T} = StaticArray{Tuple{N}, T, 1}
41 const StaticMatrix{N, M, T} = StaticArray{Tuple{N, M}, T, 2}
42 const StaticVecOrMat{T} = Union{StaticVector{<:Any, T}, StaticMatrix{<:Any, <:Any, T}}
43
44 # The ::Tuple variants exist to make sure that anything that calls with a tuple
45 # instead of a Tuple gets through to the constructor, so the user gets a nice
46 # error message
47 tuple_length(T::Tuple) = length(T)
48 tuple_prod(T::Tuple) = prod(T)
49 tuple_minimum(T::Tuple) = minimum(T)
50
51 tuple_svec(::Type{T}) where {T<:Tuple} = T.parameters
52
53 # Julia `Base` provides a function for this purpose since v1.1: `fieldtypes`.
54 # We don't use it yet, though, because it wrecks type inference with Julia
55 # v1.6.
56 Base.@pure tuple_tuple(::Type{T}) where {T<:Tuple} = (tuple_svec(T)...,)
57
58 tuple_length(::Type{<:NTuple{n, Any}}) where {n} = n
59 tuple_prod(::Type{T}) where {T<:Tuple} = mapreduce(Int, *, tuple_tuple(T), init = 1)
60 tuple_minimum(::Type{Tuple{}}) = 0
61 tuple_minimum(::Type{T}) where {T<:Tuple{Any,Vararg}} = mapreduce(Int, min, tuple_tuple(T), init = typemax(Int))
62
63 """
64 size_to_tuple(::Type{S}) where S<:Tuple
65
66 Converts a size given by `Tuple{N, M, ...}` into a tuple `(N, M, ...)`.
67 """
68 size_to_tuple(::Type{T}) where {T<:Tuple} = tuple_tuple(T)
69
70 # Something doesn't match up type wise
71 @generated function check_array_parameters(::Type{Size}, ::Type{T}, ::Type{Val{N}}, ::Type{Val{L}}) where {Size,T,N,L}
72 if !all(x->isa(x, Int), Size.parameters)
73 return :(throw(ArgumentError("Static Array parameter Size must be a tuple of Ints (e.g. `SArray{Tuple{3,3}}` or `SMatrix{3,3}`).")))
74 end
75
76 if L != tuple_prod(Size) || L < 0 || tuple_minimum(Size) < 0 || tuple_length(Size) != N
77 return :(throw(ArgumentError("Size mismatch in Static Array parameters. Got size $Size, dimension $N and length $L.")))
78 end
79
80 return nothing
81 end
82
83 # Cast any Tuple to an TupleN{T}
84 @generated function convert_ntuple(::Type{T}, d::NTuple{N,Any}) where {N,T}
85 exprs = ntuple(i -> :(convert(T, d[$i])), Val(N))
86 return quote
87 Base.@_inline_meta
88 $(Expr(:tuple, exprs...))
89 end
90 end
91
92
93 """
94 SArray{S, T, N, L}(x::NTuple{L})
95 SArray{S, T, N, L}(x1, x2, x3, ...)
96
97 Construct a statically-sized array `SArray`. Since this type is immutable, the data must be
98 provided upon construction and cannot be mutated later. The `S` parameter is a Tuple-type
99 specifying the dimensions, or size, of the array - such as `Tuple{3,4,5}` for a 3×4×5-sized
100 array. The `N` parameter is the dimension of the array; the `L` parameter is the `length`
101 of the array and is always equal to `prod(S)`. Constructors may drop the `L`, `N` and `T`
102 parameters if they are inferrable from the input (e.g. `L` is always inferrable from `S`).
103
104 SArray{S}(a::Array)
105
106 Construct a statically-sized array of dimensions `S` (expressed as a `Tuple{...}`) using
107 the data from `a`. The `S` parameter is mandatory since the size of `a` is unknown to the
108 compiler (the element type may optionally also be specified).
109 """
110 struct SArray{S <: Tuple, T, N, L} <: StaticArray{S, T, N}
111 data::NTuple{L,T}
112
113 function SArray{S, T, N, L}(x::NTuple{L,T}) where {S<:Tuple, T, N, L}
114 check_array_parameters(S, T, Val{N}, Val{L})
115 new{S, T, N, L}(x)
116 end
117
118 function SArray{S, T, N, L}(x::NTuple{L,Any}) where {S<:Tuple, T, N, L}
119 check_array_parameters(S, T, Val{N}, Val{L})
120 new{S, T, N, L}(convert_ntuple(T, x))
121 end
122 end
123
124 @inline SArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} = SArray{S,T,N,tuple_prod(S)}(x)
125
126 """
127 SVector{S, T}(x::NTuple{S, T})
128 SVector{S, T}(x1, x2, x3, ...)
129
130 Construct a statically-sized vector `SVector`. Since this type is immutable,
131 the data must be provided upon construction and cannot be mutated later.
132 Constructors may drop the `T` and `S` parameters if they are inferrable from the
133 input (e.g. `SVector(1,2,3)` constructs an `SVector{3, Int}`).
134
135 SVector{S}(vec::Vector)
136
137 Construct a statically-sized vector of length `S` using the data from `vec`.
138 The parameter `S` is mandatory since the length of `vec` is unknown to the
139 compiler (the element type may optionally also be specified).
140 """
141 const SVector{S, T} = SArray{Tuple{S}, T, 1, S}
142
143 """
144 SMatrix{S1, S2, T, L}(x::NTuple{L, T})
145 SMatrix{S1, S2, T, L}(x1, x2, x3, ...)
146
147 Construct a statically-sized matrix `SMatrix`. Since this type is immutable,
148 the data must be provided upon construction and cannot be mutated later. The
149 `L` parameter is the `length` of the array and is always equal to `S1 * S2`.
150 Constructors may drop the `L`, `T` and even `S2` parameters if they are inferrable
151 from the input (e.g. `L` is always inferrable from `S1` and `S2`).
152
153 SMatrix{S1, S2}(mat::Matrix)
154
155 Construct a statically-sized matrix of dimensions `S1 × S2` using the data from
156 `mat`. The parameters `S1` and `S2` are mandatory since the size of `mat` is
157 unknown to the compiler (the element type may optionally also be specified).
158 """
159 const SMatrix{S1, S2, T, L} = SArray{Tuple{S1, S2}, T, 2, L}
160
161 # MArray
162
163 """
164 MArray{S, T, N, L}(undef)
165 MArray{S, T, N, L}(x::NTuple{L})
166 MArray{S, T, N, L}(x1, x2, x3, ...)
167
168
169 Construct a statically-sized, mutable array `MArray`. The data may optionally be
170 provided upon construction and can be mutated later. The `S` parameter is a Tuple-type
171 specifying the dimensions, or size, of the array - such as `Tuple{3,4,5}` for a 3×4×5-sized
172 array. The `N` parameter is the dimension of the array; the `L` parameter is the `length`
173 of the array and is always equal to `prod(S)`. Constructors may drop the `L`, `N` and `T`
174 parameters if they are inferrable from the input (e.g. `L` is always inferrable from `S`).
175
176 MArray{S}(a::Array)
177
178 Construct a statically-sized, mutable array of dimensions `S` (expressed as a `Tuple{...}`)
179 using the data from `a`. The `S` parameter is mandatory since the size of `a` is unknown to
180 the compiler (the element type may optionally also be specified).
181 """
182 mutable struct MArray{S <: Tuple, T, N, L} <: StaticArray{S, T, N}
183 data::NTuple{L,T}
184
185 function MArray{S,T,N,L}(x::NTuple{L,T}) where {S<:Tuple,T,N,L}
186 check_array_parameters(S, T, Val{N}, Val{L})
187 1 (2 %) 1 (2 %)
1 (2 %) samples spent in MArray
1 (100 %) (ex.), 1 (100 %) (incl.) when called from convert line 204
new{S,T,N,L}(x)
188 end
189
190 function MArray{S,T,N,L}(x::NTuple{L,Any}) where {S<:Tuple,T,N,L}
191 check_array_parameters(S, T, Val{N}, Val{L})
192 new{S,T,N,L}(convert_ntuple(T, x))
193 end
194
195 function MArray{S,T,N,L}(::UndefInitializer) where {S<:Tuple,T,N,L}
196 check_array_parameters(S, T, Val{N}, Val{L})
197 new{S,T,N,L}()
198 end
199 end
200
201 @inline MArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} = MArray{S,T,N,tuple_prod(S)}(x)
202
203 """
204 MVector{S,T}(undef)
205 MVector{S,T}(x::NTuple{S, T})
206 MVector{S,T}(x1, x2, x3, ...)
207
208 Construct a statically-sized, mutable vector `MVector`. Data may optionally be
209 provided upon construction, and can be mutated later. Constructors may drop the
210 `T` and `S` parameters if they are inferrable from the input (e.g.
211 `MVector(1,2,3)` constructs an `MVector{3, Int}`).
212
213 MVector{S}(vec::Vector)
214
215 Construct a statically-sized, mutable vector of length `S` using the data from
216 `vec`. The parameter `S` is mandatory since the length of `vec` is unknown to the
217 compiler (the element type may optionally also be specified).
218 """
219 const MVector{S, T} = MArray{Tuple{S}, T, 1, S}
220
221 """
222 MMatrix{S1, S2, T, L}(undef)
223 MMatrix{S1, S2, T, L}(x::NTuple{L, T})
224 MMatrix{S1, S2, T, L}(x1, x2, x3, ...)
225
226 Construct a statically-sized, mutable matrix `MMatrix`. The data may optionally
227 be provided upon construction and can be mutated later. The `L` parameter is the
228 `length` of the array and is always equal to `S1 * S2`. Constructors may drop
229 the `L`, `T` and even `S2` parameters if they are inferrable from the input
230 (e.g. `L` is always inferrable from `S1` and `S2`).
231
232 MMatrix{S1, S2}(mat::Matrix)
233
234 Construct a statically-sized, mutable matrix of dimensions `S1 × S2` using the data from
235 `mat`. The parameters `S1` and `S2` are mandatory since the size of `mat` is
236 unknown to the compiler (the element type may optionally also be specified).
237 """
238 const MMatrix{S1, S2, T, L} = MArray{Tuple{S1, S2}, T, 2, L}
239
240
241 # SizedArray
242
243 require_one_based_indexing(A...) = !Base.has_offset_axes(A...) ||
244 throw(ArgumentError("offset arrays are not supported but got an array with index other than 1"))
245
246 """
247 SizedArray{Tuple{dims...}}(array)
248
249 Wraps an `AbstractArray` with a static size, so to take advantage of the (faster)
250 methods defined by the static array package. The size is checked once upon
251 construction to determine if the number of elements (`length`) match, but the
252 array may be reshaped.
253
254 The aliases `SizedVector{N}` and `SizedMatrix{N,M}` are provided as more
255 convenient names for one and two dimensional `SizedArray`s. For example, to
256 wrap a 2x3 array `a` in a `SizedArray`, use `SizedMatrix{2,3}(a)`.
257 """
258 struct SizedArray{S<:Tuple,T,N,M,TData<:AbstractArray{T,M}} <: StaticArray{S,T,N}
259 data::TData
260
261 function SizedArray{S,T,N,M,TData}(a::TData) where {S<:Tuple,T,N,M,TData<:AbstractArray{T,M}}
262 require_one_based_indexing(a)
263 if size(a) != size_to_tuple(S) && size(a) != (tuple_prod(S),)
264 throw(DimensionMismatch("Dimensions $(size(a)) don't match static size $S"))
265 end
266 return new{S,T,N,M,TData}(a)
267 end
268
269 function SizedArray{S,T,N,1,TData}(::UndefInitializer) where {S<:Tuple,T,N,TData<:AbstractArray{T,1}}
270 return new{S,T,N,1,TData}(TData(undef, tuple_prod(S)))
271 end
272 function SizedArray{S,T,N,N,TData}(::UndefInitializer) where {S<:Tuple,T,N,TData<:AbstractArray{T,N}}
273 return new{S,T,N,N,TData}(TData(undef, size_to_tuple(S)...))
274 end
275 end
276
277 const SizedVector{S,T} = SizedArray{Tuple{S},T,1,1}
278
279 const SizedMatrix{S1,S2,T} = SizedArray{Tuple{S1,S2},T,2}
280
281 # FieldArray
282
283 """
284 abstract FieldArray{N, T, D} <: StaticArray{N, T, D}
285
286 Inheriting from this type will make it easy to create your own rank-D tensor types. A `FieldArray`
287 will automatically define `getindex` and `setindex!` appropriately. An immutable
288 `FieldArray` will be as performant as an `SArray` of similar length and element type,
289 while a mutable `FieldArray` will behave similarly to an `MArray`.
290
291 Note that you must define the fields of any `FieldArray` subtype in column major order. If you
292 want to use an alternative ordering you will need to pay special attention in providing your
293 own definitions of `getindex`, `setindex!` and tuple conversion.
294
295 If you define a `FieldArray` which is parametric on the element type you should
296 consider defining `similar_type` as in the `FieldVector` example.
297
298
299 # Example
300
301 struct Stiffness <: FieldArray{Tuple{2,2,2,2}, Float64, 4}
302 xxxx::Float64
303 yxxx::Float64
304 xyxx::Float64
305 yyxx::Float64
306 xxyx::Float64
307 yxyx::Float64
308 xyyx::Float64
309 yyyx::Float64
310 xxxy::Float64
311 yxxy::Float64
312 xyxy::Float64
313 yyxy::Float64
314 xxyy::Float64
315 yxyy::Float64
316 xyyy::Float64
317 yyyy::Float64
318 end
319 """
320 abstract type FieldArray{N, T, D} <: StaticArray{N, T, D} end
321
322 """
323 abstract FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, 2}
324
325 Inheriting from this type will make it easy to create your own rank-two tensor types. A `FieldMatrix`
326 will automatically define `getindex` and `setindex!` appropriately. An immutable
327 `FieldMatrix` will be as performant as an `SMatrix` of similar length and element type,
328 while a mutable `FieldMatrix` will behave similarly to an `MMatrix`.
329
330 Note that the fields of any subtype of `FieldMatrix` must be defined in column
331 major order unless you are willing to implement your own `getindex`.
332
333 If you define a `FieldMatrix` which is parametric on the element type you
334 should consider defining `similar_type` as in the `FieldVector` example.
335
336 # Example
337
338 struct Stress <: FieldMatrix{3, 3, Float64}
339 xx::Float64
340 yx::Float64
341 zx::Float64
342 xy::Float64
343 yy::Float64
344 zy::Float64
345 xz::Float64
346 yz::Float64
347 zz::Float64
348 end
349
350 Note that the fields of any subtype of `FieldMatrix` must be defined in column major order.
351 This means that formatting of constructors for literal `FieldMatrix` can be confusing. For example
352
353 sigma = Stress(1.0, 2.0, 3.0,
354 4.0, 5.0, 6.0,
355 7.0, 8.0, 9.0)
356
357 3×3 Stress:
358 1.0 4.0 7.0
359 2.0 5.0 8.0
360 3.0 6.0 9.0
361
362 will give you the transpose of what the multi-argument formatting suggests. For clarity,
363 you may consider using the alternative
364
365 sigma = Stress(SA[1.0 2.0 3.0;
366 4.0 5.0 6.0;
367 7.0 8.0 9.0])
368 """
369 abstract type FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, T, 2} end
370
371 """
372 abstract FieldVector{N, T} <: FieldArray{Tuple{N}, 1}
373
374 Inheriting from this type will make it easy to create your own vector types. A `FieldVector`
375 will automatically define `getindex` and `setindex!` appropriately. An immutable
376 `FieldVector` will be as performant as an `SVector` of similar length and element type,
377 while a mutable `FieldVector` will behave similarly to an `MVector`.
378
379 If you define a `FieldVector` which is parametric on the element type you
380 should consider defining `similar_type` to preserve your array type through
381 array operations as in the example below.
382
383 # Example
384
385 struct Vec3D{T} <: FieldVector{3, T}
386 x::T
387 y::T
388 z::T
389 end
390
391 StaticArrays.similar_type(::Type{<:Vec3D}, ::Type{T}, s::Size{(3,)}) where {T} = Vec3D{T}
392 """
393 abstract type FieldVector{N, T} <: FieldArray{Tuple{N}, T, 1} end
394
395 # Add a new BroadcastStyle for StaticArrays, derived from AbstractArrayStyle
396 # A constructor that changes the style parameter N (array dimension) is also required
397 struct StaticArrayStyle{N} <: Base.Broadcast.AbstractArrayStyle{N} end
398 StaticArrayStyle{M}(::Val{N}) where {M,N} = StaticArrayStyle{N}()
399
400 """
401 similar_type(static_array)
402 similar_type(static_array, T)
403 similar_type(array, ::Size)
404 similar_type(array, T, ::Size)
405
406 Returns a constructor for a statically-sized array similar to the input array
407 (or type) `static_array`/`array`, optionally with different element type `T` or size
408 `Size`. If the input `array` is not a `StaticArray` then the `Size` is mandatory.
409
410 This differs from `similar()` in that the resulting array type may not be
411 mutable (or define `setindex!()`), and therefore the returned type may need to
412 be *constructed* with its data.
413
414 Note that the (optional) size *must* be specified as a static `Size` object (so the compiler
415 can infer the result statically).
416
417 New types should define the signature `similar_type(::Type{A},::Type{T},::Size{S}) where {A<:MyType,T,S}`
418 if they wish to overload the default behavior.
419 """
420 function similar_type end
421
422 """
423 Dynamic()
424
425 Used to signify that a dimension of an array is not known statically.
426 """
427 struct Dynamic end
428
429 const StaticDimension = Union{Int, Dynamic}
430
431 """
432 Size(dims::Int...)
433
434 `Size` is used extensively throughout the `StaticArrays` API to describe _compile-time_
435 knowledge of the size of an array. The dimensions are stored as a type parameter and are
436 statically propagated by the compiler, resulting in efficient, type-inferrable code. For
437 example, to create a static matrix of zeros, use `A = zeros(SMatrix{3,3})`. The static
438 size of `A` can be obtained by `Size(A)`. (rather than `size(zeros(3,3))`, which returns
439 `Base.Tuple{2,Int}`).
440
441 Note that if dimensions are not known statically (e.g., for standard `Array`s),
442 [`Dynamic()`](@ref) should be used instead of an `Int`.
443
444 Size(a::AbstractArray)
445 Size(::Type{T<:AbstractArray})
446
447 The `Size` constructor can be used to extract static dimension information from a given
448 array. For example:
449
450 ```julia-repl
451 julia> Size(zeros(SMatrix{3, 4}))
452 Size(3, 4)
453
454 julia> Size(zeros(3, 4))
455 Size(StaticArrays.Dynamic(), StaticArrays.Dynamic())
456 ```
457
458 This has multiple uses, including "trait"-based dispatch on the size of a statically-sized
459 array. For example:
460
461 ```julia
462 det(x::StaticMatrix) = _det(Size(x), x)
463 _det(::Size{(1,1)}, x::StaticMatrix) = x[1,1]
464 _det(::Size{(2,2)}, x::StaticMatrix) = x[1,1]*x[2,2] - x[1,2]*x[2,1]
465 # and other definitions as necessary
466 ```
467
468 """
469 struct Size{S}
470 function Size{S}() where {S}
471 new{S::Tuple{Vararg{StaticDimension}}}()
472 end
473 end
474
475 Base.@pure Size(s::Tuple{Vararg{StaticDimension}}) = Size{s}()
476 Base.@pure Size(s::StaticDimension...) = Size{s}()
477 Size(::Type{T}) where {T<:Tuple} = Size{tuple_tuple(T)}()
478
479 Base.show(io::IO, ::Size{S}) where {S} = print(io, "Size", S)
480
481 function missing_size_error(::Type{SA}) where SA
482 error("""
483 The size of type `$SA` is not known.
484
485 If you were trying to construct (or `convert` to) a `StaticArray` you
486 may need to add the size explicitly as a type parameter so its size is
487 inferrable to the Julia compiler (or performance would be terrible). For
488 example, you might try
489
490 m = zeros(3,3)
491 SMatrix(m) # this error
492 SMatrix{3,3}(m) # correct - size is inferrable
493 SArray{Tuple{3,3}}(m) # correct, note Tuple{3,3}
494 """)
495 end
496
497 Size(a::T) where {T<:AbstractArray} = Size(T)
498 Size(::Type{SA}) where {SA <: StaticArray} = missing_size_error(SA)
499 Size(::Type{SA}) where {SA <: StaticArray{S}} where {S<:Tuple} = @isdefined(S) ? Size(S) : missing_size_error(SA)
500
501 Size(::Type{<:AbstractArray{<:Any, N}}) where {N} = Size(ntuple(_ -> Dynamic(), Val(N)))
502
503 end # module