3
3
module Broadcast
4
4
5
5
using Base. Cartesian
6
- using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape,
6
+ using Base: promote_eltype_op, _default_eltype, linearindices, tail, OneTo, to_shape,
7
7
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache
8
8
import Base: .+ , .- , .* , ./ , .\ , .// , .== , .< , .!= , .<= , .÷ , .% , .<< , .>> , .^
9
9
import Base: broadcast
@@ -16,7 +16,7 @@ export broadcast_getindex, broadcast_setindex!
16
16
broadcast (f) = f ()
17
17
@inline broadcast (f, x:: Number... ) = f (x... )
18
18
@inline broadcast {N} (f, t:: NTuple{N} , ts:: Vararg{NTuple{N}} ) = map (f, t, ts... )
19
- @inline broadcast (f, As:: AbstractArray... ) = broadcast_t (f, promote_eltype_op (f, As ... ) , As... )
19
+ @inline broadcast (f, As:: AbstractArray... ) = broadcast_c (f, Array , As... )
20
20
21
21
# special cases for "X .= ..." (broadcast!) assignments
22
22
broadcast! (:: typeof (identity), X:: AbstractArray , x:: Number ) = fill! (X, x)
@@ -127,14 +127,14 @@ Base.@propagate_inbounds _broadcast_getindex(::Any, A, I) = A[I]
127
127
# # Broadcasting core
128
128
# nargs encodes the number of As arguments (which matches the number
129
129
# of keeps). The first two type parameters are to ensure specialization.
130
- @generated function _broadcast! {K,ID,AT,nargs} (f, B:: AbstractArray , keeps:: K , Idefaults:: ID , As:: AT , :: Type{Val{nargs}} )
130
+ @generated function _broadcast! {K,ID,AT,nargs} (f, B:: AbstractArray , keeps:: K , Idefaults:: ID , As:: AT , :: Type{Val{nargs}} , iter )
131
131
quote
132
132
$ (Expr (:meta , :noinline ))
133
133
# destructure the keeps and As tuples
134
134
@nexprs $ nargs i-> (A_i = As[i])
135
135
@nexprs $ nargs i-> (keep_i = keeps[i])
136
136
@nexprs $ nargs i-> (Idefault_i = Idefaults[i])
137
- @simd for I in CartesianRange ( indices (B))
137
+ @simd for I in iter
138
138
# reverse-broadcast the indices
139
139
@nexprs $ nargs i-> (I_i = newindex (I, keep_i, Idefault_i))
140
140
# extract array values
148
148
149
149
# For BitArray outputs, we cache the result in a "small" Vector{Bool},
150
150
# and then copy in chunks into the output
151
- @generated function _broadcast! {K,ID,AT,nargs} (f, B:: BitArray , keeps:: K , Idefaults:: ID , As:: AT , :: Type{Val{nargs}} )
151
+ @generated function _broadcast! {K,ID,AT,nargs} (f, B:: BitArray , keeps:: K , Idefaults:: ID , As:: AT , :: Type{Val{nargs}} , iter )
152
152
quote
153
153
$ (Expr (:meta , :noinline ))
154
154
# destructure the keeps and As tuples
159
159
Bc = B. chunks
160
160
ind = 1
161
161
cind = 1
162
- @simd for I in CartesianRange ( indices (B))
162
+ @simd for I in iter
163
163
# reverse-broadcast the indices
164
164
@nexprs $ nargs i-> (I_i = newindex (I, keep_i, Idefault_i))
165
165
# extract array values
@@ -193,12 +193,12 @@ as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`.
193
193
shape = indices (B)
194
194
check_broadcast_indices (shape, As... )
195
195
keeps, Idefaults = map_newindexer (shape, As)
196
- _broadcast! (f, B, keeps, Idefaults, As, Val{nargs})
197
- B
196
+ iter = CartesianRange (shape)
197
+ _broadcast! (f, B, keeps, Idefaults, As, Val{nargs}, iter)
198
+ return B
198
199
end
199
200
200
201
# broadcast with computed element type
201
-
202
202
@generated function _broadcast! {K,ID,AT,nargs} (f, B:: AbstractArray , keeps:: K , Idefaults:: ID , As:: AT , :: Type{Val{nargs}} , iter, st, count)
203
203
quote
204
204
$ (Expr (:meta , :noinline ))
233
233
end
234
234
end
235
235
236
- function broadcast_t (f, :: Type{Any} , As... )
237
- shape = broadcast_indices (As... )
238
- iter = CartesianRange (shape)
239
- if isempty (iter)
240
- return similar (Array{Any}, shape)
241
- end
236
+ # broadcast methods that dispatch on the type found by inference
237
+ function broadcast_t (f, :: Type{Any} , shape, iter, As... )
242
238
nargs = length (As)
243
239
keeps, Idefaults = map_newindexer (shape, As)
244
240
st = start (iter)
@@ -248,17 +244,46 @@ function broadcast_t(f, ::Type{Any}, As...)
248
244
B[I] = val
249
245
return _broadcast! (f, B, keeps, Idefaults, As, Val{nargs}, iter, st, 1 )
250
246
end
247
+ @inline function broadcast_t (f, T, shape, iter, As... )
248
+ B = similar (Array{T}, shape)
249
+ nargs = length (As)
250
+ keeps, Idefaults = map_newindexer (shape, As)
251
+ _broadcast! (f, B, keeps, Idefaults, As, Val{nargs}, iter)
252
+ return B
253
+ end
251
254
252
- @inline broadcast_t (f, T, As... ) = broadcast! (f, similar (Array{T}, broadcast_indices (As... )), As... )
253
-
255
+ # broadcast method that uses inference to find the type, but preserves abstract
256
+ # container types when possible (used by binary elementwise operators)
257
+ @inline broadcast_elwise_op (f, As... ) =
258
+ broadcast! (f, similar (Array{promote_eltype_op (f, As... )}, broadcast_indices (As... )), As... )
259
+
260
+ ftype (f, A) = typeof (a -> f (a))
261
+ ftype (f, A... ) = typeof (a -> f (a... ))
262
+ ftype (T:: DataType , A) = Type{T}
263
+ ftype (T:: DataType , A... ) = Type{T}
264
+ ziptype (A) = Tuple{eltype (A)}
265
+ ziptype (A, B) = Iterators. Zip2{Tuple{eltype (A)}, Tuple{eltype (B)}}
266
+ @inline ziptype (A, B, C, D... ) = Iterators. Zip{Tuple{eltype (A)}, ziptype (B, C, D... )}
267
+
268
+ # broadcast methods that dispatch on the type of the final container
269
+ @inline function broadcast_c (f, :: Type{Array} , As... )
270
+ T = _default_eltype (Base. Generator{ziptype (As... ), ftype (f, As... )})
271
+ shape = broadcast_indices (As... )
272
+ iter = CartesianRange (shape)
273
+ if isleaftype (T)
274
+ return broadcast_t (f, T, shape, iter, As... )
275
+ end
276
+ if isempty (iter)
277
+ return similar (Array{T}, shape)
278
+ end
279
+ return broadcast_t (f, Any, shape, iter, As... )
280
+ end
254
281
function broadcast_c (f, :: Type{Tuple} , As... )
255
282
shape = broadcast_indices (As... )
256
- check_broadcast_indices (shape, As... )
257
283
n = length (shape[1 ])
258
284
return ntuple (k-> f ((_broadcast_getindex (A, k) for A in As). .. ), n)
259
285
end
260
286
@inline broadcast_c (f, :: Type{Any} , a... ) = f (a... )
261
- @inline broadcast_c (f, :: Type{Array} , As... ) = broadcast_t (f, promote_eltype_op (f, As... ), As... )
262
287
263
288
"""
264
289
broadcast(f, As...)
@@ -441,10 +466,10 @@ end
441
466
# # elementwise operators ##
442
467
443
468
for op in (:÷ , :% , :<< , :>> , :- , :/ , :\ , :// , :^ )
444
- @eval $ (Symbol (:., op))(A:: AbstractArray , B:: AbstractArray ) = broadcast ($ op, A, B)
469
+ @eval $ (Symbol (:., op))(A:: AbstractArray , B:: AbstractArray ) = broadcast_elwise_op ($ op, A, B)
445
470
end
446
- .+ (As:: AbstractArray... ) = broadcast (+ , As... )
447
- .* (As:: AbstractArray... ) = broadcast (* , As... )
471
+ .+ (As:: AbstractArray... ) = broadcast_elwise_op (+ , As... )
472
+ .* (As:: AbstractArray... ) = broadcast_elwise_op (* , As... )
448
473
449
474
# ## element-wise comparison operators returning BitArray ##
450
475
0 commit comments