-
-
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
Add mapreduce_single function #25051
Conversation
2553673
to
e120c61
Compare
This has the benefit of being able to get rid of the funny composition behaviour to get the widening behaviour of |
e120c61
to
937b954
Compare
Okay, I think this should be ready. I tried to avoid touching the |
@TotalVerb You did the last deep dive into this code: would you mind having a quick look over it? |
base/reduce.jl
Outdated
reduce_single(op, x) = x | ||
reduce_single(::typeof(+), x::Bool) = Int(x) | ||
|
||
reduce_single(::typeof(add_sum), x) = reduce_single(+, x) |
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.
Although adding + zero(x)
for add and * one(x)
was ugly, I think it is more general. There are theoretically other types than Bool
that are not the type of their "additive monoid" or "multiplicative monoid". Char
is now one of these for multiplication. Of course, Irrational
is one of these, but that doesn't support zero
either. The extra add or multiply should be easily optimized by LLVM for all types except already-slow ones like BigInt
, anyway.
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.
zero(pi)
and one('a')
both currently throw errors, so they won't work either.
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.
Well, let's leave it as is then. one('a')
should theoretically return ""
though.
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.
Yeah, I'll add a fix for that.
@@ -372,7 +428,7 @@ 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::Callable, a) = mapreduce(promote_sys_size_add ∘ f, +, a) | |||
sum(f, a) = mapreduce(f, add_sum, a) |
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.
Good catch: it looks like this Callable is not necessary for avoiding ambiguity any more. The same change should apply for prod
.
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.
Fixed.
reducedim_initarray(A, region, real(zero(eltype(A)))) | ||
global reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(*), A::T, region) = | ||
reducedim_initarray(A, region, real(one(eltype(A)))) | ||
global reducedim_init(f, op::Union{typeof(+),typeof(add_sum)}, A::T, region) = |
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.
I think it's not safe to generalize this to f ∉ [identity, abs, abs2]
because we need the reduction operator to be type stable over whatever is produced by the f
used for mapreduce
. Nevertheless, it's a nice simplification to have, but I think we will need to continue hardcoding the list of acceptable map
functions.
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.
Won't mapreduce_single(f, op, zero(eltype(A)))
should handle that?
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.
In general, for some choices of f
, this assumes that mr_single(f, +, 0) + f(0)
, etc., will be of the same concrete type of mr_single(f, +, 0)
, which one can design f
to make it fail (for example, in the case of *
, if f
return a dimensionful quantity, like 1m
, which has different concrete type than 1m^2
). This seems to be where _reducedim_init
is more general and guards against. Of course it is also possible to thwart _reducedim_init
with badly behaving f
, so 😕.
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.
Although I'm not actually sure _reducedim_init
does what it should in these type unstable cases, so maybe it's best to leave cleanup of _reducedim_init
to later.
@@ -422,7 +478,7 @@ julia> prod(1:20) | |||
2432902008176640000 | |||
``` | |||
""" | |||
prod(a) = mapreduce(promote_sys_size_mul, *, a) | |||
prod(a) = mapreduce(identity, mul_prod, a) | |||
|
|||
## maximum & minimum | |||
|
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 might be worth changing the f(a1)
in the below function to mapreduce_single(f, op, a1)
for consistency, although no reasonable types I can think of should have a lattice structure outside the type itself.
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.
done.
@@ -133,7 +141,7 @@ function mapfoldr(f, op, itr) | |||
if isempty(itr) |
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.
Unrelated comment, which can be done as part of this PR or in a later one: Since we have #24187 Iterators.Reverse
now, I think we can write these mapfoldr
functions generically. As it stands it only works with arrays, and this generalization may allow it to work with arbitrary reversible iterators.
How about calling this |
Because it deals with singleton case, in the same way that Perhaps it would be best to change it to |
I would say go with |
eae9a54
to
982c833
Compare
Okay done. One question: should |
Also, I didn't touch |
LGTM. Rebase? |
Since the demise of `r_promote` in #22825, there is now a type-instability in `mapreduce` if the operator does not give an element of the same type as the input. This arose during my implementation of Kahan summation using a reduction operator, see: JuliaMath/KahanSummation.jl#7 This adds a `mapreduce_single` function which defines what the result should be in these cases.
The promotion machinery for reduction in sum/prod was changed in JuliaLang/julia#25051. This updates the use.
Since the demise of
r_promote
in #22825, there is now a type-instability inmapreduce
if the operator does not give an element of the same type as the input. This arose during my implementation of Kahan summation using a reduction operator, see: JuliaMath/KahanSummation.jl#7This adds a
mapreduce_single
function which defines what the result should be in these cases.