Skip to content

Commit ce16396

Browse files
N5N3tkf
andcommitted
API clean
and export `extrema!` fix doc Update collections.md drop sparsearray change Co-Authored-By: Takafumi Arakaki <29282+tkf@users.noreply.github.com>
1 parent 28471a6 commit ce16396

File tree

9 files changed

+123
-113
lines changed

9 files changed

+123
-113
lines changed

base/compiler/compiler.jl

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,6 @@ include("operators.jl")
5555
include("pointer.jl")
5656
include("refvalue.jl")
5757

58-
# required for bootstrap
59-
extrema(itr) = extrema(identity, itr)
60-
function extrema(f, itr)
61-
y = iterate(itr)
62-
y === nothing && throw(ArgumentError("collection must be non-empty"))
63-
(v, s) = y
64-
vmin = vmax = f(v)
65-
while true
66-
y = iterate(itr, s)
67-
y === nothing && break
68-
(x, s) = y
69-
fx = f(x)
70-
vmax = max(fx, vmax)
71-
vmin = min(fx, vmin)
72-
end
73-
return (vmin, vmax)
74-
end
75-
7658
# checked arithmetic
7759
const checked_add = +
7860
const checked_sub = -
@@ -149,6 +131,19 @@ include("compiler/abstractinterpretation.jl")
149131
include("compiler/typeinfer.jl")
150132
include("compiler/optimize.jl") # TODO: break this up further + extract utilities
151133

134+
# required for bootstrap
135+
# TODO: find why this is needed and remove it.
136+
function extrema(x::Array)
137+
isempty(x) && throw(ArgumentError("collection must be non-empty"))
138+
vmin = vmax = x[1]
139+
for i in 2:length(x)
140+
xi = x[i]
141+
vmax = max(vmax, xi)
142+
vmin = min(vmin, xi)
143+
end
144+
return vmin, vmax
145+
end
146+
152147
include("compiler/bootstrap.jl")
153148
ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_toplevel)
154149

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ export
385385
eachindex,
386386
eachrow,
387387
eachslice,
388+
extrema!,
388389
extrema,
389390
fill!,
390391
fill,

base/mpfr.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,17 @@ function min(x::BigFloat, y::BigFloat)
706706
return z
707707
end
708708

709+
# We don't want allocations during `extrema`.
710+
# TODO: avoid allocations in `min/max`
711+
function Base._extrema_rf(x::NTuple{2,BigFloat}, y::NTuple{2,BigFloat})
712+
(x1, x2), (y1, y2) = x, y
713+
isnan(x1) && return x
714+
isnan(y1) && return y
715+
z1 = x1 < y1 || signbit(x1) > signbit(y1) ? x1 : y1
716+
z2 = x2 < y2 || signbit(x2) > signbit(y2) ? y2 : x2
717+
z1, z2
718+
end
719+
709720
function modf(x::BigFloat)
710721
zint = BigFloat()
711722
zfloat = BigFloat()

base/multidimensional.jl

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,82 +1702,6 @@ _unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A)
17021702
end
17031703
end
17041704

1705-
"""
1706-
extrema([f,] A::AbstractArray; dims, [init]) -> Array{Tuple}
1707-
1708-
Compute the minimum and maximum elements of `A` over dimensions `dims`.
1709-
If `f` is provided, return the minimum and maximum elements after applying `f` to them.
1710-
1711-
!!! compat "Julia 1.2"
1712-
The `extrema(f, A)` method requires Julia 1.2 or later.
1713-
1714-
# Examples
1715-
```jldoctest
1716-
julia> A = reshape(Vector(1:2:16), (2,2,2))
1717-
2×2×2 Array{Int64, 3}:
1718-
[:, :, 1] =
1719-
1 5
1720-
3 7
1721-
1722-
[:, :, 2] =
1723-
9 13
1724-
11 15
1725-
1726-
julia> extrema(A, dims = (1,2))
1727-
1×1×2 Array{Tuple{Int64, Int64}, 3}:
1728-
[:, :, 1] =
1729-
(1, 7)
1730-
1731-
[:, :, 2] =
1732-
(9, 15)
1733-
```
1734-
"""
1735-
extrema(f::F, A::AbstractArray; dims=:, init=_InitialValue()) where {F} =
1736-
_extrema_dims(f, A, dims, init)
1737-
1738-
_extrema_dims(f::F, A::AbstractArray, ::Colon, init) where {F} =
1739-
mapreduce(_DupY(f), _extrema_rf, A; init = init)
1740-
_extrema_dims(f::F, A::AbstractArray, ::Colon, ::_InitialValue) where {F} =
1741-
mapreduce(_DupY(f), _extrema_rf, A)
1742-
# Note: not passing `init = _InitialValue()` since user-defined
1743-
# `reduce`/`foldl` cannot be aware of `Base._InitialValue` that is an
1744-
# internal implementation detail.
1745-
1746-
_extrema_dims(f::F, A::AbstractArray, dims, init) where {F} =
1747-
mapreduce(_DupY(f), _extrema_rf, A; dims = dims, init = init)
1748-
function _extrema_dims(f::F, A::AbstractArray, dims, ::_InitialValue) where {F}
1749-
sz = size(A)
1750-
for d in dims
1751-
sz = setindex(sz, 1, d)
1752-
end
1753-
T = promote_op(f, eltype(A))
1754-
B = Array{Tuple{T,T}}(undef, sz...)
1755-
return extrema!(f, B, A)
1756-
end
1757-
1758-
@noinline function extrema!(f, B, A)
1759-
require_one_based_indexing(B, A)
1760-
sA = size(A)
1761-
sB = size(B)
1762-
for I in CartesianIndices(sB)
1763-
fAI = f(A[I])
1764-
B[I] = (fAI, fAI)
1765-
end
1766-
Bmax = CartesianIndex(sB)
1767-
@inbounds @simd for I in CartesianIndices(sA)
1768-
J = min(Bmax,I)
1769-
BJ = B[J]
1770-
fAI = f(A[I])
1771-
if fAI < BJ[1]
1772-
B[J] = (fAI, BJ[2])
1773-
elseif fAI > BJ[2]
1774-
B[J] = (BJ[1], fAI)
1775-
end
1776-
end
1777-
return B
1778-
end
1779-
extrema!(B, A) = extrema!(identity, B, A)
1780-
17811705
# Show for pairs() with Cartesian indices. Needs to be here rather than show.jl for bootstrap order
17821706
function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T <: Union{AbstractVector, Tuple}
17831707
print(io, "pairs(::$T)")

base/reduce.jl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -842,17 +842,25 @@ julia> extrema(sin, Real[]; init = (1.0, -1.0)) # good, since -1 ≤ sin(::Real
842842
(1.0, -1.0)
843843
```
844844
"""
845-
extrema(f, itr; kw...) = mapreduce(_DupY(f), _extrema_rf, itr; kw...)
845+
extrema(f, itr; kw...) = mapreduce(ExtremaMap(f), _extrema_rf, itr; kw...)
846846

847847
# Not using closure since `extrema(type, itr)` is a very likely use-case and it's better
848848
# to avoid type-instability (#23618).
849-
struct _DupY{F} <: Function
849+
struct ExtremaMap{F} <: Function
850850
f::F
851851
end
852-
_DupY(f::Type{T}) where {T} = _DupY{Type{T}}(f)
853-
@inline (f::_DupY)(x) = (y = f.f(x); (y, y))
852+
ExtremaMap(::Type{T}) where {T} = ExtremaMap{Type{T}}(T)
853+
@inline (f::ExtremaMap)(x) = (y = f.f(x); (y, y))
854854

855855
@inline _extrema_rf((min1, max1), (min2, max2)) = (min(min1, min2), max(max1, max2))
856+
# optimization for AbstractFloat
857+
function _extrema_rf(x::NTuple{2,T}, y::NTuple{2,T}) where {T<:AbstractFloat}
858+
(x1, x2), (y1, y2) = x, y
859+
anynan = isnan(x1)|isnan(y1)
860+
z1 = ifelse(anynan, x1-y1, ifelse(signbit(x1-y1), x1, y1))
861+
z2 = ifelse(anynan, x1-y1, ifelse(signbit(x2-y2), y2, x2))
862+
z1, z2
863+
end
856864

857865
## findmax, findmin, argmax & argmin
858866

base/reducedim.jl

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,15 @@ end
7777
## initialization
7878
# initarray! is only called by sum!, prod!, etc.
7979
for (Op, initfun) in ((:(typeof(add_sum)), :zero), (:(typeof(mul_prod)), :one))
80-
@eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && fill!(a, $(initfun)(T)); a)
80+
@eval initarray!(a::AbstractArray{T}, ::Any, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && fill!(a, $(initfun)(T)); a)
8181
end
8282

83-
for Op in (:(typeof(max)), :(typeof(min)))
84-
@eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && copyfirst!(a, src); a)
85-
end
83+
# for min/max copyfirst is not correct for initialization, use `mapfirst!` instead
84+
initarray!(a::AbstractArray{T}, f, ::Union{typeof(min),typeof(max),typeof(_extrema_rf)},
85+
init::Bool, src::AbstractArray) where {T} = (init && mapfirst!(f, a, src); a)
8686

8787
for (Op, initval) in ((:(typeof(&)), true), (:(typeof(|)), false))
88-
@eval initarray!(a::AbstractArray, ::$(Op), init::Bool, src::AbstractArray) = (init && fill!(a, $initval); a)
88+
@eval initarray!(a::AbstractArray, ::Any, ::$(Op), init::Bool, src::AbstractArray) = (init && fill!(a, $initval); a)
8989
end
9090

9191
# reducedim_initarray is called by
@@ -435,7 +435,7 @@ julia> count!(<=(2), [1; 1], A)
435435
"""
436436
count!(r::AbstractArray, A::AbstractArrayOrBroadcasted; init::Bool=true) = count!(identity, r, A; init=init)
437437
count!(f, r::AbstractArray, A::AbstractArrayOrBroadcasted; init::Bool=true) =
438-
mapreducedim!(_bool(f), add_sum, initarray!(r, add_sum, init, A), A)
438+
mapreducedim!(_bool(f), add_sum, initarray!(r, f, add_sum, init, A), A)
439439

440440
"""
441441
sum(A::AbstractArray; dims)
@@ -737,6 +737,74 @@ julia> minimum!([1 1], A)
737737
"""
738738
minimum!(r, A)
739739

740+
"""
741+
extrema(A::AbstractArray; dims) -> Array{Tuple}
742+
743+
Compute the minimum and maximum elements of an array over the given dimensions.
744+
745+
See also: [`minimum`](@ref), [`maximum`](@ref), [`extrema!`](@ref).
746+
747+
# Examples
748+
```jldoctest
749+
julia> A = reshape(Vector(1:2:16), (2,2,2))
750+
2×2×2 Array{Int64, 3}:
751+
[:, :, 1] =
752+
1 5
753+
3 7
754+
755+
[:, :, 2] =
756+
9 13
757+
11 15
758+
759+
julia> extrema(A, dims = (1,2))
760+
1×1×2 Array{Tuple{Int64, Int64}, 3}:
761+
[:, :, 1] =
762+
(1, 7)
763+
764+
[:, :, 2] =
765+
(9, 15)
766+
```
767+
"""
768+
extrema(A::AbstractArray; dims)
769+
770+
"""
771+
extrema(f, A::AbstractArray; dims) -> Array{Tuple}
772+
773+
Compute the minimum and maximum of `f` applied to each element in the given dimensions
774+
of `A`.
775+
776+
!!! compat "Julia 1.2"
777+
This method requires Julia 1.2 or later.
778+
"""
779+
extrema(f, A::AbstractArray; dims)
780+
781+
"""
782+
extrema!(r, A)
783+
784+
Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`.
785+
786+
!!! compat "Julia 1.8"
787+
This method requires Julia 1.8 or later.
788+
789+
# Examples
790+
```jldoctest
791+
julia> A = [1 2; 3 4]
792+
2×2 Matrix{Int64}:
793+
1 2
794+
3 4
795+
796+
julia> extrema!([(1, 1); (1, 1)], A)
797+
2-element Vector{Tuple{Int64, Int64}}:
798+
(1, 2)
799+
(3, 4)
800+
801+
julia> extrema!([(1, 1);; (1, 1)], A)
802+
1×2 Matrix{Tuple{Int64, Int64}}:
803+
(1, 3) (2, 4)
804+
```
805+
"""
806+
extrema!(r, A)
807+
740808
"""
741809
all(A; dims)
742810
@@ -883,15 +951,17 @@ julia> any!([1 1], A)
883951
any!(r, A)
884952

885953
for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod, :mul_prod),
886-
(:maximum, :_maximum, :max), (:minimum, :_minimum, :min)]
954+
(:maximum, :_maximum, :max), (:minimum, :_minimum, :min),
955+
(:extrema, :_extrema, :_extrema_rf)]
956+
mapf = fname === :extrema ? :(ExtremaMap(f)) : :f
887957
@eval begin
888958
# User-facing methods with keyword arguments
889959
@inline ($fname)(a::AbstractArray; dims=:, kw...) = ($_fname)(a, dims; kw...)
890960
@inline ($fname)(f, a::AbstractArray; dims=:, kw...) = ($_fname)(f, a, dims; kw...)
891961

892962
# Underlying implementations using dispatch
893963
($_fname)(a, ::Colon; kw...) = ($_fname)(identity, a, :; kw...)
894-
($_fname)(f, a, ::Colon; kw...) = mapreduce(f, $op, a; kw...)
964+
($_fname)(f, a, ::Colon; kw...) = mapreduce($mapf, $op, a; kw...)
895965
end
896966
end
897967

@@ -904,16 +974,18 @@ _all(a, ::Colon) = _all(identity, a, :)
904974

905975
for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod),
906976
(:maximum, :max), (:minimum, :min),
907-
(:all, :&), (:any, :|)]
977+
(:all, :&), (:any, :|),
978+
(:extrema, :_extrema_rf)]
908979
fname! = Symbol(fname, '!')
909980
_fname = Symbol('_', fname)
981+
mapf = fname === :extrema ? :(ExtremaMap(f)) : :f
910982
@eval begin
911983
$(fname!)(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) =
912-
mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A)
984+
mapreducedim!($mapf, $(op), initarray!(r, $mapf, $(op), init, A), A)
913985
$(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init)
914986

915987
$(_fname)(A, dims; kw...) = $(_fname)(identity, A, dims; kw...)
916-
$(_fname)(f, A, dims; kw...) = mapreduce(f, $(op), A; dims=dims, kw...)
988+
$(_fname)(f, A, dims; kw...) = mapreduce($mapf, $(op), A; dims=dims, kw...)
917989
end
918990
end
919991

doc/src/base/collections.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ Base.maximum!
102102
Base.minimum
103103
Base.minimum!
104104
Base.extrema
105+
Base.extrema!
105106
Base.argmax
106107
Base.argmin
107108
Base.findmax

stdlib/SparseArrays/test/higherorderfns.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,8 +709,8 @@ end
709709
@test extrema(f, x) == extrema(f, y)
710710
@test extrema(spzeros(n, n)) == (0.0, 0.0)
711711
@test extrema(spzeros(n)) == (0.0, 0.0)
712-
@test_throws "reducing over an empty" extrema(spzeros(0, 0))
713-
@test_throws "reducing over an empty" extrema(spzeros(0))
712+
@test_throws ArgumentError extrema(spzeros(0, 0))
713+
@test_throws ArgumentError extrema(spzeros(0))
714714
@test extrema(sparse(ones(n, n))) == (1.0, 1.0)
715715
@test extrema(sparse(ones(n))) == (1.0, 1.0)
716716
@test extrema(A; dims=:) == extrema(B; dims=:)

test/reduce.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr)
258258
@test minimum(5) == 5
259259
@test extrema(5) == (5, 5)
260260
@test extrema(abs2, 5) == (25, 25)
261-
@test Core.Compiler.extrema(abs2, 5) == (25, 25)
262261

263262
let x = [4,3,5,2]
264263
@test maximum(x) == 5
@@ -269,7 +268,6 @@ let x = [4,3,5,2]
269268
@test maximum(abs2, x) == 25
270269
@test minimum(abs2, x) == 4
271270
@test extrema(abs2, x) == (4, 25)
272-
@test Core.Compiler.extrema(abs2, x) == (4, 25)
273271
end
274272

275273
@test maximum([-0.,0.]) === 0.0

0 commit comments

Comments
 (0)