-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Adjust initialization in maximum and minimum #27845
Changes from all commits
3bab777
151594c
55e23d7
a1ac258
3f0ed84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,28 +3,41 @@ | |
## Functions to compute the reduced shape | ||
|
||
# for reductions that expand 0 dims to 1 | ||
reduced_index(i::OneTo) = OneTo(1) | ||
reduced_index(i::Slice) = Slice(first(i):first(i)) | ||
reduced_index(i::AbstractUnitRange) = | ||
throw(ArgumentError( | ||
""" | ||
No method is implemented for reducing index range of type $typeof(i). Please implement | ||
reduced_index for this index type or report this as an issue. | ||
""" | ||
)) | ||
reduced_indices(a::AbstractArray, region) = reduced_indices(axes(a), region) | ||
|
||
# for reductions that keep 0 dims as 0 | ||
reduced_indices0(a::AbstractArray, region) = reduced_indices0(axes(a), region) | ||
|
||
function reduced_indices(inds::Indices{N}, d::Int, rd::AbstractUnitRange) where N | ||
function reduced_indices(inds::Indices{N}, d::Int) where N | ||
d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) | ||
if d == 1 | ||
return (oftype(inds[1], rd), tail(inds)...) | ||
return (reduced_index(inds[1]), tail(inds)...) | ||
elseif 1 < d <= N | ||
return tuple(inds[1:d-1]..., oftype(inds[d], rd), inds[d+1:N]...)::typeof(inds) | ||
return tuple(inds[1:d-1]..., oftype(inds[d], reduced_index(inds[d])), inds[d+1:N]...)::typeof(inds) | ||
else | ||
return inds | ||
end | ||
end | ||
reduced_indices(inds::Indices, d::Int) = reduced_indices(inds, d, OneTo(1)) | ||
|
||
function reduced_indices0(inds::Indices{N}, d::Int) where N | ||
d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) | ||
if d <= N | ||
ind = inds[d] | ||
return reduced_indices(inds, d, (isempty(ind) ? ind : OneTo(1))) | ||
rd = isempty(ind) ? ind : reduced_index(inds[d]) | ||
if d == 1 | ||
return (rd, tail(inds)...) | ||
else | ||
return tuple(inds[1:d-1]..., oftype(inds[d], rd), inds[d+1:N]...)::typeof(inds) | ||
end | ||
else | ||
return inds | ||
end | ||
|
@@ -38,7 +51,7 @@ function reduced_indices(inds::Indices{N}, region) where N | |
if d < 1 | ||
throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) | ||
elseif d <= N | ||
rinds[d] = oftype(rinds[d], OneTo(1)) | ||
rinds[d] = reduced_index(rinds[d]) | ||
end | ||
end | ||
tuple(rinds...)::typeof(inds) | ||
|
@@ -53,7 +66,7 @@ function reduced_indices0(inds::Indices{N}, region) where N | |
throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) | ||
elseif d <= N | ||
rind = rinds[d] | ||
rinds[d] = oftype(rind, (isempty(rind) ? rind : OneTo(1))) | ||
rinds[d] = isempty(rind) ? rind : reduced_index(rind) | ||
end | ||
end | ||
tuple(rinds...)::typeof(inds) | ||
|
@@ -79,25 +92,6 @@ end | |
reducedim_initarray(A::AbstractArray, region, init, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), init) | ||
reducedim_initarray(A::AbstractArray, region, init::T) where {T} = reducedim_initarray(A, region, init, T) | ||
|
||
function reducedim_initarray0(A::AbstractArray{T}, region, f, ops) where T | ||
ri = reduced_indices0(A, region) | ||
if isempty(A) | ||
if prod(length, reduced_indices(A, region)) != 0 | ||
reducedim_initarray0_empty(A, region, f, ops) # ops over empty slice of A | ||
else | ||
R = f == identity ? T : Core.Compiler.return_type(f, (T,)) | ||
similar(A, R, ri) | ||
end | ||
else | ||
R = f == identity ? T : typeof(f(first(A))) | ||
si = similar(A, R, ri) | ||
mapfirst!(f, si, A) | ||
end | ||
end | ||
|
||
reducedim_initarray0_empty(A::AbstractArray, region, f, ops) = mapslices(x->ops(f.(x)), A, dims = region) | ||
reducedim_initarray0_empty(A::AbstractArray, region,::typeof(identity), ops) = mapslices(ops, A, dims = region) | ||
|
||
# TODO: better way to handle reducedim initialization | ||
# | ||
# The current scheme is basically following Steven G. Johnson's original implementation | ||
|
@@ -124,8 +118,33 @@ function _reducedim_init(f, op, fv, fop, A, region) | |
return reducedim_initarray(A, region, z, Tr) | ||
end | ||
|
||
reducedim_init(f, op::typeof(max), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, f, maximum) | ||
reducedim_init(f, op::typeof(min), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, f, minimum) | ||
# initialization when computing minima and maxima requires a little care | ||
for (f1, f2, initval) in ((:min, :max, :Inf), (:max, :min, :(-Inf))) | ||
@eval function reducedim_init(f, op::typeof($f1), A::AbstractArray, region) | ||
# First compute the reduce indices. This will throw an ArgumentError | ||
# if any region is invalid | ||
ri = reduced_indices(A, region) | ||
|
||
# Next, throw if reduction is over a region with length zero | ||
any(i -> isempty(axes(A, i)), region) && _empty_reduce_error() | ||
|
||
# Make a view of the first slice of the region | ||
A1 = view(A, ri...) | ||
|
||
if isempty(A1) | ||
# If the slice is empty just return non-view version as the initial array | ||
return copy(A1) | ||
else | ||
# otherwise use the min/max of the first slice as initial value | ||
v0 = mapreduce(f, $f2, A1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't seem to be correct to use the min/max of the first slice as the initial value for all slices. I'm probably missing something. (I wish I could have used something simpler than what I did for this at #28027). BTW, typo below: "intial" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be fine. If you are computing the maximum along a dimension then it should be fine to initialize with any value less than or equal to the first element in the slice. The minimum over the first slice will satisfy this, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I misunderstood "first slice". I thought it referred to the first slice along which reduction is performed. Carry on. |
||
|
||
# but NaNs need to be avoided as intial values | ||
v0 = v0 != v0 ? typeof(v0)($initval) : v0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT the Actually, maybe something similar to the Anyway, a few tests for these corner cases would be good. ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is basically a check for floats but tries to be a little more generic. Using the |
||
|
||
return reducedim_initarray(A, region, v0) | ||
end | ||
end | ||
end | ||
reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(max), A::AbstractArray{T}, region) where {T} = | ||
reducedim_initarray(A, region, zero(f(zero(T)))) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like simply making this a
UnitRange
instead of aSlice
will do the trick —make -C test reduce reduced offsetarray
passes for me locally with that change.