5
5
# ##### Generic (map)reduce functions ######
6
6
7
7
if Int === Int32
8
- const SmallSigned = Union{Int8,Int16}
9
- const SmallUnsigned = Union{UInt8,UInt16}
8
+ const SmallSigned = Union{Int8,Int16}
9
+ const SmallUnsigned = Union{UInt8,UInt16}
10
10
else
11
- const SmallSigned = Union{Int8,Int16,Int32}
12
- const SmallUnsigned = Union{UInt8,UInt16,UInt32}
11
+ const SmallSigned = Union{Int8,Int16,Int32}
12
+ const SmallUnsigned = Union{UInt8,UInt16,UInt32}
13
13
end
14
14
15
- const CommonReduceResult = Union{UInt64,UInt128,Int64,Int128,Float16,Float32,Float64}
16
- const WidenReduceResult = Union{SmallSigned, SmallUnsigned}
17
-
18
- promote_sys_size {T} (:: Type{T} ) = T
19
- promote_sys_size {T<:SmallSigned} (:: Type{T} ) = Int
20
- promote_sys_size {T<:SmallUnsigned} (:: Type{T} ) = UInt
21
- # r_promote_type: promote T to the type of reduce(op, ::Array{T})
22
- # (some "extra" methods are required here to avoid ambiguity warnings)
23
- r_promote_type (op, :: Type{T} ) where {T} = T
24
- r_promote_type (op, :: Type{T} ) where {T<: WidenReduceResult } = promote_sys_size (T)
25
- r_promote_type (:: typeof (+ ), :: Type{T} ) where {T<: WidenReduceResult } = promote_sys_size (T)
26
- r_promote_type (:: typeof (* ), :: Type{T} ) where {T<: WidenReduceResult } = promote_sys_size (T)
27
- r_promote_type (:: typeof (+ ), :: Type{T} ) where {T<: Number } = typeof (zero (T)+ zero (T))
28
- r_promote_type (:: typeof (* ), :: Type{T} ) where {T<: Number } = typeof (one (T)* one (T))
29
- r_promote_type (:: typeof (scalarmax), :: Type{T} ) where {T<: WidenReduceResult } = T
30
- r_promote_type (:: typeof (scalarmin), :: Type{T} ) where {T<: WidenReduceResult } = T
31
- r_promote_type (:: typeof (max), :: Type{T} ) where {T<: WidenReduceResult } = T
32
- r_promote_type (:: typeof (min), :: Type{T} ) where {T<: WidenReduceResult } = T
33
-
34
- # r_promote: promote x to the type of reduce(op, [x])
35
- r_promote (op, x:: T ) where {T} = convert (r_promote_type (op, T), x)
15
+ # Certain reductions like sum and prod may wish to promote the items being
16
+ # reduced over to an appropriate size.
17
+ promote_sys_size (x) = x
18
+ promote_sys_size (x:: Union{Bool, SmallSigned} ) = Int (x)
19
+ promote_sys_size (x:: SmallUnsigned ) = UInt (x)
36
20
37
21
# # foldl && mapfoldl
38
22
39
23
@noinline function mapfoldl_impl (f, op, v0, itr, i)
40
24
# Unroll the while loop once; if v0 is known, the call to op may
41
25
# be evaluated at compile time
42
26
if done (itr, i)
43
- return r_promote (op, v0)
27
+ return v0
44
28
else
45
29
(x, i) = next (itr, i)
46
- v = op (r_promote (op, v0) , f (x))
30
+ v = op (v0 , f (x))
47
31
while ! done (itr, i)
48
32
@inbounds (x, i) = next (itr, i)
49
33
v = op (v, f (x))
@@ -71,7 +55,7 @@ In general, this cannot be used with empty collections (see [`reduce(op, itr)`](
71
55
function mapfoldl (f, op, itr)
72
56
i = start (itr)
73
57
if done (itr, i)
74
- return Base. mr_empty_iter (f, op, itr, iteratoreltype (itr))
58
+ return Base. mapreduce_empty_iter (f, op, itr, iteratoreltype (itr))
75
59
end
76
60
(x, i) = next (itr, i)
77
61
v0 = f (x)
@@ -110,10 +94,10 @@ function mapfoldr_impl(f, op, v0, itr, i::Integer)
110
94
# Unroll the while loop once; if v0 is known, the call to op may
111
95
# be evaluated at compile time
112
96
if isempty (itr) || i == 0
113
- return r_promote (op, v0)
97
+ return v0
114
98
else
115
99
x = itr[i]
116
- v = op (f (x), r_promote (op, v0) )
100
+ v = op (f (x), v0 )
117
101
while i > 1
118
102
x = itr[i -= 1 ]
119
103
v = op (f (x), v)
@@ -141,7 +125,7 @@ In general, this cannot be used with empty collections (see [`reduce(op, itr)`](
141
125
function mapfoldr (f, op, itr)
142
126
i = endof (itr)
143
127
if isempty (itr)
144
- return Base. mr_empty_iter (f, op, itr, iteratoreltype (itr))
128
+ return Base. mapreduce_empty_iter (f, op, itr, iteratoreltype (itr))
145
129
end
146
130
return mapfoldr_impl (f, op, f (itr[i]), itr, i- 1 )
147
131
end
@@ -184,12 +168,12 @@ foldr(op, itr) = mapfoldr(identity, op, itr)
184
168
@noinline function mapreduce_impl (f, op, A:: AbstractArray , ifirst:: Integer , ilast:: Integer , blksize:: Int )
185
169
if ifirst == ilast
186
170
@inbounds a1 = A[ifirst]
187
- return r_promote (op, f (a1) )
171
+ return f (a1)
188
172
elseif ifirst + blksize > ilast
189
173
# sequential portion
190
174
@inbounds a1 = A[ifirst]
191
175
@inbounds a2 = A[ifirst+ 1 ]
192
- v = op (r_promote (op, f (a1)), r_promote (op, f (a2) ))
176
+ v = op (f (a1), f (a2))
193
177
@simd for i = ifirst + 2 : ilast
194
178
@inbounds ai = A[i]
195
179
v = op (v, f (ai))
@@ -248,39 +232,49 @@ pairwise_blocksize(::typeof(abs2), ::typeof(+)) = 4096
248
232
249
233
# handling empty arrays
250
234
_empty_reduce_error () = throw (ArgumentError (" reducing over an empty collection is not allowed" ))
251
- mr_empty (f, op, T) = _empty_reduce_error ()
252
- # use zero(T)::T to improve type information when zero(T) is not defined
253
- mr_empty (:: typeof (identity), op:: typeof (+ ), T) = r_promote (op, zero (T):: T )
254
- mr_empty (:: typeof (abs), op:: typeof (+ ), T) = r_promote (op, abs (zero (T):: T ))
255
- mr_empty (:: typeof (abs2), op:: typeof (+ ), T) = r_promote (op, abs2 (zero (T):: T ))
256
- mr_empty (:: typeof (identity), op:: typeof (* ), T) = r_promote (op, one (T):: T )
257
- mr_empty (:: typeof (abs), op:: typeof (scalarmax), T) = abs (zero (T):: T )
258
- mr_empty (:: typeof (abs2), op:: typeof (scalarmax), T) = abs2 (zero (T):: T )
259
- mr_empty (:: typeof (abs), op:: typeof (max), T) = mr_empty (abs, scalarmax, T)
260
- mr_empty (:: typeof (abs2), op:: typeof (max), T) = mr_empty (abs2, scalarmax, T)
261
- mr_empty (f, op:: typeof (& ), T) = true
262
- mr_empty (f, op:: typeof (| ), T) = false
263
-
264
- mr_empty_iter (f, op, itr, :: HasEltype ) = mr_empty (f, op, eltype (itr))
265
- mr_empty_iter (f, op:: typeof (& ), itr, :: EltypeUnknown ) = true
266
- mr_empty_iter (f, op:: typeof (| ), itr, :: EltypeUnknown ) = false
267
- mr_empty_iter (f, op, itr, :: EltypeUnknown ) = _empty_reduce_error ()
235
+ reduce_empty (op, T) = _empty_reduce_error ()
236
+ reduce_empty (:: typeof (+ ), T) = zero (T)
237
+ reduce_empty (:: typeof (* ), T) = one (T)
238
+ reduce_empty (:: typeof (& ), :: Type{Bool} ) = true
239
+ reduce_empty (:: typeof (| ), :: Type{Bool} ) = false
240
+
241
+ mapreduce_empty (f, op, T) = _empty_reduce_error ()
242
+ mapreduce_empty (:: typeof (identity), op, T) = reduce_empty (op, T)
243
+ mapreduce_empty (:: typeof (promote_sys_size), op, T) =
244
+ promote_sys_size (mapreduce_empty (identity, op, T))
245
+ mapreduce_empty (:: typeof (abs), :: typeof (+ ), T) = abs (zero (T))
246
+ mapreduce_empty (:: typeof (abs2), :: typeof (+ ), T) = abs2 (zero (T))
247
+ mapreduce_empty (:: typeof (abs), :: Union{typeof(scalarmax), typeof(max)} , T) =
248
+ abs (zero (T))
249
+ mapreduce_empty (:: typeof (abs2), :: Union{typeof(scalarmax), typeof(max)} , T) =
250
+ abs2 (zero (T))
251
+
252
+ # Allow mapreduce_empty to “see through” promote_sys_size
253
+ let ComposedFunction = typename (typeof (identity ∘ identity)). wrapper
254
+ global mapreduce_empty (f:: ComposedFunction{typeof(promote_sys_size)} , op, T) =
255
+ promote_sys_size (mapreduce_empty (f. g, op, T))
256
+ end
257
+
258
+ mapreduce_empty_iter (f, op, itr, :: HasEltype ) = mapreduce_empty (f, op, eltype (itr))
259
+ mapreduce_empty_iter (f, op:: typeof (& ), itr, :: EltypeUnknown ) = true
260
+ mapreduce_empty_iter (f, op:: typeof (| ), itr, :: EltypeUnknown ) = false
261
+ mapreduce_empty_iter (f, op, itr, :: EltypeUnknown ) = _empty_reduce_error ()
268
262
269
263
_mapreduce (f, op, A:: AbstractArray ) = _mapreduce (f, op, IndexStyle (A), A)
270
264
271
265
function _mapreduce (f, op, :: IndexLinear , A:: AbstractArray{T} ) where T
272
266
inds = linearindices (A)
273
267
n = length (inds)
274
268
if n == 0
275
- return mr_empty (f, op, T)
269
+ return mapreduce_empty (f, op, T)
276
270
elseif n == 1
277
271
@inbounds a1 = A[inds[1 ]]
278
- return r_promote (op, f (a1) )
272
+ return f (a1)
279
273
elseif n < 16 # process short array here, avoid mapreduce_impl() compilation
280
274
@inbounds i = inds[1 ]
281
275
@inbounds a1 = A[i]
282
276
@inbounds a2 = A[i+= 1 ]
283
- s = op (r_promote (op, f (a1)), r_promote (op, f (a2) ))
277
+ s = op (f (a1), f (a2))
284
278
while i < last (inds)
285
279
@inbounds Ai = A[i+= 1 ]
286
280
s = op (s, f (Ai))
@@ -303,9 +297,6 @@ Reduce the given collection `itr` with the given binary operator `op`. `v0` must
303
297
neutral element for `op` that will be returned for empty collections. It is unspecified
304
298
whether `v0` is used for non-empty collections.
305
299
306
- The return type is `Int` (`UInt`) for (un)signed integers of less than system word size.
307
- For all other arguments, a common return type is found to which all arguments are promoted.
308
-
309
300
Reductions for certain commonly-used operators may have special implementations, and
310
301
should be used instead: `maximum(itr)`, `minimum(itr)`, `sum(itr)`, `prod(itr)`,
311
302
`any(itr)`, `all(itr)`.
@@ -351,24 +342,47 @@ reduce(op, a::Number) = a
351
342
352
343
Sum the results of calling function `f` on each element of `itr`.
353
344
345
+ The return type is `Int` for signed integers of less than system word size, and
346
+ `UInt` for unsigned integers of less than system word size. For all other
347
+ arguments, a common return type is found to which all arguments are promoted.
348
+
354
349
```jldoctest
355
350
julia> sum(abs2, [2; 3; 4])
356
351
29
357
352
```
353
+
354
+ Note the important difference between `sum(A)` and `reduce(+, A)` for arrays
355
+ with small integer eltype:
356
+
357
+ ```jldoctest
358
+ julia> sum(Int8[100, 28])
359
+ 128
360
+
361
+ julia> reduce(+, Int8[100, 28])
362
+ -128
363
+ ```
364
+
365
+ In the former case, the integers are widened to system word size and therefore
366
+ the result is 128. In the latter case, no such widening happens and integer
367
+ overflow results in -128.
358
368
"""
359
- sum (f:: Callable , a) = mapreduce (f, + , a)
369
+ sum (f:: Callable , a) = mapreduce (promote_sys_size ∘ f, + , a)
360
370
361
371
"""
362
372
sum(itr)
363
373
364
374
Returns the sum of all elements in a collection.
365
375
376
+ The return type is `Int` for signed integers of less than system word size, and
377
+ `UInt` for unsigned integers of less than system word size. For all other
378
+ arguments, a common return type is found to which all arguments are promoted.
379
+
366
380
```jldoctest
367
381
julia> sum(1:20)
368
382
210
369
383
```
370
384
"""
371
- sum (a) = mapreduce (identity , + , a)
385
+ sum (a) = mapreduce (promote_sys_size , + , a)
372
386
sum (a:: AbstractArray{Bool} ) = count (a)
373
387
374
388
@@ -383,7 +397,7 @@ summation algorithm for additional accuracy.
383
397
"""
384
398
function sum_kbn (A)
385
399
T = _default_eltype (typeof (A))
386
- c = r_promote ( + , zero (T):: T )
400
+ c = promote_sys_size ( zero (T):: T )
387
401
i = start (A)
388
402
if done (A, i)
389
403
return c
@@ -409,24 +423,32 @@ end
409
423
410
424
Returns the product of `f` applied to each element of `itr`.
411
425
426
+ The return type is `Int` for signed integers of less than system word size, and
427
+ `UInt` for unsigned integers of less than system word size. For all other
428
+ arguments, a common return type is found to which all arguments are promoted.
429
+
412
430
```jldoctest
413
431
julia> prod(abs2, [2; 3; 4])
414
432
576
415
433
```
416
434
"""
417
- prod (f:: Callable , a) = mapreduce (f, * , a)
435
+ prod (f:: Callable , a) = mapreduce (promote_sys_size ∘ f, * , a)
418
436
419
437
"""
420
438
prod(itr)
421
439
422
440
Returns the product of all elements of a collection.
423
441
442
+ The return type is `Int` for signed integers of less than system word size, and
443
+ `UInt` for unsigned integers of less than system word size. For all other
444
+ arguments, a common return type is found to which all arguments are promoted.
445
+
424
446
```jldoctest
425
447
julia> prod(1:20)
426
448
2432902008176640000
427
449
```
428
450
"""
429
- prod (a) = mapreduce (identity , * , a)
451
+ prod (a) = mapreduce (promote_sys_size , * , a)
430
452
431
453
# # maximum & minimum
432
454
0 commit comments