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 maximum/minimum #35839

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 41 additions & 11 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 @@ -606,35 +606,47 @@ 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`.
If provided, `init` must be a neutral element for `max` that will be returned
for empty collections.

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

julia> maximum(length, []; init=-1)
-1
```
"""
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`.
If provided, `init` must be a neutral element for `min` that will be returned
for empty collections.

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

julia> minimum(length, []; init=-1)
-1
```
"""
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.
If provided, `init` must be a neutral element for `max` that will be returned
for empty collections.

# Examples
```jldoctest
Expand All @@ -643,14 +655,24 @@ julia> maximum(-20.5:10)

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

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

julia> maximum((); init=-1)
-1
```
"""
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.
If provided, `init` must be a neutral element for `min` that will be returned
for empty collections.

# Examples
```jldoctest
Expand All @@ -659,9 +681,17 @@ julia> minimum(-20.5:10)

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

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

julia> minimum([]; init=-1)
-1
```
"""
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) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 That's what we did in JuliaArrays/StaticArrays.jl#750

_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...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use init as a positional argument for $_fname. I guess that would shave off some compiler work (and avoid it to get in the way of the inliner)?

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
3 changes: 3 additions & 0 deletions test/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr)
@test_throws ArgumentError maximum(Int[])
@test_throws ArgumentError minimum(Int[])

@test maximum(Int[]; init=-1) == -1
@test minimum(Int[]; init=-1) == -1

@test maximum(5) == 5
@test minimum(5) == 5
@test extrema(5) == (5, 5)
Expand Down
5 changes: 5 additions & 0 deletions test/reducedim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ safe_minabs(A::Array{T}, region) where {T} = safe_mapslices(minimum, abs.(A), re
@test @inferred(count(!, Breduc, dims=region)) ≈ safe_count(.!Breduc, region)
end

# Combining dims and init
A = Array{Int}(undef, 0, 3)
@test_throws ArgumentError maximum(A; dims=1)
@test maximum(A; dims=1, init=-1) == reshape([-1,-1,-1], 1, 3)

# Test reduction along first dimension; this is special-cased for
# size(A, 1) >= 16
Breduc = rand(64, 3)
Expand Down