From 33f4dada9a6e19dddc4eb0d65807d4185f4ed9a0 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 21 Sep 2022 18:58:48 +0200 Subject: [PATCH] Move `Size` from StaticArrays.jl (#13) * Move `Size` from StaticArrays.jl * one more test * export Size --- Project.toml | 2 +- README.md | 6 ++- src/StaticArraysCore.jl | 82 +++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 13 +++++++ 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 35a4f50..d542fd4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "StaticArraysCore" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.3.0" +version = "1.4.0" [compat] julia = "1.6" diff --git a/README.md b/README.md index 176d1d3..1a1b42a 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,10 @@ Interface package for [StaticArrays.jl](https://github.com/JuliaArrays/StaticArr [![Build Status](https://github.com/JuliaArrays/StaticArraysCore.jl/workflows/CI/badge.svg)](https://github.com/JuliaArrays/StaticArraysCore.jl/actions?query=workflow%3ACI) [![codecov.io](https://codecov.io/github/JuliaArrays/StaticArraysCore.jl/branch/main/graph/badge.svg)](http://codecov.io/github/JuliaArrays/StaticArraysCore.jl/branch/main) -Contains definitions for the following types: +Contains definitions for the following types and functions: * immutable: `SArray`, `SVector` and `SMatrix`, * mutable: `MArray`, `MVector` and `MMatrix`, -* wrapper: `SizedArray`, `SizedVector` and `SizedMatrix`. +* wrapper: `SizedArray`, `SizedVector` and `SizedMatrix`, +* `Size` and `Dynamic`, +* `similar_type`. diff --git a/src/StaticArraysCore.jl b/src/StaticArraysCore.jl index f5d920a..2b56fe2 100644 --- a/src/StaticArraysCore.jl +++ b/src/StaticArraysCore.jl @@ -4,6 +4,7 @@ export SArray, SMatrix, SVector export MArray, MMatrix, MVector export SizedArray, SizedMatrix, SizedVector export FieldArray, FieldMatrix, FieldVector +export Size """ abstract type StaticArray{S, T, N} <: AbstractArray{T, N} end @@ -411,4 +412,85 @@ if they wish to overload the default behavior. """ function similar_type end +""" + Dynamic() + +Used to signify that a dimension of an array is not known statically. +""" +struct Dynamic end + +const StaticDimension = Union{Int, Dynamic} + +""" + Size(dims::Int...) + +`Size` is used extensively throughout the `StaticArrays` API to describe _compile-time_ +knowledge of the size of an array. The dimensions are stored as a type parameter and are +statically propagated by the compiler, resulting in efficient, type-inferrable code. For +example, to create a static matrix of zeros, use `A = zeros(SMatrix{3,3})`. The static +size of `A` can be obtained by `Size(A)`. (rather than `size(zeros(3,3))`, which returns +`Base.Tuple{2,Int}`). + +Note that if dimensions are not known statically (e.g., for standard `Array`s), +[`Dynamic()`](@ref) should be used instead of an `Int`. + + Size(a::AbstractArray) + Size(::Type{T<:AbstractArray}) + +The `Size` constructor can be used to extract static dimension information from a given +array. For example: + +```julia-repl +julia> Size(zeros(SMatrix{3, 4})) +Size(3, 4) + +julia> Size(zeros(3, 4)) +Size(StaticArrays.Dynamic(), StaticArrays.Dynamic()) +``` + +This has multiple uses, including "trait"-based dispatch on the size of a statically-sized +array. For example: + +```julia +det(x::StaticMatrix) = _det(Size(x), x) +_det(::Size{(1,1)}, x::StaticMatrix) = x[1,1] +_det(::Size{(2,2)}, x::StaticMatrix) = x[1,1]*x[2,2] - x[1,2]*x[2,1] +# and other definitions as necessary +``` + +""" +struct Size{S} + function Size{S}() where {S} + new{S::Tuple{Vararg{StaticDimension}}}() + end +end + +Base.@pure Size(s::Tuple{Vararg{StaticDimension}}) = Size{s}() +Base.@pure Size(s::StaticDimension...) = Size{s}() +Base.@pure Size(s::Type{<:Tuple}) = Size{tuple(s.parameters...)}() + +Base.show(io::IO, ::Size{S}) where {S} = print(io, "Size", S) + +function missing_size_error(::Type{SA}) where SA + error(""" + The size of type `$SA` is not known. + + If you were trying to construct (or `convert` to) a `StaticArray` you + may need to add the size explicitly as a type parameter so its size is + inferrable to the Julia compiler (or performance would be terrible). For + example, you might try + + m = zeros(3,3) + SMatrix(m) # this error + SMatrix{3,3}(m) # correct - size is inferrable + SArray{Tuple{3,3}}(m) # correct, note Tuple{3,3} + """) +end + +Size(a::T) where {T<:AbstractArray} = Size(T) +Size(::Type{SA}) where {SA <: StaticArray} = missing_size_error(SA) +Size(::Type{SA}) where {SA <: StaticArray{S}} where {S<:Tuple} = @isdefined(S) ? Size(S) : missing_size_error(SA) + +Base.@pure Size(::Type{<:AbstractArray{<:Any, N}}) where {N} = Size(ntuple(_ -> Dynamic(), N)) + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 0e2c251..1f97d03 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,3 +25,16 @@ using StaticArraysCore, Test @test StaticArraysCore.StaticArrayStyle{1}(Val(2)) === StaticArraysCore.StaticArrayStyle{2}() end + +@testset "Size" begin + M = SArray{Tuple{2,3,4},Int,3}(tuple(rand(Int, 24)...)) + @test (@inferred Size(M)) === Size(2, 3, 4) + Ms = Size(M) + @test repr(Ms) == "Size(2, 3, 4)" + @test Size(2, StaticArraysCore.Dynamic(), 5) === Size{(2, StaticArraysCore.Dynamic(), 5)}() + @test Size((2, StaticArraysCore.Dynamic(), 5)) === Size{(2, StaticArraysCore.Dynamic(), 5)}() + @test Size(Tuple{2, StaticArraysCore.Dynamic(), 5}) === Size{(2, StaticArraysCore.Dynamic(), 5)}() + @test Size([2 3; 4 5]) === Size{(StaticArraysCore.Dynamic(), StaticArraysCore.Dynamic())}() + + @test_throws ErrorException Size(SArray) +end