Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support init keyword in sum/prod/maximum/minimum #36188

Merged
merged 26 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ed1cfc9
Support `init` keyword in `maximum`/`minimum`
timholy May 11, 2020
a9fd8b8
Add `init` keyword argument to `sum` and `prod`
tkf Jun 8, 2020
16b13d2
Merge remote-tracking branch 'origin/teh/reduce_init' into sum-init
tkf Jun 8, 2020
72d01c0
Test sum and prod
tkf Jun 8, 2020
c3e66a5
Add compat annotations to `minimum` and `maximum`
tkf Jun 8, 2020
31feb69
Tweak docstring signature style
tkf Jun 8, 2020
32e0c99
Fix reflection doctest
tkf Jun 8, 2020
b8a686b
Add a NEWS item
tkf Jun 8, 2020
749e2ce
Fix `sum(::AbstractArray{Bool}; dims)`
tkf Jun 8, 2020
bf551a7
Fix a typo
tkf Jun 8, 2020
e1173cb
Fix sum(::AbstractArray{Bool}) optimization path
tkf Jun 8, 2020
791f93c
Apply suggestions from code review
tkf Jun 8, 2020
2b84499
Reflect the same changes to other docstrings
tkf Jun 8, 2020
2122e0e
Mention how `nothing` is used in the test
tkf Jun 8, 2020
6918213
Demonstrate `init` for `sum` and `prod`
tkf Jun 8, 2020
ea2cc8b
Reflect the same changes to other docstrings (2)
tkf Jun 8, 2020
32d7f93
Use `function noncallable end`
tkf Jun 8, 2020
3e7b09c
Use init=Inf for minimum docstring
tkf Jun 8, 2020
0791d49
Apply suggestions from code review
tkf Jun 9, 2020
923bbd8
Revert: maximum(length, []; init=-1)
tkf Jun 9, 2020
0050d4f
Use typemax(Int64) for minimum(length, []; init=typemax(Int64))
tkf Jun 9, 2020
6951b2c
Yet another example: minimum(tanh, Real[]; init=1.0)
tkf Jun 9, 2020
04c0cc9
Yet another example: maximum(tanh, Real[]; init=-1.0)
tkf Jun 9, 2020
5d952ec
Apply suggestions from code review
tkf Jun 10, 2020
42a0155
A short note on the output bound of tanh
tkf Jun 10, 2020
254a7cb
Use sin instead of tanh
tkf Jun 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Standard library changes
* The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]).
* `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]).
* All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]).
* `sum`, `prod`, `maximum`, and `minimum` now support `init` keyword argument ([#36188], [#35839]).

#### LinearAlgebra
* New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]).
Expand Down
139 changes: 117 additions & 22 deletions base/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function mapfoldl_impl(f::F, op::OP, nt, itr) where {F,OP}
end

function foldl_impl(op::OP, nt, itr) where {OP}
v = _foldl_impl(op, get(nt, :init, _InitialValue()), itr)
v = _foldl_impl(op, nt, itr)
v isa _InitialValue && return reduce_empty_iter(op, itr)
return v
end
Expand Down Expand Up @@ -157,7 +157,7 @@ Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl`
If provided, the keyword argument `init` will be used exactly once. In general, it will be
necessary to provide `init` to work with empty collections.
"""
mapfoldl(f, op, itr; kw...) = mapfoldl_impl(f, op, kw.data, itr)
mapfoldl(f, op, itr; init=_InitialValue()) = mapfoldl_impl(f, op, init, itr)

"""
foldl(op, itr; [init])
Expand Down Expand Up @@ -200,7 +200,7 @@ Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr
provided, the keyword argument `init` will be used exactly once. In general, it will be
necessary to provide `init` to work with empty collections.
"""
mapfoldr(f, op, itr; kw...) = mapfoldr_impl(f, op, kw.data, itr)
mapfoldr(f, op, itr; init=_InitialValue()) = mapfoldr_impl(f, op, init, itr)


"""
Expand Down Expand Up @@ -462,14 +462,21 @@ reduce(op, a::Number) = a # Do we want this?
## sum

"""
sum(f, itr)
sum(f, itr; [init])

Sum the results of calling function `f` on each element of `itr`.

The return type is `Int` for signed integers of less than system word size, and
`UInt` for unsigned integers of less than system word size. For all other
arguments, a common return type is found to which all arguments are promoted.

The value returned for empty `itr` can be specified by `init`. It must be
the additive identity (i.e. zero) as it is unspecified whether `init` is used
tkf marked this conversation as resolved.
Show resolved Hide resolved
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> sum(abs2, [2; 3; 4])
Expand All @@ -491,60 +498,88 @@ In the former case, the integers are widened to system word size and therefore
the result is 128. In the latter case, no such widening happens and integer
overflow results in -128.
"""
sum(f, a) = mapreduce(f, add_sum, a)
sum(f, a; kw...) = mapreduce(f, add_sum, a; kw...)
nalimilan marked this conversation as resolved.
Show resolved Hide resolved

"""
sum(itr)
sum(itr; [init])

Returns the sum of all elements in a collection.

The return type is `Int` for signed integers of less than system word size, and
`UInt` for unsigned integers of less than system word size. For all other
arguments, a common return type is found to which all arguments are promoted.

The value returned for empty `itr` can be specified by `init`. It must be
the additive identity (i.e. zero) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> sum(1:20)
210
tkf marked this conversation as resolved.
Show resolved Hide resolved

julia> sum(1:20; init = 0.0)
210.0
```
"""
sum(a) = sum(identity, a)
sum(a::AbstractArray{Bool}) = count(a)
sum(a; kw...) = sum(identity, a; kw...)
sum(a::AbstractArray{Bool}; kw...) =
kw.data === NamedTuple() ? count(a) : reduce(add_sum, a; kw...)

## prod
"""
prod(f, itr)
prod(f, itr; [init])

Returns the product of `f` applied to each element of `itr`.

The return type is `Int` for signed integers of less than system word size, and
`UInt` for unsigned integers of less than system word size. For all other
arguments, a common return type is found to which all arguments are promoted.

The value returned for empty `itr` can be specified by `init`. It must be the
multiplicative identity (i.e. one) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> prod(abs2, [2; 3; 4])
576
tkf marked this conversation as resolved.
Show resolved Hide resolved
```
"""
prod(f, a) = mapreduce(f, mul_prod, a)
prod(f, a; kw...) = mapreduce(f, mul_prod, a; kw...)

"""
prod(itr)
prod(itr; [init])

Returns the product of all elements of a collection.

The return type is `Int` for signed integers of less than system word size, and
`UInt` for unsigned integers of less than system word size. For all other
arguments, a common return type is found to which all arguments are promoted.

The value returned for empty `itr` can be specified by `init`. It must be the
multiplicative identity (i.e. one) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> prod(1:20)
2432902008176640000
julia> prod(1:5)
120

julia> prod(1:5; init = 1.0)
120.0
```
"""
prod(a) = mapreduce(identity, mul_prod, a)
prod(a; kw...) = mapreduce(identity, mul_prod, a; kw...)

## maximum & minimum
_fast(::typeof(min),x,y) = min(x,y)
Expand Down Expand Up @@ -610,62 +645,122 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)},
end

"""
maximum(f, itr)
maximum(f, itr; [init])

Returns the largest result of calling function `f` on each element of `itr`.

The value returned for empty `itr` can be specified by `init`. It must be
a neutral element for `max` (i.e. which is less than or equal to any
other element) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> maximum(length, ["Julion", "Julia", "Jule"])
6

julia> maximum(length, []; init=-1)
-1
tkf marked this conversation as resolved.
Show resolved Hide resolved

julia> maximum(sin, Real[]; init=-1.0) # good, since output of sin is >= -1
-1.0
```
"""
maximum(f, a) = mapreduce(f, max, a)
maximum(f, a; kw...) = mapreduce(f, max, a; kw...)

"""
minimum(f, itr)
minimum(f, itr; [init])

Returns the smallest result of calling function `f` on each element of `itr`.

The value returned for empty `itr` can be specified by `init`. It must be
a neutral element for `min` (i.e. which is greater than or equal to any
other element) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> minimum(length, ["Julion", "Julia", "Jule"])
4

julia> minimum(length, []; init=typemax(Int64))
9223372036854775807

julia> minimum(sin, Real[]; init=1.0) # good, since output of sin is <= 1
1.0
```
"""
minimum(f, a) = mapreduce(f, min, a)
minimum(f, a; kw...) = mapreduce(f, min, a; kw...)

"""
maximum(itr)
maximum(itr; [init])

Returns the largest element in a collection.

The value returned for empty `itr` can be specified by `init`. It must be
a neutral element for `max` (i.e. which is less than or equal to any
other element) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> maximum(-20.5:10)
9.5

julia> maximum([1,2,3])
3

julia> maximum(())
ERROR: ArgumentError: reducing over an empty collection is not allowed
Stacktrace:
[...]

julia> maximum((); init=-Inf)
-Inf
```
"""
maximum(a) = mapreduce(identity, max, a)
maximum(a; kw...) = mapreduce(identity, max, a; kw...)

"""
minimum(itr)
minimum(itr; [init])

Returns the smallest element in a collection.

The value returned for empty `itr` can be specified by `init`. It must be
a neutral element for `min` (i.e. which is greater than or equal to any
other element) as it is unspecified whether `init` is used
for non-empty collections.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.

# Examples
```jldoctest
julia> minimum(-20.5:10)
-20.5

julia> minimum([1,2,3])
1

julia> minimum([])
ERROR: ArgumentError: reducing over an empty collection is not allowed
Stacktrace:
[...]

julia> minimum([]; init=Inf)
Inf
```
"""
minimum(a) = mapreduce(identity, min, a)
minimum(a; kw...) = mapreduce(identity, min, a; kw...)

## all & any

Expand Down
28 changes: 14 additions & 14 deletions base/reducedim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,21 +307,21 @@ julia> mapreduce(isodd, |, a, dims=1)
1 1 1 1
```
"""
mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, kw...) =
_mapreduce_dim(f, op, kw.data, A, dims)
mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) =
_mapreduce_dim(f, op, init, A, dims)
mapreduce(f, op, A::AbstractArrayOrBroadcasted...; kw...) =
reduce(op, map(f, A...); kw...)

_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, ::Colon) =
mapfoldl(f, op, A; nt...)
_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, ::Colon) =
mapfoldl_impl(f, op, nt, A)

_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, ::Colon) =
_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, ::Colon) =
_mapreduce(f, op, IndexStyle(A), A)

_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, dims) =
mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A)
_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, dims) =
mapreducedim!(f, op, reducedim_initarray(A, dims, nt), A)

_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, dims) =
_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, dims) =
mapreducedim!(f, op, reducedim_init(f, op, A, dims), A)

"""
Expand Down Expand Up @@ -717,12 +717,12 @@ for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod,
(:maximum, :_maximum, :max), (:minimum, :_minimum, :min)]
@eval begin
# User-facing methods with keyword arguments
@inline ($fname)(a::AbstractArray; dims=:) = ($_fname)(a, dims)
@inline ($fname)(f, a::AbstractArray; dims=:) = ($_fname)(f, a, dims)
@inline ($fname)(a::AbstractArray; dims=:, kw...) = ($_fname)(a, dims; kw...)
@inline ($fname)(f, a::AbstractArray; dims=:, kw...) = ($_fname)(f, a, dims; kw...)

# Underlying implementations using dispatch
($_fname)(a, ::Colon) = ($_fname)(identity, a, :)
($_fname)(f, a, ::Colon) = mapreduce(f, $op, a)
($_fname)(a, ::Colon; kw...) = ($_fname)(identity, a, :; kw...)
($_fname)(f, a, ::Colon; kw...) = mapreduce(f, $op, a; kw...)
end
end

Expand All @@ -743,8 +743,8 @@ for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod),
mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A)
$(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init)

$(_fname)(A, dims) = $(_fname)(identity, A, dims)
$(_fname)(f, A, dims) = mapreduce(f, $(op), A, dims=dims)
$(_fname)(A, dims; kw...) = $(_fname)(identity, A, dims; kw...)
$(_fname)(f, A, dims; kw...) = mapreduce(f, $(op), A; dims=dims, kw...)
end
end

Expand Down
6 changes: 4 additions & 2 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1218,10 +1218,12 @@ See also [`applicable`](@ref).
julia> hasmethod(length, Tuple{Array})
true

julia> hasmethod(sum, Tuple{Function, Array}, (:dims,))
julia> f(; oranges=0) = oranges;

julia> hasmethod(f, Tuple{}, (:oranges,))
true

julia> hasmethod(sum, Tuple{Function, Array}, (:apples, :bananas))
julia> hasmethod(f, Tuple{}, (:apples, :bananas))
false

julia> g(; xs...) = 4;
Expand Down
Loading