diff --git a/NEWS.md b/NEWS.md index 048fa4a1bf5f2..31aebe866ce37 100644 --- a/NEWS.md +++ b/NEWS.md @@ -33,9 +33,6 @@ This section lists changes that do not have deprecation warnings. for `real(z) < 0`, which differs from `log(gamma(z))` by multiples of 2π in the imaginary part ([#18330]). - * `broadcast` now handles tuples, and treats any argument that is not a tuple - or an array as a "scalar" ([#16986]). - Library improvements -------------------- @@ -649,7 +646,6 @@ Language tooling improvements [#16854]: https://github.com/JuliaLang/julia/issues/16854 [#16953]: https://github.com/JuliaLang/julia/issues/16953 [#16972]: https://github.com/JuliaLang/julia/issues/16972 -[#16986]: https://github.com/JuliaLang/julia/issues/16986 [#17033]: https://github.com/JuliaLang/julia/issues/17033 [#17037]: https://github.com/JuliaLang/julia/issues/17037 [#17075]: https://github.com/JuliaLang/julia/issues/17075 diff --git a/base/arraymath.jl b/base/arraymath.jl index 26f60b7aed75a..5fbe638d37691 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -53,8 +53,8 @@ promote_array_type{S<:Integer}(::typeof(.\), ::Type{S}, ::Type{Bool}, T::Type) = promote_array_type{S<:Integer}(F, ::Type{S}, ::Type{Bool}, T::Type) = T for f in (:+, :-, :div, :mod, :&, :|, :$) - @eval ($f)(A::AbstractArray, B::AbstractArray) = - _elementwise($f, promote_eltype_op($f, A, B), A, B) + @eval ($f){R,S}(A::AbstractArray{R}, B::AbstractArray{S}) = + _elementwise($f, promote_op($f, R, S), A, B) end function _elementwise(op, ::Type{Any}, A::AbstractArray, B::AbstractArray) promote_shape(A, B) # check size compatibility diff --git a/base/broadcast.jl b/base/broadcast.jl index d6801a7e074b4..5914bc21de4d8 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -10,44 +10,25 @@ export broadcast_getindex, broadcast_setindex! ## Broadcasting utilities ## -# fallback for broadcasting with zero arguments and some special cases +# fallback routines for broadcasting with no arguments or with scalars +# to just produce a scalar result: broadcast(f) = f() -@inline broadcast(f, x::Number...) = f(x...) -@inline broadcast{N}(f, t::NTuple{N}, ts::Vararg{NTuple{N}}) = map(f, t, ts...) -@inline broadcast(f, As::AbstractArray...) = broadcast_t(f, promote_eltype_op(f, As...), As...) +broadcast(f, x::Number...) = f(x...) # special cases for "X .= ..." (broadcast!) assignments broadcast!(::typeof(identity), X::AbstractArray, x::Number) = fill!(X, x) broadcast!(f, X::AbstractArray) = fill!(X, f()) broadcast!(f, X::AbstractArray, x::Number...) = fill!(X, f(x...)) function broadcast!{T,S,N}(::typeof(identity), x::AbstractArray{T,N}, y::AbstractArray{S,N}) - check_broadcast_shape(broadcast_indices(x), broadcast_indices(y)) + check_broadcast_shape(size(x), size(y)) copy!(x, y) end -# logic for deciding the resulting container type -containertype(x) = containertype(typeof(x)) -containertype(::Type) = Any -containertype{T<:Tuple}(::Type{T}) = Tuple -containertype{T<:AbstractArray}(::Type{T}) = Array -containertype(ct1, ct2) = promote_containertype(containertype(ct1), containertype(ct2)) -@inline containertype(ct1, ct2, cts...) = promote_containertype(containertype(ct1), containertype(ct2, cts...)) - -promote_containertype(::Type{Array}, ::Type{Array}) = Array -promote_containertype(::Type{Array}, ct) = Array -promote_containertype(ct, ::Type{Array}) = Array -promote_containertype(::Type{Tuple}, ::Type{Any}) = Tuple -promote_containertype(::Type{Any}, ::Type{Tuple}) = Tuple -promote_containertype{T}(::Type{T}, ::Type{T}) = T - -## Calculate the broadcast indices of the arguments, or error if incompatible +## Calculate the broadcast shape of the arguments, or error if incompatible # array inputs -broadcast_indices() = () -broadcast_indices(A) = broadcast_indices(containertype(A), A) -broadcast_indices(::Type{Any}, A) = () -broadcast_indices(::Type{Tuple}, A) = (OneTo(length(A)),) -broadcast_indices(::Type{Array}, A) = indices(A) -@inline broadcast_indices(A, B...) = broadcast_shape((), broadcast_indices(A), map(broadcast_indices, B)...) +broadcast_shape() = () +broadcast_shape(A) = indices(A) +@inline broadcast_shape(A, B...) = broadcast_shape((), indices(A), map(indices, B)...) # shape (i.e., tuple-of-indices) inputs broadcast_shape(shape::Tuple) = shape @inline broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs((), shape, shape1), shapes...) @@ -69,21 +50,24 @@ _bcsm(a, b) = a == b || length(b) == 1 _bcsm(a, b::Number) = b == 1 _bcsm(a::Number, b::Number) = a == b || b == 1 +## Check that all arguments are broadcast compatible with shape ## Check that all arguments are broadcast compatible with shape # comparing one input against a shape +check_broadcast_shape(::Tuple{}) = nothing +check_broadcast_shape(::Tuple{}, A::Union{AbstractArray,Number}) = check_broadcast_shape((), indices(A)) check_broadcast_shape(shp) = nothing -check_broadcast_shape(shp, ::Tuple{}) = nothing +check_broadcast_shape(shp, A) = check_broadcast_shape(shp, indices(A)) check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing +check_broadcast_shape(shp, ::Tuple{}) = nothing check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions")) function check_broadcast_shape(shp, Ashp::Tuple) _bcsm(shp[1], Ashp[1]) || throw(DimensionMismatch("array could not be broadcast to match destination")) check_broadcast_shape(tail(shp), tail(Ashp)) end -check_broadcast_indices(shp, A) = check_broadcast_shape(shp, broadcast_indices(A)) # comparing many inputs -@inline function check_broadcast_indices(shp, A, As...) - check_broadcast_indices(shp, A) - check_broadcast_indices(shp, As...) +@inline function check_broadcast_shape(shp, A, As...) + check_broadcast_shape(shp, A) + check_broadcast_shape(shp, As...) end ## Indexing manipulations @@ -99,13 +83,14 @@ end # newindexer(shape, A) generates `keep` and `Idefault` (for use by # `newindex` above) for a particular array `A`, given the -# broadcast_indices `shape` +# broadcast_shape `shape` # `keep` is equivalent to map(==, indices(A), shape) (but see #17126) -@inline newindexer(shape, A) = shapeindexer(shape, broadcast_indices(A)) -@inline shapeindexer(shape, indsA::Tuple{}) = (), () -@inline function shapeindexer(shape, indsA::Tuple) +newindexer(shape, x::Number) = (), () +@inline newindexer(shape, A) = newindexer(shape, indices(A)) +@inline newindexer(shape, indsA::Tuple{}) = (), () +@inline function newindexer(shape, indsA::Tuple) ind1 = indsA[1] - keep, Idefault = shapeindexer(tail(shape), tail(indsA)) + keep, Idefault = newindexer(tail(shape), tail(indsA)) (shape[1] == ind1, keep...), (first(ind1), Idefault...) end @@ -125,10 +110,6 @@ const bitcache_size = 64 * bitcache_chunks # do not change this dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) = Base.copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6)) -@inline _broadcast_getindex(A, I) = _broadcast_getindex(containertype(A), A, I) -@inline _broadcast_getindex(::Type{Any}, A, I) = A -@inline _broadcast_getindex(::Any, A, I) = A[I] - ## Broadcasting core # nargs encodes the number of As arguments (which matches the number # of keeps). The first two type parameters are to ensure specialization. @@ -143,7 +124,7 @@ dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) = # reverse-broadcast the indices @nexprs $nargs i->(I_i = newindex(I, keep_i, Idefault_i)) # extract array values - @nexprs $nargs i->(@inbounds val_i = _broadcast_getindex(A_i, I_i)) + @nexprs $nargs i->(@inbounds val_i = A_i[I_i]) # call the function and store the result @inbounds B[I] = @ncall $nargs f val end @@ -167,7 +148,7 @@ end # reverse-broadcast the indices @nexprs $nargs i->(I_i = newindex(I, keep_i, Idefault_i)) # extract array values - @nexprs $nargs i->(@inbounds val_i = _broadcast_getindex(A_i, I_i)) + @nexprs $nargs i->(@inbounds val_i = A_i[I_i]) # call the function and store the result @inbounds C[ind] = @ncall $nargs f val ind += 1 @@ -195,7 +176,7 @@ as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`. """ @inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs}) shape = indices(B) - check_broadcast_indices(shape, As...) + check_broadcast_shape(shape, As...) keeps, Idefaults = map_newindexer(shape, As) _broadcast!(f, B, keeps, Idefaults, As, Val{nargs}) B @@ -215,7 +196,7 @@ end # reverse-broadcast the indices @nexprs $nargs i->(I_i = newindex(I, keep_i, Idefault_i)) # extract array values - @nexprs $nargs i->(@inbounds val_i = _broadcast_getindex(A_i, I_i)) + @nexprs $nargs i->(@inbounds val_i = A_i[I_i]) # call the function V = @ncall $nargs f val S = typeof(V) @@ -238,7 +219,7 @@ end end function broadcast_t(f, ::Type{Any}, As...) - shape = broadcast_indices(As...) + shape = broadcast_shape(As...) iter = CartesianRange(shape) if isempty(iter) return similar(Array{Any}, shape) @@ -247,46 +228,19 @@ function broadcast_t(f, ::Type{Any}, As...) keeps, Idefaults = map_newindexer(shape, As) st = start(iter) I, st = next(iter, st) - val = f([ _broadcast_getindex(As[i], newindex(I, keeps[i], Idefaults[i])) for i=1:nargs ]...) + val = f([ As[i][newindex(I, keeps[i], Idefaults[i])] for i=1:nargs ]...) B = similar(Array{typeof(val)}, shape) B[I] = val return _broadcast!(f, B, keeps, Idefaults, As, Val{nargs}, iter, st, 1) end -@inline broadcast_t(f, T, As...) = broadcast!(f, similar(Array{T}, broadcast_indices(As...)), As...) - -@generated function broadcast_tup{AT,nargs}(f, As::AT, ::Type{Val{nargs}}, n) - quote - ntuple(n -> (@ncall $nargs f i->_broadcast_getindex(As[i], n)), Val{n}) - end -end - -function broadcast_c(f, ::Type{Tuple}, As...) - shape = broadcast_indices(As...) - check_broadcast_indices(shape, As...) - n = length(shape[1]) - nargs = length(As) - return broadcast_tup(f, As, Val{nargs}, n) -end -@inline broadcast_c(f, ::Type{Any}, a...) = f(a...) -@inline broadcast_c(f, ::Type{Array}, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...) +@inline broadcast_t(f, T, As...) = broadcast!(f, similar(Array{T}, broadcast_shape(As...)), As...) """ broadcast(f, As...) -Broadcasts the arrays, tuples and/or scalars `As` to a container of the -appropriate type and dimensions. In this context, anything that is not a -subtype of `AbstractArray` or `Tuple` is considered a scalar. The resulting -container is stablished by the following rules: - - - If all the arguments are scalars, it returns a scalar. - - If the arguments are tuples and zero or more scalars, it returns a tuple. - - If there is at least an array in the arguments, it returns an array - (and treats tuples as 1-dimensional arrays) expanding singleton dimensions. - -A special syntax exists for broadcasting: `f.(args...)` is equivalent to -`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a -single broadcast loop. +Broadcasts the arrays `As` to a common size by expanding singleton dimensions, and returns +an array of the results `f(as...)` for each position. ```jldoctest julia> A = [1, 2, 3, 4, 5] @@ -312,32 +266,27 @@ julia> broadcast(+, A, B) 8 9 11 12 14 15 - -julia> parse.(Int, ["1", "2"]) -2-element Array{Int64,1}: - 1 - 2 - -julia> abs.((1, -2)) -(1,2) - -julia> broadcast(+, 1.0, (0, -2.0)) -(1.0,-1.0) - -julia> broadcast(+, 1.0, (0, -2.0), [1]) -2-element Array{Float64,1}: - 2.0 - 0.0 - -julia> string.(("one","two","three","four"), ": ", 1:4) -4-element Array{String,1}: - "one: 1" - "two: 2" - "three: 3" - "four: 4" ``` """ -@inline broadcast(f, As...) = broadcast_c(f, containertype(As...), As...) +@inline broadcast(f, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...) + +# alternate, more compact implementation; unfortunately slower. +# also the `collect` machinery doesn't yet support arbitrary index bases. +#= +@generated function _broadcast{nargs}(f, keeps, As, ::Type{Val{nargs}}, iter) + quote + collect((@ncall $nargs f i->As[i][newindex(I, keeps[i])]) for I in iter) + end +end + +function broadcast(f, As...) + shape = broadcast_shape(As...) + iter = CartesianRange(shape) + keeps, Idefaults = map_newindexer(shape, As) + naT = Val{nfields(As)} + _broadcast(f, keeps, Idefaults, As, naT, iter) +end +=# """ bitbroadcast(f, As...) @@ -355,7 +304,7 @@ julia> bitbroadcast(isodd,[1,2,3,4,5]) true ``` """ -@inline bitbroadcast(f, As...) = broadcast!(f, similar(BitArray, broadcast_indices(As...)), As...) +@inline bitbroadcast(f, As...) = broadcast!(f, similar(BitArray, broadcast_shape(As...)), As...) """ broadcast_getindex(A, inds...) @@ -396,13 +345,13 @@ julia> broadcast_getindex(C,[1,2,10]) 15 ``` """ -broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(similar(Array{eltype(src)}, broadcast_indices(I...)), src, I...) +broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(similar(Array{eltype(src)}, broadcast_shape(I...)), src, I...) @generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...) N = length(I) Isplat = Expr[:(I[$d]) for d = 1:N] quote @nexprs $N d->(I_d = I[d]) - check_broadcast_indices(indices(dest), $(Isplat...)) # unnecessary if this function is never called directly + check_broadcast_shape(indices(dest), $(Isplat...)) # unnecessary if this function is never called directly checkbounds(src, $(Isplat...)) @nexprs $N d->(@nexprs $N k->(Ibcast_d_k = indices(I_k, d) == OneTo(1))) @nloops $N i dest d->(@nexprs $N k->(j_d_k = Ibcast_d_k ? 1 : i_d)) begin @@ -425,7 +374,7 @@ position in `X` at the indices in `A` given by the same positions in `inds`. quote @nexprs $N d->(I_d = I[d]) checkbounds(A, $(Isplat...)) - shape = broadcast_indices($(Isplat...)) + shape = broadcast_shape($(Isplat...)) @nextract $N shape d->(length(shape) < d ? OneTo(1) : shape[d]) @nexprs $N d->(@nexprs $N k->(Ibcast_d_k = indices(I_k, d) == 1:1)) if !isa(x, AbstractArray) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 875e1997b515c..a0bbccb574f93 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -282,7 +282,6 @@ maybe_oneto() = OneTo(1) ### From abstractarray.jl: Internal multidimensional indexing definitions ### getindex(x::Number, i::CartesianIndex{0}) = x -getindex(t::Tuple, I...) = getindex(t, IteratorsMD.flatten(I)...) # These are not defined on directly on getindex to avoid # ambiguities for AbstractArray subtypes. See the note in abstractarray.jl diff --git a/base/number.jl b/base/number.jl index 21185aff75bb5..b98655c5cd5e3 100644 --- a/base/number.jl +++ b/base/number.jl @@ -99,8 +99,6 @@ zero{T<:Number}(::Type{T}) = convert(T,0) one(x::Number) = oftype(x,1) one{T<:Number}(::Type{T}) = convert(T,1) -_default_type(::Type{Number}) = Int - """ factorial(n) diff --git a/base/reducedim.jl b/base/reducedim.jl index a6bc026ee0ae5..95770ea0abafd 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -196,7 +196,7 @@ function _mapreducedim!{T,N}(f, op, R::AbstractArray, A::AbstractArray{T,N}) return R end indsAt, indsRt = safe_tail(indices(A)), safe_tail(indices(R)) # handle d=1 manually - keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) + keep, Idefault = Broadcast.newindexer(indsAt, indsRt) if reducedim1(R, A) # keep the accumulator as a local variable when reducing along the first dimension i1 = first(indices1(R)) @@ -331,7 +331,7 @@ function findminmax!{T,N}(f, Rval, Rind, A::AbstractArray{T,N}) # If we're reducing along dimension 1, for efficiency we can make use of a temporary. # Otherwise, keep the result in Rval/Rind so that we traverse A in storage order. indsAt, indsRt = safe_tail(indices(A)), safe_tail(indices(Rval)) - keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) + keep, Idefault = Broadcast.newindexer(indsAt, indsRt) k = 0 if reducedim1(Rval, A) i1 = first(indices1(Rval)) diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index 1936ca01f4e75..189a8562814df 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -26,7 +26,7 @@ import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, rotl90, rotr90, round, scale!, setindex!, similar, size, transpose, tril, triu, vec, permute! -import Base.Broadcast: broadcast_indices +import Base.Broadcast: broadcast_shape export AbstractSparseArray, AbstractSparseMatrix, AbstractSparseVector, SparseMatrixCSC, SparseVector, blkdiag, dense, droptol!, dropzeros!, dropzeros, diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index f5ed76d2032be..034c42cdf4f62 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1509,8 +1509,8 @@ end function gen_broadcast_body_sparse(f::Function, is_first_sparse::Bool) F = Expr(:quote, f) quote - Base.Broadcast.check_broadcast_indices(indices(B), A_1) - Base.Broadcast.check_broadcast_indices(indices(B), A_2) + Base.Broadcast.check_broadcast_shape(indices(B), A_1) + Base.Broadcast.check_broadcast_shape(indices(B), A_2) colptrB = B.colptr; rowvalB = B.rowval; nzvalB = B.nzval colptr1 = A_1.colptr; rowval1 = A_1.rowval; nzval1 = A_1.nzval @@ -1673,8 +1673,8 @@ function gen_broadcast_body_zpreserving(f::Function, is_first_sparse::Bool) op2 = :(val1) end quote - Base.Broadcast.check_broadcast_indices(indices(B), $A1) - Base.Broadcast.check_broadcast_indices(indices(B), $A2) + Base.Broadcast.check_broadcast_shape(indices(B), $A1) + Base.Broadcast.check_broadcast_shape(indices(B), $A2) nnzB = isempty(B) ? 0 : nnz($A1) * div(B.n, ($A1).n) * div(B.m, ($A1).m) @@ -1743,16 +1743,16 @@ end broadcast{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) + broadcast!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) @inline broadcast_zpreserving!(args...) = broadcast!(args...) @inline broadcast_zpreserving(args...) = broadcast(args...) broadcast_zpreserving{Tv1,Ti1,Tv2,Ti2}(f::Function, A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_type(Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::SparseMatrixCSC{Tv,Ti}, A_2::Union{Array,BitArray,Number}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) broadcast_zpreserving{Tv,Ti}(f::Function, A_1::Union{Array,BitArray,Number}, A_2::SparseMatrixCSC{Tv,Ti}) = - broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) + broadcast_zpreserving!(f, spzeros(promote_eltype(A_1, A_2), Ti, to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) ## Binary arithmetic and boolean operators @@ -1766,7 +1766,7 @@ for op in (+, -, min, max, &, |, $) throw(DimensionMismatch("")) end Tv = promote_op($op, Tv1, Tv2) - B = spzeros(Tv, promote_type(Ti1, Ti2), to_shape(broadcast_indices(A_1, A_2))) + B = spzeros(Tv, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))) $body B end @@ -1808,15 +1808,15 @@ end # macro (.^)(A::Array, B::SparseMatrixCSC) = (.^)(A, full(B)) .+{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = - broadcast!(+, spzeros(promote_op(+, Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) + broadcast!(+, spzeros(promote_op(+, Tv1, Tv2), promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) function .-{Tva,Tia,Tvb,Tib}(A::SparseMatrixCSC{Tva,Tia}, B::SparseMatrixCSC{Tvb,Tib}) - broadcast!(-, spzeros(promote_op(-, Tva, Tvb), promote_type(Tia, Tib), to_shape(broadcast_indices(A, B))), A, B) + broadcast!(-, spzeros(promote_op(-, Tva, Tvb), promote_type(Tia, Tib), to_shape(broadcast_shape(A, B))), A, B) end ## element-wise comparison operators returning SparseMatrixCSC ## -.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) -.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_indices(A_1, A_2))), A_1, A_2) +.<{Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(<, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) +.!={Tv1,Ti1,Tv2,Ti2}(A_1::SparseMatrixCSC{Tv1,Ti1}, A_2::SparseMatrixCSC{Tv2,Ti2}) = broadcast!(!=, spzeros( Bool, promote_type(Ti1, Ti2), to_shape(broadcast_shape(A_1, A_2))), A_1, A_2) ## full equality function ==(A1::SparseMatrixCSC, A2::SparseMatrixCSC) diff --git a/base/statistics.jl b/base/statistics.jl index 05805f2a2db50..e3186fc83485e 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -116,7 +116,7 @@ function centralize_sumabs2!{S,T,N}(R::AbstractArray{S}, A::AbstractArray{T,N}, return R end indsAt, indsRt = safe_tail(indices(A)), safe_tail(indices(R)) # handle d=1 manually - keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) + keep, Idefault = Broadcast.newindexer(indsAt, indsRt) if reducedim1(R, A) i1 = first(indices1(R)) @inbounds for IA in CartesianRange(indsAt) diff --git a/doc/manual/arrays.rst b/doc/manual/arrays.rst index ba99ceb041973..93b09ab1525c3 100644 --- a/doc/manual/arrays.rst +++ b/doc/manual/arrays.rst @@ -526,27 +526,6 @@ function elementwise: Elementwise operators such as ``.+`` and ``.*`` perform broadcasting if necessary. There is also a :func:`broadcast!` function to specify an explicit destination, and :func:`broadcast_getindex` and :func:`broadcast_setindex!` that broadcast the indices before indexing. Moreover, ``f.(args...)`` is equivalent to ``broadcast(f, args...)``, providing a convenient syntax to broadcast any function (:ref:`man-dot-vectorizing`). -Additionally, :func:`broadcast` is not limited to arrays (see the function documentation), it also handles tuples and treats any argument that is not an array or a tuple as a "scalar". - -.. doctest:: - - julia> convert.(Float32, [1, 2]) - 2-element Array{Float32,1}: - 1.0 - 2.0 - - julia> ceil.((UInt8,), [1.2 3.4; 5.6 6.7]) - 2×2 Array{UInt8,2}: - 0x02 0x04 - 0x06 0x07 - - julia> string.(1:3, ". ", ["First", "Second", "Third"]) - 3-element Array{String,1}: - "1. First" - "2. Second" - "3. Third" - - Implementation -------------- diff --git a/doc/stdlib/arrays.rst b/doc/stdlib/arrays.rst index 3fefb0ef6bc87..33b99f5c3630f 100644 --- a/doc/stdlib/arrays.rst +++ b/doc/stdlib/arrays.rst @@ -606,13 +606,7 @@ All mathematical operations and functions are supported for arrays .. Docstring generated from Julia source - Broadcasts the arrays, tuples and/or scalars ``As`` to a container of the appropriate type and dimensions. In this context, anything that is not a subtype of ``AbstractArray`` or ``Tuple`` is considered a scalar. The resulting container is stablished by the following rules: - - * If all the arguments are scalars, it returns a scalar. - * If the arguments are tuples and zero or more scalars, it returns a tuple. - * If there is at least an array in the arguments, it returns an array (and treats tuples as 1-dimensional arrays) expanding singleton dimensions. - - A special syntax exists for broadcasting: ``f.(args...)`` is equivalent to ``broadcast(f, args...)``\ , and nested ``f.(g.(args...))`` calls are fused into a single broadcast loop. + Broadcasts the arrays ``As`` to a common size by expanding singleton dimensions, and returns an array of the results ``f(as...)`` for each position. .. doctest:: @@ -640,29 +634,6 @@ All mathematical operations and functions are supported for arrays 11 12 14 15 - julia> parse.(Int, ["1", "2"]) - 2-element Array{Int64,1}: - 1 - 2 - - julia> abs.((1, -2)) - (1,2) - - julia> broadcast(+, 1.0, (0, -2.0)) - (1.0,-1.0) - - julia> broadcast(+, 1.0, (0, -2.0), [1]) - 2-element Array{Float64,1}: - 2.0 - 0.0 - - julia> string.(("one","two","three","four"), ": ", 1:4) - 4-element Array{String,1}: - "one: 1" - "two: 2" - "three: 3" - "four: 4" - .. function:: broadcast!(f, dest, As...) .. Docstring generated from Julia source diff --git a/test/broadcast.jl b/test/broadcast.jl index 7dca2095bff7e..fc30e1c4fec5a 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -2,8 +2,7 @@ module TestBroadcastInternals -using Base.Broadcast: broadcast_indices, check_broadcast_indices, - check_broadcast_shape, newindex, _bcs, _bcsm +using Base.Broadcast: broadcast_shape, check_broadcast_shape, newindex, _bcs, _bcsm using Base: Test, OneTo @test @inferred(_bcs((), (3,5), (3,5))) == (3,5) @@ -19,28 +18,28 @@ using Base: Test, OneTo @test_throws DimensionMismatch _bcs((), (-1:1, 2:6), (-1:1, 2:5)) @test_throws DimensionMismatch _bcs((), (-1:1, 2:5), (2, 2:5)) -@test @inferred(broadcast_indices(zeros(3,4), zeros(3,4))) == (OneTo(3),OneTo(4)) -@test @inferred(broadcast_indices(zeros(3,4), zeros(3))) == (OneTo(3),OneTo(4)) -@test @inferred(broadcast_indices(zeros(3), zeros(3,4))) == (OneTo(3),OneTo(4)) -@test @inferred(broadcast_indices(zeros(3), zeros(1,4), zeros(1))) == (OneTo(3),OneTo(4)) - -check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,5)) -check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,1)) -check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3)) -check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,5), zeros(3)) -check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,5), 1) -check_broadcast_indices((OneTo(3),OneTo(5)), 5, 2) -@test_throws DimensionMismatch check_broadcast_indices((OneTo(3),OneTo(5)), zeros(2,5)) -@test_throws DimensionMismatch check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,4)) -@test_throws DimensionMismatch check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,4,2)) -@test_throws DimensionMismatch check_broadcast_indices((OneTo(3),OneTo(5)), zeros(3,5), zeros(2)) -check_broadcast_indices((-1:1, 6:9), 1) +@test @inferred(broadcast_shape(zeros(3,4), zeros(3,4))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3,4), zeros(3))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3), zeros(3,4))) == (OneTo(3),OneTo(4)) +@test @inferred(broadcast_shape(zeros(3), zeros(1,4), zeros(1))) == (OneTo(3),OneTo(4)) + +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,1)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), zeros(3)) +check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), 1) +check_broadcast_shape((OneTo(3),OneTo(5)), 5, 2) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(2,5)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,4)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,4,2)) +@test_throws DimensionMismatch check_broadcast_shape((OneTo(3),OneTo(5)), zeros(3,5), zeros(2)) check_broadcast_shape((-1:1, 6:9), (-1:1, 6:9)) check_broadcast_shape((-1:1, 6:9), (-1:1, 1)) check_broadcast_shape((-1:1, 6:9), (1, 6:9)) @test_throws DimensionMismatch check_broadcast_shape((-1:1, 6:9), (-1, 6:9)) @test_throws DimensionMismatch check_broadcast_shape((-1:1, 6:9), (-1:1, 6)) +check_broadcast_shape((-1:1, 6:9), 1) ci(x) = CartesianIndex(x) @test @inferred(newindex(ci((2,2)), (true, true), (-1,-1))) == ci((2,2)) @@ -328,12 +327,3 @@ end let A17984 = [] @test isa(abs.(A17984), Array{Any,1}) end - -# Issue #16966 -@test parse.(Int, "1") == 1 -@test parse.(Int, ["1", "2"]) == [1, 2] -@test trunc.((Int,), [1.2, 3.4]) == [1, 3] -@test abs.((1, -2)) == (1, 2) -@test broadcast(+, 1.0, (0, -2.0)) == (1.0,-1.0) -@test broadcast(+, 1.0, (0, -2.0), [1]) == [2.0, 0.0] -@test broadcast(*, ["Hello"], ", ", ["World"], "!") == ["Hello, World!"]