Skip to content

Commit

Permalink
Fix #28866, prevent array operations from dropping 0d containers
Browse files Browse the repository at this point in the history
This is a simple workaround for the handful of elementwise operations that are defined on arrays _without_ the need for explicit broadcast but use broadcasting (with an extra shape check) in their implementation. These were the only affected cases I could find.
  • Loading branch information
mbauman committed May 23, 2019
1 parent 826bb8b commit 101dd07
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 6 deletions.
10 changes: 5 additions & 5 deletions base/arraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ julia> A
conj!(A::AbstractArray{<:Number}) = (@inbounds broadcast!(conj, A, A); A)

for f in (:-, :conj, :real, :imag)
@eval ($f)(A::AbstractArray) = broadcast($f, A)
@eval ($f)(A::AbstractArray) = broadcast_preserving_zero_d($f, A)
end


Expand All @@ -36,23 +36,23 @@ end
for f in (:+, :-)
@eval function ($f)(A::AbstractArray, B::AbstractArray)
promote_shape(A, B) # check size compatibility
broadcast($f, A, B)
broadcast_preserving_zero_d($f, A, B)
end
end

function +(A::Array, Bs::Array...)
for B in Bs
promote_shape(A, B) # check size compatibility
end
broadcast(+, A, Bs...)
broadcast_preserving_zero_d(+, A, Bs...)
end

for f in (:/, :\, :*)
if f != :/
@eval ($f)(A::Number, B::AbstractArray) = broadcast($f, A, B)
@eval ($f)(A::Number, B::AbstractArray) = broadcast_preserving_zero_d($f, A, B)
end
if f != :\
@eval ($f)(A::AbstractArray, B::Number) = broadcast($f, A, B)
@eval ($f)(A::AbstractArray, B::Number) = broadcast_preserving_zero_d($f, A, B)
end
end

Expand Down
16 changes: 15 additions & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ using .Base.Cartesian
using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin,
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias
import .Base: copy, copyto!, axes
export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__
export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__, broadcast_preserving_zero_d

## Computing the result's axes: deprecated name
const broadcast_axes = axes
Expand Down Expand Up @@ -790,6 +790,20 @@ julia> A
"""
broadcast!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N} = (materialize!(dest, broadcasted(f, As...)); dest)

"""
broadcast_preserving_zero_d(f, As...)
Like [`broadcast`](@ref), except in the case of a 0-dimensional result where it returns a 0-dimensional container
Broadcast automatically unwraps zero-dimensional results to be just the element itself,
but in some cases it is necessary to always return a container — even in the 0-dimensional case.
"""
function broadcast_preserving_zero_d(f, As...)
bc = broadcasted(f, As...)
r = materialize(bc)
return length(axes(bc)) == 0 ? fill!(similar(bc, typeof(r)), r) : r
end

"""
Broadcast.materialize(bc)
Expand Down
17 changes: 17 additions & 0 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2619,6 +2619,23 @@ Base.view(::T25958, args...) = args
@test t[end,end,end] == @view(t[end,end,end]) == @views t[end,end,end]
end

@testset "0-dimensional container operations" begin
for op in (-, conj, real, imag)
@test op(fill(2)) == fill(op(2))
@test op(fill(1+2im)) == fill(op(1+2im))
end
for op in (+, -)
@test op(fill(1), fill(2)) == fill(op(1, 2))
@test op(fill(1), fill(2)) isa AbstractArray{Int, 0}
end
@test fill(1) + fill(2) + fill(3) == fill(1+2+3)
@test fill(1) / 2 == fill(1/2)
@test 2 \ fill(1) == fill(1/2)
@test 2*fill(1) == fill(2)
@test fill(1)*2 == fill(2)
end


# Fix oneunit bug for unitful arrays
@test oneunit([Second(1) Second(2); Second(3) Second(4)]) == [Second(1) Second(0); Second(0) Second(1)]

0 comments on commit 101dd07

Please sign in to comment.