From 51ea65504e8bdca82bc944e6bc26e562fe1c7c25 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 13:17:29 -0700 Subject: [PATCH 1/8] define basic FillArrays types in Base --- base/Base.jl | 2 + base/broadcast.jl | 44 +++++++++++-- base/exports.jl | 2 + base/fillarrays.jl | 134 ++++++++++++++++++++++++++++++++++++++ doc/src/manual/arrays.md | 7 +- test/choosetests.jl | 2 +- test/fill.jl | 136 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 315 insertions(+), 12 deletions(-) create mode 100644 base/fillarrays.jl create mode 100644 test/fill.jl diff --git a/base/Base.jl b/base/Base.jl index 878448c3e3964..fb0f6b8003712 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -215,6 +215,8 @@ include("multidimensional.jl") include("permuteddimsarray.jl") using .PermutedDimsArrays +include("fillarrays.jl") + include("broadcast.jl") using .Broadcast using .Broadcast: broadcasted, broadcasted_kwsyntax, materialize, materialize! diff --git a/base/broadcast.jl b/base/broadcast.jl index 6cfd4dbb8bf89..63a7d101590b7 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -9,8 +9,9 @@ module Broadcast using .Base.Cartesian using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin, @pure, - _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias -import .Base: copy, copyto!, axes + _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias, + AbstractFill, Fill + import .Base: copy, copyto!, axes, getindex_value export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__, broadcast_preserving_zero_d, BroadcastFunction ## Computing the result's axes: deprecated name @@ -669,14 +670,14 @@ julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already s 3 julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars -Base.RefValue{Type{Int64}}(Int64) +Fill{Type{Int64}}(Int64) julia> Broadcast.broadcastable("hello") # Strings break convention of matching iteration and act like a scalar instead -Base.RefValue{String}("hello") +Fill{String}("hello") ``` """ -broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair}) = Ref(x) -broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T) +broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair}) = Fill(x) +broadcastable(::Type{T}) where {T} = Fill{Type{T}}(T) broadcastable(x::Union{AbstractArray,Number,Ref,Tuple,Broadcasted}) = x # Default to collecting iterables — which will error for non-iterables broadcastable(x) = collect(x) @@ -1353,4 +1354,35 @@ function Base.show(io::IO, op::BroadcastFunction) end Base.show(io::IO, ::MIME"text/plain", op::BroadcastFunction) = show(io, op) + +# Fill Broadcasting +function broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}) where {T,N} + return Fill(op(getindex_value(r)), axes(r)) +end + +function broadcasted(::DefaultArrayStyle, op, a::AbstractFill, b::AbstractFill) + val = op(getindex_value(a), getindex_value(b)) + return Fill(val, broadcast_shape(axes(a), axes(b))) +end + +# Need to prevent array-valued fills from broadcasting over entry +_broadcast_getindex_value(a::AbstractFill{<:Number}) = getindex_value(a) +_broadcast_getindex_value(a::AbstractFill) = Ref(getindex_value(a)) + +function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractFill, b::AbstractRange) + broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError("Cannot broadcast $a and $b. Convert $b to a Vector first.")) + return broadcasted(*, _broadcast_getindex_value(a), b) +end + +function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractFill) + broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError("Cannot broadcast $a and $b. Convert $b to a Vector first.")) + return broadcasted(*, a, _broadcast_getindex_value(b)) +end + +broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}, x::Number) where {T,N} = Fill(op(getindex_value(r),x), axes(r)) +broadcasted(::DefaultArrayStyle{N}, op, x::Number, r::AbstractFill{T,N}) where {T,N} = Fill(op(x, getindex_value(r)), axes(r)) +broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}, x::Ref) where {T,N} = Fill(op(getindex_value(r),x[]), axes(r)) +broadcasted(::DefaultArrayStyle{N}, op, x::Ref, r::AbstractFill{T,N}) where {T,N} = Fill(op(x[], getindex_value(r)), axes(r)) + + end # module diff --git a/base/exports.jl b/base/exports.jl index 440b28fb155b2..1df35f04da695 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -14,6 +14,7 @@ export # Types AbstractChannel, + AbstractFill, AbstractIrrational, AbstractMatrix, AbstractRange, @@ -48,6 +49,7 @@ export Dims, Enum, ExponentialBackOff, + Fill, IndexCartesian, IndexLinear, IndexStyle, diff --git a/base/fillarrays.jl b/base/fillarrays.jl new file mode 100644 index 0000000000000..766da0463ddaa --- /dev/null +++ b/base/fillarrays.jl @@ -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 + +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) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 1db77f107aabd..e7275211b2c75 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -947,12 +947,9 @@ julia> string.(1:3, ". ", ["First", "Second", "Third"]) Sometimes, you want a container (like an array) that would normally participate in broadcast to be "protected" from broadcast's behavior of iterating over all of its elements. By placing it inside another container -(like a single element [`Tuple`](@ref)) broadcast will treat it as a single value. +(we suggest [`Fill`](@ref)) broadcast will treat it as a single value. ```jldoctest -julia> ([1, 2, 3], [4, 5, 6]) .+ ([1, 2, 3],) -([2, 4, 6], [5, 7, 9]) - -julia> ([1, 2, 3], [4, 5, 6]) .+ tuple([1, 2, 3]) +julia> ([1, 2, 3], [4, 5, 6]) .+ Fill([1, 2, 3]) ([2, 4, 6], [5, 7, 9]) ``` diff --git a/test/choosetests.jl b/test/choosetests.jl index 460b8121bb9f5..92955c26a82d4 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -55,7 +55,7 @@ function choosetests(choices = []) "boundscheck", "error", "ambiguous", "cartesian", "osutils", "channels", "iostream", "secretbuffer", "specificity", "reinterpretarray", "syntax", "corelogging", "missing", "asyncmap", - "smallarrayshrink" + "smallarrayshrink", "fill" ] tests = [] diff --git a/test/fill.jl b/test/fill.jl new file mode 100644 index 0000000000000..64822f032f6ba --- /dev/null +++ b/test/fill.jl @@ -0,0 +1,136 @@ +@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 From f21d0133fe3d57d310e5011e3f19452705b0f876 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 13:32:51 -0700 Subject: [PATCH 2/8] rename test/fill.jl to test/fillarrays.jl --- test/choosetests.jl | 2 +- test/fillarrays.jl | 136 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 test/fillarrays.jl diff --git a/test/choosetests.jl b/test/choosetests.jl index 92955c26a82d4..db8b883b34d9f 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -55,7 +55,7 @@ function choosetests(choices = []) "boundscheck", "error", "ambiguous", "cartesian", "osutils", "channels", "iostream", "secretbuffer", "specificity", "reinterpretarray", "syntax", "corelogging", "missing", "asyncmap", - "smallarrayshrink", "fill" + "smallarrayshrink", "fillarrays" ] tests = [] diff --git a/test/fillarrays.jl b/test/fillarrays.jl new file mode 100644 index 0000000000000..64822f032f6ba --- /dev/null +++ b/test/fillarrays.jl @@ -0,0 +1,136 @@ +@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 From 4529ea348fc1cce5e212203abf76a0c74c2aaad8 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 13:33:14 -0700 Subject: [PATCH 3/8] delete --- test/fill.jl | 136 --------------------------------------------------- 1 file changed, 136 deletions(-) delete mode 100644 test/fill.jl diff --git a/test/fill.jl b/test/fill.jl deleted file mode 100644 index 64822f032f6ba..0000000000000 --- a/test/fill.jl +++ /dev/null @@ -1,136 +0,0 @@ -@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 From 0e54068a77e801b2336e2c5241cd44d88d263671 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 13:40:06 -0700 Subject: [PATCH 4/8] don't export AbstractFill --- base/exports.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/exports.jl b/base/exports.jl index 1df35f04da695..d1e1f99d0966a 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -14,7 +14,6 @@ export # Types AbstractChannel, - AbstractFill, AbstractIrrational, AbstractMatrix, AbstractRange, From 9cde68040439827b6d4bd169db649201841a934d Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 16:14:49 -0700 Subject: [PATCH 5/8] remove ref to `Fill` --- doc/src/manual/arrays.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index e7275211b2c75..ad0e911e2e63c 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -946,8 +946,8 @@ julia> string.(1:3, ". ", ["First", "Second", "Third"]) ``` Sometimes, you want a container (like an array) that would normally participate in broadcast to be "protected" -from broadcast's behavior of iterating over all of its elements. By placing it inside another container -(we suggest [`Fill`](@ref)) broadcast will treat it as a single value. +from broadcast's behavior of iterating over all of its elements. By placing it inside another container broadcast +will treat it as a single value. ```jldoctest julia> ([1, 2, 3], [4, 5, 6]) .+ Fill([1, 2, 3]) ([2, 4, 6], [5, 7, 9]) From de8b3ccd363c9abc8aec021c09db4ea303f46651 Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 16:30:20 -0700 Subject: [PATCH 6/8] doctest fix --- base/broadcast.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 63a7d101590b7..c5cbb1ae746a3 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -670,10 +670,12 @@ julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already s 3 julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars -Fill{Type{Int64}}(Int64) +0-dimensional Fill{Type{Int64}, 0, Tuple{}}: +Int64 julia> Broadcast.broadcastable("hello") # Strings break convention of matching iteration and act like a scalar instead -Fill{String}("hello") +0-dimensional Fill{String, 0, Tuple{}}: +"hello" ``` """ broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair}) = Fill(x) From 879ca524b80c6580af06a6cb129003d13d5930fa Mon Sep 17 00:00:00 2001 From: MasonProtter Date: Sun, 10 Jan 2021 16:30:20 -0700 Subject: [PATCH 7/8] doctest fix --- base/broadcast.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 63a7d101590b7..c5cbb1ae746a3 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -670,10 +670,12 @@ julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already s 3 julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars -Fill{Type{Int64}}(Int64) +0-dimensional Fill{Type{Int64}, 0, Tuple{}}: +Int64 julia> Broadcast.broadcastable("hello") # Strings break convention of matching iteration and act like a scalar instead -Fill{String}("hello") +0-dimensional Fill{String, 0, Tuple{}}: +"hello" ``` """ broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair}) = Fill(x) From aad18ad42e4839b620144feecb57865dd51b8d2f Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Mon, 12 Apr 2021 15:18:01 -0400 Subject: [PATCH 8/8] fix whitespace --- doc/src/manual/arrays.md | 2 +- test/fillarrays.jl | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 1873f29ee5ddc..82c0b3efb28e9 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -947,7 +947,7 @@ julia> string.(1:3, ". ", ["First", "Second", "Third"]) Sometimes, you want a container (like an array) that would normally participate in broadcast to be "protected" from broadcast's behavior of iterating over all of its elements. By placing it inside another container broadcast -will treat it as a single value. +will treat it as a single value. ```jldoctest julia> ([1, 2, 3], [4, 5, 6]) .+ Fill([1, 2, 3]) ([2, 4, 6], [5, 7, 9]) diff --git a/test/fillarrays.jl b/test/fillarrays.jl index 64822f032f6ba..e4251b9c1fda7 100644 --- a/test/fillarrays.jl +++ b/test/fillarrays.jl @@ -73,7 +73,6 @@ end # @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)