fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
631
julia-0.6.3/share/julia/base/broadcast.jl
Normal file
631
julia-0.6.3/share/julia/base/broadcast.jl
Normal file
@@ -0,0 +1,631 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module Broadcast
|
||||
|
||||
using Base.Cartesian
|
||||
using Base: linearindices, tail, OneTo, to_shape,
|
||||
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache,
|
||||
nullable_returntype, null_safe_op, hasvalue, isoperator
|
||||
import Base: broadcast, broadcast!
|
||||
export broadcast_getindex, broadcast_setindex!, dotview, @__dot__
|
||||
|
||||
const ScalarType = Union{Type{Any}, Type{Nullable}}
|
||||
|
||||
## Broadcasting utilities ##
|
||||
# fallbacks for some special cases
|
||||
@inline broadcast(f, x::Number...) = f(x...)
|
||||
@inline broadcast(f, t::NTuple{N,Any}, ts::Vararg{NTuple{N,Any}}) where {N} = map(f, t, ts...)
|
||||
broadcast!(::typeof(identity), x::Array{T,N}, y::Array{S,N}) where {T,S,N} =
|
||||
size(x) == size(y) ? copy!(x, y) : broadcast_c!(identity, Array, Array, x, y)
|
||||
|
||||
# special cases for "X .= ..." (broadcast!) assignments
|
||||
broadcast!(::typeof(identity), X::AbstractArray, x::Number) = fill!(X, x)
|
||||
broadcast!(f, X::AbstractArray, x::Number...) = (@inbounds for I in eachindex(X); X[I] = f(x...); end; X)
|
||||
|
||||
# logic for deciding the resulting container type
|
||||
_containertype(::Type) = Any
|
||||
_containertype(::Type{<:Ptr}) = Any
|
||||
_containertype(::Type{<:Tuple}) = Tuple
|
||||
_containertype(::Type{<:Ref}) = Array
|
||||
_containertype(::Type{<:AbstractArray}) = Array
|
||||
_containertype(::Type{<:Nullable}) = Nullable
|
||||
containertype(x) = _containertype(typeof(x))
|
||||
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}, ::ScalarType) = Tuple
|
||||
promote_containertype(::ScalarType, ::Type{Tuple}) = Tuple
|
||||
promote_containertype(::Type{Any}, ::Type{Nullable}) = Nullable
|
||||
promote_containertype(::Type{Nullable}, ::Type{Any}) = Nullable
|
||||
promote_containertype(::Type{T}, ::Type{T}) where {T} = T
|
||||
|
||||
## Calculate the broadcast indices of the arguments, or error if incompatible
|
||||
# array inputs
|
||||
broadcast_indices() = ()
|
||||
broadcast_indices(A) = broadcast_indices(containertype(A), A)
|
||||
broadcast_indices(::ScalarType, A) = ()
|
||||
broadcast_indices(::Type{Tuple}, A) = (OneTo(length(A)),)
|
||||
broadcast_indices(::Type{Array}, A::Ref) = ()
|
||||
broadcast_indices(::Type{Array}, A) = indices(A)
|
||||
@inline broadcast_indices(A, B...) = broadcast_shape((), broadcast_indices(A), map(broadcast_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...)
|
||||
# _bcs consolidates two shapes into a single output shape
|
||||
_bcs(out, ::Tuple{}, ::Tuple{}) = out
|
||||
@inline _bcs(out, ::Tuple{}, newshape) = _bcs((out..., newshape[1]), (), tail(newshape))
|
||||
@inline _bcs(out, shape, ::Tuple{}) = _bcs((out..., shape[1]), tail(shape), ())
|
||||
@inline function _bcs(out, shape, newshape)
|
||||
newout = _bcs1(shape[1], newshape[1])
|
||||
_bcs((out..., newout), tail(shape), tail(newshape))
|
||||
end
|
||||
# _bcs1 handles the logic for a single dimension
|
||||
_bcs1(a::Integer, b::Integer) = a == 1 ? b : (b == 1 ? a : (a == b ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size"))))
|
||||
_bcs1(a::Integer, b) = a == 1 ? b : (first(b) == 1 && last(b) == a ? b : throw(DimensionMismatch("arrays could not be broadcast to a common size")))
|
||||
_bcs1(a, b::Integer) = _bcs1(b, a)
|
||||
_bcs1(a, b) = _bcsm(b, a) ? b : (_bcsm(a, b) ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size")))
|
||||
# _bcsm tests whether the second index is consistent with the first
|
||||
_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
|
||||
# comparing one input against a shape
|
||||
check_broadcast_shape(shp) = nothing
|
||||
check_broadcast_shape(shp, ::Tuple{}) = nothing
|
||||
check_broadcast_shape(::Tuple{}, ::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...)
|
||||
end
|
||||
|
||||
## Indexing manipulations
|
||||
|
||||
# newindex(I, keep, Idefault) replaces a CartesianIndex `I` with something that
|
||||
# is appropriate for a particular broadcast array/scalar. `keep` is a
|
||||
# NTuple{N,Bool}, where keep[d] == true means that one should preserve
|
||||
# I[d]; if false, replace it with Idefault[d].
|
||||
@inline newindex(I::CartesianIndex, keep, Idefault) = CartesianIndex(_newindex(I.I, keep, Idefault))
|
||||
@inline _newindex(I, keep, Idefault) =
|
||||
(ifelse(keep[1], I[1], Idefault[1]), _newindex(tail(I), tail(keep), tail(Idefault))...)
|
||||
@inline _newindex(I, keep::Tuple{}, Idefault) = () # truncate if keep is shorter than I
|
||||
|
||||
# newindexer(shape, A) generates `keep` and `Idefault` (for use by
|
||||
# `newindex` above) for a particular array `A`, given the
|
||||
# broadcast_indices `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)
|
||||
ind1 = indsA[1]
|
||||
keep, Idefault = shapeindexer(tail(shape), tail(indsA))
|
||||
(shape[1] == ind1, keep...), (first(ind1), Idefault...)
|
||||
end
|
||||
|
||||
# Equivalent to map(x->newindexer(shape, x), As) (but see #17126)
|
||||
map_newindexer(shape, ::Tuple{}) = (), ()
|
||||
@inline function map_newindexer(shape, As)
|
||||
A1 = As[1]
|
||||
keeps, Idefaults = map_newindexer(shape, tail(As))
|
||||
keep, Idefault = newindexer(shape, A1)
|
||||
(keep, keeps...), (Idefault, Idefaults...)
|
||||
end
|
||||
@inline function map_newindexer(shape, A, Bs)
|
||||
keeps, Idefaults = map_newindexer(shape, Bs)
|
||||
keep, Idefault = newindexer(shape, A)
|
||||
(keep, keeps...), (Idefault, Idefaults...)
|
||||
end
|
||||
|
||||
Base.@propagate_inbounds _broadcast_getindex(A, I) = _broadcast_getindex(containertype(A), A, I)
|
||||
# `(x,)`, where `x` is a scalar, broadcasts the same way as `[x]` or `x`
|
||||
Base.@propagate_inbounds _broadcast_getindex(::Type{Tuple}, A::Tuple{Any}, I) = A[1]
|
||||
Base.@propagate_inbounds _broadcast_getindex(::Type{Array}, A::Ref, I) = A[]
|
||||
Base.@propagate_inbounds _broadcast_getindex(::ScalarType, A, I) = A
|
||||
Base.@propagate_inbounds _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.
|
||||
@generated function _broadcast!(f, B::AbstractArray, keeps::K, Idefaults::ID, A::AT, Bs::BT, ::Type{Val{N}}, iter) where {K,ID,AT,BT,N}
|
||||
nargs = N + 1
|
||||
quote
|
||||
$(Expr(:meta, :inline))
|
||||
# destructure the keeps and As tuples
|
||||
A_1 = A
|
||||
@nexprs $N i->(A_{i+1} = Bs[i])
|
||||
@nexprs $nargs i->(keep_i = keeps[i])
|
||||
@nexprs $nargs i->(Idefault_i = Idefaults[i])
|
||||
@simd for I in iter
|
||||
# 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))
|
||||
# call the function and store the result
|
||||
result = @ncall $nargs f val
|
||||
@inbounds B[I] = result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# For BitArray outputs, we cache the result in a "small" Vector{Bool},
|
||||
# and then copy in chunks into the output
|
||||
@generated function _broadcast!(f, B::BitArray, keeps::K, Idefaults::ID, A::AT, Bs::BT, ::Type{Val{N}}, iter) where {K,ID,AT,BT,N}
|
||||
nargs = N + 1
|
||||
quote
|
||||
$(Expr(:meta, :inline))
|
||||
# destructure the keeps and As tuples
|
||||
A_1 = A
|
||||
@nexprs $N i->(A_{i+1} = Bs[i])
|
||||
@nexprs $nargs i->(keep_i = keeps[i])
|
||||
@nexprs $nargs i->(Idefault_i = Idefaults[i])
|
||||
C = Vector{Bool}(bitcache_size)
|
||||
Bc = B.chunks
|
||||
ind = 1
|
||||
cind = 1
|
||||
@simd for I in iter
|
||||
# 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))
|
||||
# call the function and store the result
|
||||
@inbounds C[ind] = @ncall $nargs f val
|
||||
ind += 1
|
||||
if ind > bitcache_size
|
||||
dumpbitcache(Bc, cind, C)
|
||||
cind += bitcache_chunks
|
||||
ind = 1
|
||||
end
|
||||
end
|
||||
if ind > 1
|
||||
@inbounds C[ind:bitcache_size] = false
|
||||
dumpbitcache(Bc, cind, C)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
broadcast!(f, dest, As...)
|
||||
|
||||
Like [`broadcast`](@ref), but store the result of
|
||||
`broadcast(f, As...)` in the `dest` array.
|
||||
Note that `dest` is only used to store the result, and does not supply
|
||||
arguments to `f` unless it is also listed in the `As`,
|
||||
as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`.
|
||||
"""
|
||||
@inline broadcast!(f, C::AbstractArray, A, Bs::Vararg{Any,N}) where {N} =
|
||||
broadcast_c!(f, containertype(C), containertype(A, Bs...), C, A, Bs...)
|
||||
@inline function broadcast_c!(f, ::Type, ::Type, C, A, Bs::Vararg{Any,N}) where N
|
||||
shape = indices(C)
|
||||
@boundscheck check_broadcast_indices(shape, A, Bs...)
|
||||
keeps, Idefaults = map_newindexer(shape, A, Bs)
|
||||
iter = CartesianRange(shape)
|
||||
_broadcast!(f, C, keeps, Idefaults, A, Bs, Val{N}, iter)
|
||||
return C
|
||||
end
|
||||
|
||||
# broadcast with computed element type
|
||||
@generated function _broadcast!(f, B::AbstractArray, keeps::K, Idefaults::ID, As::AT, ::Type{Val{nargs}}, iter, st, count) where {K,ID,AT,nargs}
|
||||
quote
|
||||
$(Expr(:meta, :noinline))
|
||||
# destructure the keeps and As tuples
|
||||
@nexprs $nargs i->(A_i = As[i])
|
||||
@nexprs $nargs i->(keep_i = keeps[i])
|
||||
@nexprs $nargs i->(Idefault_i = Idefaults[i])
|
||||
while !done(iter, st)
|
||||
I, st = next(iter, st)
|
||||
# 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))
|
||||
# call the function
|
||||
V = @ncall $nargs f val
|
||||
S = typeof(V)
|
||||
# store the result
|
||||
if S <: eltype(B)
|
||||
@inbounds B[I] = V
|
||||
else
|
||||
R = typejoin(eltype(B), S)
|
||||
new = similar(B, R)
|
||||
for II in Iterators.take(iter, count)
|
||||
new[II] = B[II]
|
||||
end
|
||||
new[I] = V
|
||||
return _broadcast!(f, new, keeps, Idefaults, As, Val{nargs}, iter, st, count+1)
|
||||
end
|
||||
count += 1
|
||||
end
|
||||
return B
|
||||
end
|
||||
end
|
||||
|
||||
# broadcast methods that dispatch on the type found by inference
|
||||
function broadcast_t(f, ::Type{Any}, shape, iter, As...)
|
||||
nargs = length(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 ]...)
|
||||
if val isa Bool
|
||||
B = similar(BitArray, shape)
|
||||
else
|
||||
B = similar(Array{typeof(val)}, shape)
|
||||
end
|
||||
B[I] = val
|
||||
return _broadcast!(f, B, keeps, Idefaults, As, Val{nargs}, iter, st, 1)
|
||||
end
|
||||
@inline function broadcast_t(f, T, shape, iter, A, Bs::Vararg{Any,N}) where N
|
||||
C = similar(Array{T}, shape)
|
||||
keeps, Idefaults = map_newindexer(shape, A, Bs)
|
||||
_broadcast!(f, C, keeps, Idefaults, A, Bs, Val{N}, iter)
|
||||
return C
|
||||
end
|
||||
|
||||
# default to BitArray for broadcast operations producing Bool, to save 8x space
|
||||
# in the common case where this is used for logical array indexing; in
|
||||
# performance-critical cases where Array{Bool} is desired, one can always
|
||||
# use broadcast! instead.
|
||||
@inline function broadcast_t(f, ::Type{Bool}, shape, iter, A, Bs::Vararg{Any,N}) where N
|
||||
C = similar(BitArray, shape)
|
||||
keeps, Idefaults = map_newindexer(shape, A, Bs)
|
||||
_broadcast!(f, C, keeps, Idefaults, A, Bs, Val{N}, iter)
|
||||
return C
|
||||
end
|
||||
|
||||
maptoTuple(f) = Tuple{}
|
||||
maptoTuple(f, a, b...) = Tuple{f(a), maptoTuple(f, b...).types...}
|
||||
|
||||
# An element type satisfying for all A:
|
||||
# broadcast_getindex(
|
||||
# containertype(A),
|
||||
# A, broadcast_indices(A)
|
||||
# )::_broadcast_getindex_eltype(A)
|
||||
_broadcast_getindex_eltype(A) = _broadcast_getindex_eltype(containertype(A), A)
|
||||
_broadcast_getindex_eltype(::ScalarType, T::Type) = Type{T}
|
||||
_broadcast_getindex_eltype(::ScalarType, A) = typeof(A)
|
||||
_broadcast_getindex_eltype(::Any, A) = eltype(A) # Tuple, Array, etc.
|
||||
|
||||
# An element type satisfying for all A:
|
||||
# unsafe_get(A)::unsafe_get_eltype(A)
|
||||
_unsafe_get_eltype(x::Nullable) = eltype(x)
|
||||
_unsafe_get_eltype(T::Type) = Type{T}
|
||||
_unsafe_get_eltype(x) = typeof(x)
|
||||
|
||||
# Inferred eltype of result of broadcast(f, xs...)
|
||||
_broadcast_eltype(f, A, As...) =
|
||||
Base._return_type(f, maptoTuple(_broadcast_getindex_eltype, A, As...))
|
||||
_nullable_eltype(f, A, As...) =
|
||||
Base._return_type(f, maptoTuple(_unsafe_get_eltype, A, As...))
|
||||
|
||||
# broadcast methods that dispatch on the type of the final container
|
||||
@inline function broadcast_c(f, ::Type{Array}, A, Bs...)
|
||||
T = _broadcast_eltype(f, A, Bs...)
|
||||
shape = broadcast_indices(A, Bs...)
|
||||
iter = CartesianRange(shape)
|
||||
if isleaftype(T)
|
||||
return broadcast_t(f, T, shape, iter, A, Bs...)
|
||||
end
|
||||
if isempty(iter)
|
||||
return similar(Array{T}, shape)
|
||||
end
|
||||
return broadcast_t(f, Any, shape, iter, A, Bs...)
|
||||
end
|
||||
@inline function broadcast_c(f, ::Type{Nullable}, a...)
|
||||
nonnull = all(hasvalue, a)
|
||||
S = _nullable_eltype(f, a...)
|
||||
if isleaftype(S) && null_safe_op(f, maptoTuple(_unsafe_get_eltype,
|
||||
a...).types...)
|
||||
Nullable{S}(f(map(unsafe_get, a)...), nonnull)
|
||||
else
|
||||
if nonnull
|
||||
Nullable(f(map(unsafe_get, a)...))
|
||||
else
|
||||
Nullable{nullable_returntype(S)}()
|
||||
end
|
||||
end
|
||||
end
|
||||
@inline broadcast_c(f, ::Type{Any}, a...) = f(a...)
|
||||
@inline broadcast_c(f, ::Type{Tuple}, A, Bs...) =
|
||||
tuplebroadcast(f, tuplebroadcast_maxtuple(A, Bs...), A, Bs...)
|
||||
@inline tuplebroadcast(f, ::NTuple{N,Any}, As...) where {N} =
|
||||
ntuple(k -> f(tuplebroadcast_getargs(As, k)...), Val{N})
|
||||
@inline tuplebroadcast(f, ::NTuple{N,Any}, ::Type{T}, As...) where {N,T} =
|
||||
ntuple(k -> f(T, tuplebroadcast_getargs(As, k)...), Val{N})
|
||||
# When the result of broadcast is a tuple it can only come from mixing n-tuples
|
||||
# of the same length with scalars and 1-tuples. So, in order to have a
|
||||
# type-stable broadcast, we need to find a tuple of maximum length (except when
|
||||
# there are only scalars, empty tuples and 1-tuples, in which case the
|
||||
# returned value will be an empty tuple).
|
||||
# The following methods compare broadcast arguments pairwise to determine the
|
||||
# length of the final tuple.
|
||||
tuplebroadcast_maxtuple(A, B) =
|
||||
_tuplebroadcast_maxtuple(containertype(A), containertype(B), A, B)
|
||||
@inline tuplebroadcast_maxtuple(A, Bs...) =
|
||||
tuplebroadcast_maxtuple(A, tuplebroadcast_maxtuple(Bs...))
|
||||
tuplebroadcast_maxtuple(A::NTuple{N,Any}, ::NTuple{N,Any}...) where {N} = A
|
||||
# Here we use the containertype trait to easier disambiguate between methods
|
||||
_tuplebroadcast_maxtuple(::Any, ::Any, A, B) = (nothing,)
|
||||
_tuplebroadcast_maxtuple(::Type{Tuple}, ::Any, A, B) = A
|
||||
_tuplebroadcast_maxtuple(::Any, ::Type{Tuple}, A, B) = B
|
||||
_tuplebroadcast_maxtuple(::Type{Tuple}, ::Type{Tuple}, A, B::Tuple{Any}) = A
|
||||
_tuplebroadcast_maxtuple(::Type{Tuple}, ::Type{Tuple}, A::Tuple{Any}, B) = B
|
||||
_tuplebroadcast_maxtuple(::Type{Tuple}, ::Type{Tuple}, A::Tuple{Any}, ::Tuple{Any}) = A
|
||||
_tuplebroadcast_maxtuple(::Type{Tuple}, ::Type{Tuple}, A, B) =
|
||||
throw(DimensionMismatch("tuples could not be broadcast to a common size"))
|
||||
tuplebroadcast_getargs(::Tuple{}, k) = ()
|
||||
@inline tuplebroadcast_getargs(As, k) =
|
||||
(_broadcast_getindex(first(As), k), tuplebroadcast_getargs(tail(As), k)...)
|
||||
|
||||
"""
|
||||
broadcast(f, As...)
|
||||
|
||||
Broadcasts the arrays, tuples, `Ref`s, nullables, and/or scalars `As` to a
|
||||
container of the appropriate type and dimensions. In this context, anything
|
||||
that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s), `Tuple`,
|
||||
or `Nullable` is considered a scalar. The resulting container is established 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 the arguments contain at least one array or `Ref`, it returns an array
|
||||
(expanding singleton dimensions), and treats `Ref`s as 0-dimensional arrays,
|
||||
and tuples as 1-dimensional arrays.
|
||||
|
||||
The following additional rule applies to `Nullable` arguments: If there is at
|
||||
least one `Nullable`, and all the arguments are scalars or `Nullable`, it
|
||||
returns a `Nullable` treating `Nullable`s as "containers".
|
||||
|
||||
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.
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1, 2, 3, 4, 5]
|
||||
5-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
|
||||
julia> B = [1 2; 3 4; 5 6; 7 8; 9 10]
|
||||
5×2 Array{Int64,2}:
|
||||
1 2
|
||||
3 4
|
||||
5 6
|
||||
7 8
|
||||
9 10
|
||||
|
||||
julia> broadcast(+, A, B)
|
||||
5×2 Array{Int64,2}:
|
||||
2 3
|
||||
5 6
|
||||
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), Ref(1))
|
||||
2-element Array{Float64,1}:
|
||||
2.0
|
||||
0.0
|
||||
|
||||
julia> (+).([[0,2], [1,3]], Ref{Vector{Int}}([1,-1]))
|
||||
2-element Array{Array{Int64,1},1}:
|
||||
[1, 1]
|
||||
[2, 2]
|
||||
|
||||
julia> string.(("one","two","three","four"), ": ", 1:4)
|
||||
4-element Array{String,1}:
|
||||
"one: 1"
|
||||
"two: 2"
|
||||
"three: 3"
|
||||
"four: 4"
|
||||
|
||||
julia> Nullable("X") .* "Y"
|
||||
Nullable{String}("XY")
|
||||
|
||||
julia> broadcast(/, 1.0, Nullable(2.0))
|
||||
Nullable{Float64}(0.5)
|
||||
|
||||
julia> (1 + im) ./ Nullable{Int}()
|
||||
Nullable{Complex{Float64}}()
|
||||
```
|
||||
"""
|
||||
@inline broadcast(f, A, Bs...) = broadcast_c(f, containertype(A, Bs...), A, Bs...)
|
||||
|
||||
"""
|
||||
broadcast_getindex(A, inds...)
|
||||
|
||||
Broadcasts the `inds` arrays to a common size like [`broadcast`](@ref)
|
||||
and returns an array of the results `A[ks...]`,
|
||||
where `ks` goes over the positions in the broadcast result `A`.
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1, 2, 3, 4, 5]
|
||||
5-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
|
||||
julia> B = [1 2; 3 4; 5 6; 7 8; 9 10]
|
||||
5×2 Array{Int64,2}:
|
||||
1 2
|
||||
3 4
|
||||
5 6
|
||||
7 8
|
||||
9 10
|
||||
|
||||
julia> C = broadcast(+,A,B)
|
||||
5×2 Array{Int64,2}:
|
||||
2 3
|
||||
5 6
|
||||
8 9
|
||||
11 12
|
||||
14 15
|
||||
|
||||
julia> broadcast_getindex(C,[1,2,10])
|
||||
3-element Array{Int64,1}:
|
||||
2
|
||||
5
|
||||
15
|
||||
```
|
||||
"""
|
||||
broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(similar(Array{eltype(src)}, broadcast_indices(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
|
||||
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
|
||||
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k)
|
||||
@inbounds (@nref $N dest i) = (@nref $N src J)
|
||||
end
|
||||
dest
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
broadcast_setindex!(A, X, inds...)
|
||||
|
||||
Broadcasts the `X` and `inds` arrays to a common size and stores the value from each
|
||||
position in `X` at the indices in `A` given by the same positions in `inds`.
|
||||
"""
|
||||
@generated function broadcast_setindex!(A::AbstractArray, x, I::AbstractArray...)
|
||||
N = length(I)
|
||||
Isplat = Expr[:(I[$d]) for d = 1:N]
|
||||
quote
|
||||
@nexprs $N d->(I_d = I[d])
|
||||
checkbounds(A, $(Isplat...))
|
||||
shape = broadcast_indices($(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)
|
||||
xA = convert(eltype(A), x)
|
||||
@nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = Ibcast_d_k ? 1 : i_d)) begin
|
||||
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k)
|
||||
@inbounds (@nref $N A J) = xA
|
||||
end
|
||||
else
|
||||
X = x
|
||||
@nexprs $N d->(shapelen_d = length(shape_d))
|
||||
@ncall $N Base.setindex_shape_check X shapelen
|
||||
Xstate = start(X)
|
||||
@inbounds @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = Ibcast_d_k ? 1 : i_d)) begin
|
||||
@nexprs $N k->(J_k = @nref $N I_k d->j_d_k)
|
||||
x_el, Xstate = next(X, Xstate)
|
||||
(@nref $N A J) = x_el
|
||||
end
|
||||
end
|
||||
A
|
||||
end
|
||||
end
|
||||
|
||||
############################################################
|
||||
|
||||
# x[...] .= f.(y...) ---> broadcast!(f, dotview(x, ...), y...).
|
||||
# The dotview function defaults to getindex, but we override it in
|
||||
# a few cases to get the expected in-place behavior without affecting
|
||||
# explicit calls to view. (All of this can go away if slices
|
||||
# are changed to generate views by default.)
|
||||
|
||||
Base.@propagate_inbounds dotview(args...) = getindex(args...)
|
||||
Base.@propagate_inbounds dotview(A::AbstractArray, args...) = view(A, args...)
|
||||
Base.@propagate_inbounds dotview(A::AbstractArray{<:AbstractArray}, args::Integer...) = getindex(A, args...)
|
||||
|
||||
|
||||
############################################################
|
||||
# The parser turns @. into a call to the __dot__ macro,
|
||||
# which converts all function calls and assignments into
|
||||
# broadcasting "dot" calls/assignments:
|
||||
|
||||
dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view)
|
||||
dottable(x::Symbol) = !isoperator(x) || first(string(x)) != '.' || x == :.. # don't add dots to dot operators
|
||||
dottable(x::Expr) = x.head != :$
|
||||
undot(x) = x
|
||||
function undot(x::Expr)
|
||||
if x.head == :.=
|
||||
Expr(:(=), x.args...)
|
||||
elseif x.head == :block # occurs in for x=..., y=...
|
||||
Expr(:block, map(undot, x.args)...)
|
||||
else
|
||||
x
|
||||
end
|
||||
end
|
||||
__dot__(x) = x
|
||||
function __dot__(x::Expr)
|
||||
dotargs = map(__dot__, x.args)
|
||||
if x.head == :call && dottable(x.args[1])
|
||||
Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...))
|
||||
elseif x.head == :$
|
||||
x.args[1]
|
||||
elseif x.head == :let # don't add dots to "let x=... assignments
|
||||
Expr(:let, dotargs[1], map(undot, dotargs[2:end])...)
|
||||
elseif x.head == :for # don't add dots to for x=... assignments
|
||||
Expr(:for, undot(dotargs[1]), dotargs[2])
|
||||
elseif (x.head == :(=) || x.head == :function || x.head == :macro) &&
|
||||
Meta.isexpr(x.args[1], :call) # function or macro definition
|
||||
Expr(x.head, x.args[1], dotargs[2])
|
||||
else
|
||||
head = string(x.head)
|
||||
if last(head) == '=' && first(head) != '.'
|
||||
Expr(Symbol('.',head), dotargs...)
|
||||
else
|
||||
Expr(x.head, dotargs...)
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
@. expr
|
||||
|
||||
Convert every function call or operator in `expr` into a "dot call"
|
||||
(e.g. convert `f(x)` to `f.(x)`), and convert every assignment in `expr`
|
||||
to a "dot assignment" (e.g. convert `+=` to `.+=`).
|
||||
|
||||
If you want to *avoid* adding dots for selected function calls in
|
||||
`expr`, splice those function calls in with `\$`. For example,
|
||||
`@. sqrt(abs(\$sort(x)))` is equivalent to `sqrt.(abs.(sort(x)))`
|
||||
(no dot for `sort`).
|
||||
|
||||
(`@.` is equivalent to a call to `@__dot__`.)
|
||||
|
||||
```jldoctest
|
||||
julia> x = 1.0:3.0; y = similar(x);
|
||||
|
||||
julia> @. y = x + 3 * sin(x)
|
||||
3-element Array{Float64,1}:
|
||||
3.52441
|
||||
4.72789
|
||||
3.42336
|
||||
```
|
||||
"""
|
||||
macro __dot__(x)
|
||||
esc(__dot__(x))
|
||||
end
|
||||
|
||||
end # module
|
||||
Reference in New Issue
Block a user