-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Define basic FillArrays types in Base #39184
Changes from all commits
51ea655
f21d013
4529ea3
0e54068
9cde680
de8b3cc
879ca52
dc0de19
c02c2ed
aad18ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,7 @@ export | |
Dims, | ||
Enum, | ||
ExponentialBackOff, | ||
Fill, | ||
IndexCartesian, | ||
IndexLinear, | ||
IndexStyle, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
|
||
# Basic structure of a FillArray, the last version of fill. | ||
# These definitions are *intended* to be pirated and extended by FillArrays.jl (which originally defined them) | ||
|
||
""" | ||
AbstractFill{T, N, Axes} <: AbstractArray{T, N} | ||
Supertype for lazy array types whose entries are all equal to constant. | ||
|
||
For more `Fill` functionality, use the FillArrays.jl package. | ||
""" | ||
abstract type AbstractFill{T, N, Axes} <: AbstractArray{T, N} end | ||
|
||
==(a::AbstractFill, b::AbstractFill) = axes(a) == axes(b) && getindex_value(a) == getindex_value(b) | ||
|
||
@inline function getindex(F::AbstractFill, k::Integer) | ||
@boundscheck checkbounds(F, k) | ||
getindex_value(F) | ||
end | ||
|
||
@inline function getindex(F::AbstractFill{T, N}, kj::Vararg{<:Integer, N}) where {T, N} | ||
@boundscheck checkbounds(F, kj...) | ||
getindex_value(F) | ||
end | ||
|
||
@inline function setindex!(F::AbstractFill, v, k::Integer) | ||
@boundscheck checkbounds(F, k) | ||
v == getindex_value(F) || throw(ArgumentError("Cannot setindex! to $v for an AbstractFill with value $(getindex_value(F)).")) | ||
F | ||
end | ||
|
||
@inline function setindex!(F::AbstractFill{T, N}, v, kj::Vararg{<:Integer, N}) where {T, N} | ||
@boundscheck checkbounds(F, kj...) | ||
v == getindex_value(F) || throw(ArgumentError("Cannot setindex! to $v for an AbstractFill with value $(getindex_value(F)).")) | ||
F | ||
end | ||
|
||
@inline function fill!(F::AbstractFill, v) | ||
v == getindex_value(F) || throw(ArgumentError("Cannot fill! with $v an AbstractFill with value $(getindex_value(F)).")) | ||
F | ||
end | ||
|
||
IndexStyle(::Type{<:AbstractFill{<:Any,N,<:NTuple{N,Base.OneTo{Int}}}}) where N = IndexLinear() | ||
|
||
|
||
""" | ||
Fill{T, N, Axes} | ||
where `Axes <: Tuple{Vararg{AbstractUnitRange,N}}` | ||
A lazy representation of an array of dimension `N` | ||
whose entries are all equal to a constant of type `T`, | ||
with axes of type `Axes`. | ||
Typically created by `Fill` or `Zeros` or `Ones` | ||
# Examples | ||
```jldoctest | ||
julia> Fill(1) | ||
0-dimensional Fill{Int64, 0, Tuple{}}: | ||
1 | ||
|
||
julia> Fill(7, (2,3)) | ||
2×3 Fill{Int64,2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}: | ||
7 7 7 | ||
7 7 7 | ||
|
||
julia> Fill{Float64, 1, Tuple{UnitRange{Int64}}}(7., (1:2,)) | ||
2-element Fill{Float64,1,Tuple{UnitRange{Int64}}} with indices 1:2: | ||
7.0 | ||
7.0 | ||
``` | ||
|
||
For more `Fill` functionality and efficient routines, use the FillArrays.jl package. | ||
""" | ||
struct Fill{T, N, Axes} <: AbstractFill{T, N, Axes} | ||
value::T | ||
axes::Axes | ||
|
||
Fill{T,N,Axes}(x::T, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = | ||
new{T,N,Axes}(x,sz) | ||
Fill{T,0,Tuple{}}(x::T, sz::Tuple{}) where T = new{T,0,Tuple{}}(x, sz) | ||
end | ||
|
||
Fill{T,N,Axes}(x, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = | ||
Fill{T,N,Axes}(convert(T, x)::T, sz) | ||
|
||
Fill{T,0}(x::T, ::Tuple{}) where T = Fill{T,0,Tuple{}}(x, ()) # ambiguity fix | ||
|
||
@inline Fill{T, N}(x::T, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = | ||
Fill{T,N,Axes}(x, sz) | ||
@inline Fill{T, N}(x, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} = | ||
Fill{T,N}(convert(T, x)::T, sz) | ||
|
||
@inline Fill{T, N}(x, sz::SZ) where SZ<:Tuple{Vararg{Integer,N}} where {T, N} = | ||
Fill{T,N}(x, Base.OneTo.(sz)) | ||
@inline Fill{T, N}(x, sz::Vararg{Integer, N}) where {T, N} = Fill{T,N}(convert(T, x)::T, sz) | ||
|
||
|
||
@inline Fill{T}(x, sz::Vararg{<:Integer,N}) where {T, N} = Fill{T, N}(x, sz) | ||
@inline Fill{T}(x, sz::Tuple{Vararg{<:Any,N}}) where {T, N} = Fill{T, N}(x, sz) | ||
""" `Fill(x, dims...)` construct lazy version of `fill(x, dims...)` """ | ||
@inline Fill(x::T, sz::Vararg{<:Integer,N}) where {T, N} = Fill{T, N}(x, sz) | ||
""" `Fill(x, dims)` construct lazy version of `fill(x, dims)` """ | ||
@inline Fill(x::T, sz::Tuple{Vararg{<:Any,N}}) where {T, N} = Fill{T, N}(x, sz) | ||
|
||
# We restrict to when T is specified to avoid ambiguity with a Fill of a Fill | ||
@inline Fill{T}(F::Fill{T}) where T = F | ||
@inline Fill{T,N}(F::Fill{T,N}) where {T,N} = F | ||
@inline Fill{T,N,Axes}(F::Fill{T,N,Axes}) where {T,N,Axes} = F | ||
|
||
@inline axes(F::Fill) = F.axes | ||
@inline size(F::Fill) = length.(F.axes) | ||
|
||
AbstractArray{T}(F::Fill{T}) where T = F | ||
AbstractArray{T,N}(F::Fill{T,N}) where {T,N} = F | ||
AbstractArray{T}(F::Fill{V,N}) where {T,V,N} = Fill{T}(convert(T, F.value)::T, F.axes) | ||
AbstractArray{T,N}(F::Fill{V,N}) where {T,V,N} = Fill{T}(convert(T, F.value)::T, F.axes) | ||
|
||
convert(::Type{AbstractArray{T}}, F::Fill{T}) where T = F | ||
convert(::Type{AbstractArray{T,N}}, F::Fill{T,N}) where {T,N} = F | ||
convert(::Type{AbstractArray{T}}, F::Fill) where {T} = AbstractArray{T}(F) | ||
convert(::Type{AbstractArray{T,N}}, F::Fill) where {T,N} = AbstractArray{T,N}(F) | ||
convert(::Type{AbstractFill}, F::AbstractFill) = F | ||
convert(::Type{AbstractFill{T}}, F::AbstractFill) where T = convert(AbstractArray{T}, F) | ||
convert(::Type{AbstractFill{T,N}}, F::AbstractFill) where {T,N} = convert(AbstractArray{T,N}, F) | ||
|
||
copy(F::Fill) = Fill(F.value, F.axes) | ||
|
||
getindex(F::Fill{<:Any,0}) = getindex_value(F) | ||
|
||
@inline getindex_value(F::Fill) = F.value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this? Why not just define this in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems inconsistent to me unless you meant something else: julia> F = fill(1,3)
3-element Vector{Int64}:
1
1
1
julia> F[]
ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index []
Stacktrace:
[1] throw_boundserror(A::Vector{Int64}, I::Tuple{})
@ Base ./abstractarray.jl:651
[2] checkbounds
@ ./abstractarray.jl:616 [inlined]
[3] _getindex
@ ./abstractarray.jl:1196 [inlined]
[4] getindex(::Vector{Int64})
@ Base ./abstractarray.jl:1170
[5] top-level scope
@ REPL[20]:1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's to allow for unified code for |
||
|
||
map(f::Function, r::AbstractFill) = Fill(f(getindex_value(r)), axes(r)) | ||
|
||
iterate(f::AbstractFill{<:Any, 0}) = (getindex_value(f), nothing) | ||
iterate( ::AbstractFill{<:Any, 0}, s) = nothing | ||
|
||
getindex(f::AbstractFill{<:Any, 0}, ::CartesianIndex{0}) = getindex_value(f) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
@testset "fill array constructors and convert" begin | ||
|
||
@test Fill(1) ≡ Fill{Int}(1) ≡ Fill{Int,0}(1) ≡ Fill{Int,0,Tuple{}}(1,()) | ||
@test Fill(1,(-1,5)) ≡ Fill(1,(0,5)) | ||
@test Fill(1.0,5) isa AbstractVector{Float64} | ||
@test Fill(1.0,5,5) isa AbstractMatrix{Float64} | ||
@test Fill(1,5) ≡ Fill(1,(5,)) | ||
@test Fill(1,5,5) ≡ Fill(1,(5,5)) | ||
@test eltype(Fill(1.0,5,5)) == Float64 | ||
|
||
@test_throws InexactError Matrix{Float64}(Fill(1.0+1.0im,10,10)) | ||
|
||
for T in (Int, Float64) | ||
F = Fill{T}(one(T), 5) | ||
|
||
@test eltype(F) == T | ||
@test Array(F) == fill(one(T),5) | ||
@test Array{T}(F) == fill(one(T),5) | ||
@test Array{T,1}(F) == fill(one(T),5) | ||
|
||
F = Fill{T}(one(T), 5, 5) | ||
@test eltype(F) == T | ||
@test Array(F) == fill(one(T),5,5) | ||
@test Array{T}(F) == fill(one(T),5,5) | ||
@test Array{T,2}(F) == fill(one(T),5,5) | ||
|
||
@test convert(AbstractArray,F) ≡ F | ||
@test convert(AbstractArray{T},F) ≡ AbstractArray{T}(F) ≡ F | ||
@test convert(AbstractMatrix{T},F) ≡ AbstractMatrix{T}(F) ≡ F | ||
|
||
@test convert(AbstractArray{Float32},F) ≡ AbstractArray{Float32}(F) ≡ | ||
Fill{Float32}(one(Float32),5,5) | ||
@test convert(AbstractMatrix{Float32},F) ≡ AbstractMatrix{Float32}(F) ≡ | ||
Fill{Float32}(one(Float32),5,5) | ||
|
||
@test Fill{T}(F) ≡ Fill{T,2}(F) ≡ typeof(F)(F) ≡ F | ||
end | ||
|
||
@testset "copy should return Fill" begin | ||
x = Fill(1.0,10) | ||
@test copy(x) ≡ x | ||
end | ||
end | ||
|
||
# @testset "indexing" begin | ||
# A = Fill(3.0,5) | ||
# @test A[1:3] ≡ Fill(3.0,3) | ||
# @test A[1:3,1:1] ≡ Fill(3.0,3,1) | ||
# @test_throws BoundsError A[1:3,2] | ||
# @test_throws BoundsError A[1:26] | ||
# @test A[[true, false, true, false, false]] ≡ Fill(3.0, 2) | ||
# A = Fill(3.0, 2, 2) | ||
# @test A[[true true; true false]] ≡ Fill(3.0, 3) | ||
# @test_throws DimensionMismatch A[[true, false]] | ||
|
||
# @testset "colon" begin | ||
# @test Fill(3.0,2)[:] ≡ Fill(3.0,2)[Base.Slice(Base.OneTo(2))] ≡ Fill(3.0,2) | ||
|
||
# @test Fill(3.0,2,2)[:,:] ≡ Fill(3.0,2,2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Fill(3.0,2,2) | ||
# end | ||
|
||
# @testset "mixed integer / vector /colon" begin | ||
# a = Fill(2.0,5) | ||
# z = Zeros(5) | ||
# @test a[1:5] ≡ a[:] ≡ a | ||
# @test z[1:5] ≡ z[:] ≡ z | ||
|
||
# A = Fill(2.0,5,6) | ||
# Z = Zeros(5,6) | ||
# @test A[:,1] ≡ A[1:5,1] ≡ Fill(2.0,5) | ||
# @test A[1,:] ≡ A[1,1:6] ≡ Fill(2.0,6) | ||
# @test A[:,:] ≡ A[1:5,1:6] ≡ A[1:5,:] ≡ A[:,1:6] ≡ A | ||
# @test Z[:,1] ≡ Z[1:5,1] ≡ Zeros(5) | ||
# @test Z[1,:] ≡ Z[1,1:6] ≡ Zeros(6) | ||
# @test Z[:,:] ≡ Z[1:5,1:6] ≡ Z[1:5,:] ≡ Z[:,1:6] ≡ Z | ||
# A = Fill(2.0,5,6,7) | ||
# Z = Zeros(5,6,7) | ||
# @test A[:,1,1] ≡ A[1:5,1,1] ≡ Fill(2.0,5) | ||
# @test A[1,:,1] ≡ A[1,1:6,1] ≡ Fill(2.0,6) | ||
# @test A[:,:,:] ≡ A[1:5,1:6,1:7] ≡ A[1:5,:,1:7] ≡ A[:,1:6,1:7] ≡ A | ||
# end | ||
# end | ||
|
||
|
||
@testset "Broadcast" begin | ||
x = Fill(5,5) | ||
@test (.+)(x) ≡ x | ||
@test (.-)(x) ≡ -x | ||
@test exp.(x) ≡ Fill(exp(5),5) | ||
@test x .+ 1 ≡ Fill(6,5) | ||
@test 1 .+ x ≡ Fill(6,5) | ||
@test x .+ x ≡ Fill(10,5) | ||
f = (x,y) -> cos(x*y) | ||
|
||
@testset "support Ref" begin | ||
@test Fill(1,10) .- 1 ≡ Fill(1,10) .- Ref(1) ≡ Fill(1,10) .- Ref(1I) | ||
@test Fill([1 2; 3 4],10) .- Ref(1I) == Fill([0 2; 3 3],10) | ||
@test Ref(1I) .+ Fill([1 2; 3 4],10) == Fill([2 2; 3 5],10) | ||
end | ||
end | ||
|
||
@testset "map" begin | ||
x = Fill(2,5,3) | ||
@test map(exp,x) === Fill(exp(2),5,3) | ||
end | ||
|
||
@testset "0-dimensional" begin | ||
A = Fill{Int,0,Tuple{}}(3, ()) | ||
|
||
@test A[] ≡ A[1] ≡ 3 | ||
@test A ≡ Fill{Int,0}(3, ()) ≡ Fill(3, ()) ≡ Fill(3) | ||
@test size(A) == () | ||
@test axes(A) == () | ||
end | ||
|
||
@testset "setindex!/fill!" begin | ||
F = Fill(1,10) | ||
@test (F[1] = 1) == 1 | ||
@test_throws BoundsError (F[11] = 1) | ||
@test_throws ArgumentError (F[10] = 2) | ||
|
||
F = Fill(1,10,5) | ||
@test (F[1] = 1) == 1 | ||
@test (F[3,3] = 1) == 1 | ||
@test_throws BoundsError (F[51] = 1) | ||
@test_throws BoundsError (F[1,6] = 1) | ||
@test_throws ArgumentError (F[10] = 2) | ||
@test_throws ArgumentError (F[10,1] = 2) | ||
|
||
@test (F[:,1] .= 1) == fill(1,10) | ||
@test_throws ArgumentError (F[:,1] .= 2) | ||
|
||
@test fill!(F,1) == F | ||
@test_throws ArgumentError fill!(F,2) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was carried over from FillArrays.jl but I think was a temporary restriction. I'm not entirely sure this restriction should be kept.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I just delete that line altogether?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check is necessary because the function may be incorrect in the "degenerate" case where
a
is length1
and so the shape comes fromb
, nota
. Perhaps it would be better to be rewritten asbut this would not be type stable.
In any case some thought should be put into this.