| 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 | @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)) |