StatProfilerHTML.jl report
Generated on Mon, 01 Apr 2024 21:01:18
File source code
Line Exclusive Inclusive Code
1 """
2 Args
3
4 A help wrapper to distinguish `SA(x...)` and `SA((x...,))`
5 """
6 struct Args{T<:Tuple}
7 args::T
8 end
9 Length(x::Args) = Length(length(x.args))
10 const BadArgs = Args{<:Tuple{Tuple{<:Tuple}}}
11
12 # Some help functions.
13 @pure has_ndims(::Type{<:StaticArray{<:Tuple,<:Any,N}}) where {N} = @isdefined N
14 @pure has_ndims(::Type{<:StaticArray}) = false
15 if VERSION < v"1.7"
16 Base.ndims(::Type{<:StaticArray{<:Tuple,<:Any,N}}) where {N} = N
17 end
18 @pure has_eltype(::Type{<:StaticArray{<:Tuple,T}}) where {T} = @isdefined T
19 @pure has_eltype(::Type{<:StaticArray}) = false
20 @pure has_size(::Type{<:StaticArray{S}}) where {S<:Tuple} = @isdefined S
21 @pure has_size(::Type{<:StaticArray}) = false
22 # workaround for https://github.com/JuliaArrays/StaticArrays.jl/issues/1047
23 has_size(::Type{SVector}) = false
24 has_size(::Type{MVector}) = false
25 has_size(::Type{SMatrix}) = false
26 has_size(::Type{MMatrix}) = false
27 has_size(::Type{SMatrix{N}}) where {N} = false
28 has_size(::Type{MMatrix{N}}) where {N} = false
29
30 @pure has_size1(::Type{<:StaticMatrix{M}}) where {M} = @isdefined M
31 @pure has_size1(::Type{<:StaticMatrix}) = false
32 _size1(::Type{<:StaticMatrix{M}}) where {M} = M
33 @generated function _sqrt(::Length{L}) where {L}
34 N = round(Int, sqrt(L))
35 N^2 == L && return :($N)
36 throw(DimensionMismatch("Input's length must be perfect square"))
37 end
38
39 """
40 SA′ = construct_type(::Type{SA}, x) where {SA<:StaticArray}
41
42 Pick a proper constructor `SA′` based on `x` if `SA(x)`/`SA(x...)` has no specific definition.
43 The default returned `SA′` is `SA` itself for user defined `StaticArray`s. This differs from
44 `similar_type()` in that `SA′` should always be a subtype of `SA`.
45
46 !!! note
47 To distinguish `SA(x...)` and `SA(x::Tuple)`, the former calls
48 `construct_type(SA, StaticArrays.Args(x))` instead of `construct_type(SA, x)`.
49
50 !!! note
51 Please make sure `SA'(x)` has a specific definition if the default behavior is overloaded.
52 Otherwise construction might fall into infinite recursion.
53
54 ---
55 The adaption rules for official `StaticArray`s could be summarized as:
56
57 # `SA <: FieldArray`: `eltype` adaptable
58
59 `FieldArray`s are always static-sized. We only derive `SA′`'s `eltype` using type promotion if needed.
60
61 # `SA <: Union{SArray, MArray, SHermitianCompact, SizedArray}`: `size`/`eltype` adaptable
62
63 - SA(x::Tuple)
64 If `SA` is fully static-sized, then we first try to fill `SA` with `x`'s elements.
65 If failed and `length(SA) == 1`, then we try to fill `SA` with `x` itself.
66
67 If `SA` is not fully static-sized, then we always try to fill `SA` with `x`'s elements,
68 and the constructor's `Size` is derived based on:
69 1. If `SA <: StaticVector`, then we use `length(x)` as the output `Length`
70 2. If `SA <: StaticMatrix{M}`, then we use `(M, N)` (`N = length(x) ÷ M`) as the output `Size`
71 3. If `SA <: StaticMatrix{M,M} where M`, then we use `(N, N)` (`N = sqrt(length(x)`) as the output `Size`.
72 - SA(x...)
73 Similar to `Tuple`, but we never fill `SA` with `x` itself.
74 - SA(x::StaticArray)
75 We treat `x` as `Tuple` whenever possible. If failed, then try to inherit `x`'s `Size`.
76 - SA(x::AbstractArray)
77 `x` is used to provide eltype. Thus `SA` must be static sized.
78 """
79 function construct_type(::Type{SA}, x) where {SA<:StaticArray}
80 x isa BadArgs || return SA
81 _no_precise_size(SA, x.args[1][1])
82 end
83
84 # These StaticArrays support `size`/`eltype` adaption during construction.
85 const SizeEltypeAdaptable = Union{SArray, MArray, SHermitianCompact, SizedArray}
86 function construct_type(::Type{SA}, x) where {SA<:SizeEltypeAdaptable}
87 SA′ = adapt_eltype(adapt_size(SA, x), x)
88 check_parameters(SA′)
89 (!need_rewrap(SA′, x) && x isa Tuple && SA === SA′) || return SA′
90 error("Constructor for $SA is missing. Please file a bug.")
91 end
92
93 adapt_size_impl1(::Type{SA}, ::Any, y::Int) where {SA<:StaticArray} = y
94 adapt_size_impl1(::Type{SA}, x, ::Any) where {SA<:StaticArray} = _no_precise_size(SA, x)
95
96 adapt_size_impl0(::Type{SA}, x, ::Length{y}) where {SA<:StaticArray, y} = adapt_size_impl1(SA, x, y)
97
98 function adapt_size(::Type{SA}, x) where {SA<:StaticArray}
99 if has_size(SA) && length_match_size(SA, x)
100 SZ = Tuple{size(SA)...}
101 else
102 len = x isa Tuple ? length(x) : adapt_size_impl0(SA, x, Length(x))
103 len isa Int || _no_precise_size(SA, x)
104 if SA <: StaticVector
105 SZ = Tuple{len}
106 elseif SA <: StaticMatrix && has_size1(SA)
107 N = _size1(SA)
108 M = len ÷ N
109 M * N == len || throw(DimensionMismatch("Incorrect matrix sizes. $len does not divide $N elements"))
110 SZ = Tuple{N, M}
111 elseif SA <: StaticMatrix{N,N} where {N}
112 N = _sqrt(Length(len))
113 SZ = Tuple{N, N}
114 elseif x isa StaticArray
115 SZ = Tuple{size(x)...}
116 else
117 _no_precise_size(SA, x)
118 end
119 end
120 SA′ = typeintersect(SA, StaticArrayNoEltype{SZ,tuple_length(SZ)})
121 SA′ === Union{} && _no_precise_size(SA, x)
122 return SA′
123 end
124
125 function length_match_size(::Type{SA}, x) where {SA<:StaticArray}
126 if has_ndims(SA)
127 SZ, N = size(SA), ndims(SA)
128 length(SZ) == N || throw(ArgumentError("Size $(Tuple{SZ...}) mismatches dimension $N."))
129 end
130 _length_match_size(length(SA), x) || _no_precise_size(SA, x)
131 end
132 _length_match_size(l::Int, x::Union{Tuple,StaticArray}) = l == 1 || l == length(x)
133 _length_match_size(l::Int, x::Args) = l == length(x.args)
134 _length_match_size(::Int, _) = true
135
136 function adapt_eltype(::Type{SA}, x) where {SA<:StaticArray}
137 has_eltype(SA) && return SA
138 T = if need_rewrap(SA, x)
139 typeof(x)
140 elseif x isa Tuple
141 promote_tuple_eltype(x)
142 elseif x isa Args
143 promote_tuple_eltype(x.args)
144 else
145 eltype(x)
146 end
147 return typeintersect(SA, AbstractArray{T})
148 end
149
150 need_rewrap(::Type{<:StaticArray}, x) = false
151 function need_rewrap(::Type{SA}, x::Union{Tuple,StaticArray}) where {SA <: StaticArray}
152 has_size(SA) && length(SA) == 1 && length(x) != 1
153 end
154
155 check_parameters(::Type{<:SizeEltypeAdaptable}) = nothing
156 check_parameters(::Type{SArray{S,T,N,L}}) where {S<:Tuple,T,N,L} = check_array_parameters(S,T,Val{N},Val{L})
157 check_parameters(::Type{MArray{S,T,N,L}}) where {S<:Tuple,T,N,L} = check_array_parameters(S,T,Val{N},Val{L})
158 check_parameters(::Type{SHermitianCompact{N,T,L}}) where {N,T,L} = _check_hermitian_parameters(Val(N), Val(L))
159
160 _no_precise_size(SA, x::Args) = _no_precise_size(SA, x.args)
161 _no_precise_size(SA, x::Tuple) = throw(DimensionMismatch("No precise constructor for $SA found. Length of input was $(length(x))."))
162 _no_precise_size(SA, x::StaticArray) = throw(DimensionMismatch("No precise constructor for $SA found. Size of input was $(size(x))."))
163 _no_precise_size(SA, x) = throw(DimensionMismatch("No precise constructor for $SA found. Input is not static sized."))
164
165 @inline (::Type{SA})(x...) where {SA <: StaticArray} = construct_type(SA, Args(x))(x)
166 @inline function (::Type{SA})(x::Tuple) where {SA <: SizeEltypeAdaptable}
167 SA′ = construct_type(SA, x)
168 need_rewrap(SA′, x) ? SA′((x,)) : SA′(x)
169 end
170 @inline function (::Type{SA})(sa::StaticArray) where {SA <: StaticArray}
171 SA′ = construct_type(SA, sa)
172 need_rewrap(SA′, sa) ? SA′((sa,)) : SA′(Tuple(sa))
173 end
174 1 (2 %) 2 (3 %)
2 (3 %) samples spent in StaticArray
1 (50 %) (ex.), 1 (50 %) (incl.) when called from residual! line 396
1 (50 %) (incl.) when called from residual! line 395
1 (100 %) samples spent calling convert
@propagate_inbounds (T::Type{<:StaticArray})(a::AbstractArray) = convert(T, a)
175
176 # this covers most conversions and "statically-sized reshapes"
177 @inline function convert(::Type{SA}, sa::StaticArray{S}) where {SA<:StaticArray,S<:Tuple}
178 SA′ = construct_type(SA, sa)
179 # `SA′((sa,))` is not valid. As we want `SA′(sa...)`
180 need_rewrap(SA′, sa) && _no_precise_size(SA, sa)
181 return SA′(Tuple(sa))
182 end
183 @inline convert(::Type{SA}, sa::SA) where {SA<:StaticArray} = sa
184 @inline convert(::Type{SA}, x::Tuple) where {SA<:StaticArray} = SA(x) # convert -> constructor. Hopefully no loops...
185
186 # support conversion to AbstractArray
187 AbstractArray{T}(sa::StaticArray{S,T}) where {S,T} = sa
188 AbstractArray{T,N}(sa::StaticArray{S,T,N}) where {S,T,N} = sa
189 AbstractArray{T}(sa::StaticArray{S,U}) where {S,T,U} = similar_type(typeof(sa),T,Size(sa))(sa)
190 AbstractArray{T,N}(sa::StaticArray{S,U,N}) where {S,T,U,N} = similar_type(typeof(sa),T,Size(sa))(sa)
191
192 # Constructing a Tuple from a StaticArray
193 @inline Tuple(a::StaticArray) = unroll_tuple(a, Length(a))
194
195 @noinline function dimension_mismatch_fail(::Type{SA}, a::AbstractArray) where {SA <: StaticArray}
196 throw(DimensionMismatch("expected input array of length $(length(SA)), got length $(length(a))"))
197 end
198
199
1 (2 %) samples spent in convert
1 (100 %) (incl.) when called from StaticArray line 174
@propagate_inbounds function convert(::Type{SA}, a::AbstractArray) where {SA <: StaticArray}
200 @boundscheck if length(a) != length(SA)
201 dimension_mismatch_fail(SA, a)
202 end
203 SA′ = construct_type(SA, a)
204 1 (2 %)
1 (100 %) samples spent calling MArray
return SA′(unroll_tuple(a, Length(SA′)))
205 end
206
207 length_val(a::T) where {T <: StaticArrayLike} = length_val(Size(T))
208 length_val(a::Type{T}) where {T<:StaticArrayLike} = length_val(Size(T))
209
210 unroll_tuple(a::AbstractArray, ::Length{0}) = ()
211 unroll_tuple(a::AbstractArray, ::Length{1}) = @inbounds (a[],)
212 @generated function unroll_tuple(a::AbstractArray, ::Length{L}) where {L}
213 exprs = (:(a[$j+Δj]) for j = 0:L-1)
214 quote
215 @_inline_meta
216 Δj = firstindex(a)
217 @inbounds return $(Expr(:tuple, exprs...))
218 end
219 end
220
221 # `float` and `real` of StaticArray types, analogously to application to scalars (issue 935)
222 float(::Type{SA}) where SA<:StaticArray{_S,T,_N} where {_S,T,_N} = similar_type(SA, float(T))
223 real(::Type{SA}) where SA<:StaticArray{_S,T,_N} where {_S,T,_N} = similar_type(SA, real(T))