-
-
Notifications
You must be signed in to change notification settings - Fork 11k
ENH: Add keepdims argument for generalized ufuncs. #11098
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
Conversation
numpy/core/src/umath/ufunc_object.c
Outdated
* arguments have no core dimensions. Sets an error if this is not the case. | ||
*/ | ||
static int | ||
_check_keepdims_support(PyUFuncObject *ufunc) { |
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.
This function should return an error code (0, -1) not a boolean (1, 0), since it's called _check_support
not _is_supported
(and sets an exception)
@eric-wieser - OK, this now contains also the documentation for |
doc/release/1.15.0-notes.rst
Outdated
one can pass in ``keepdims`` to leave a dimension with size 1 in the outputs, | ||
thus allowing proper broadcasting against the original inputs. The location of | ||
the extra dimension can be controlled with ``axes``. For instance, for the | ||
inner-product example, ``keepdims=True, axis=[-2, -2, -2]`` would act on the |
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.
Darn, this should be axes
here, of course. Will correct together with any other remaining comments.
@eric-wieser - you looked at this quite a bit already, do you think it is OK? If so, I can do the follow-up for just |
numpy/core/src/umath/ufunc_object.c
Outdated
*/ | ||
if (keepdims != -1) { | ||
retval = _check_keepdims_support(ufunc); | ||
if (retval < 0) { |
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.
nit: would probably combine these two lines - we do that pretty frequently
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.
As in if (retval = ... < 0)
? I don't see any example of that in ufunc_object
, and personally don't like it much. Anyway, will await for the more thorough look.
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 mean:
if (_check_keepdims_support(ufunc) < 0) {
goto fail;
}
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.
But fail
needs to have retval
set...
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.
What am I missing here? Is retval
sometimes not -1
on failure?
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 left at 0
by the get_ufunc_arguments
statement above. There is a debug statement in fail
that prints retval
, but to be honest I'm not sure it ever has a value other than -1 for failure. Probably would make sense to clean that all up, but perhaps in another PR...
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.
Yep, no need to change this now - just looked a little worrying
Looks pretty reasonable - I'll take a more thorough look later. I can't say I fully understand the gufunc internals yet. |
numpy/core/src/umath/ufunc_object.c
Outdated
*/ | ||
if (keepdims == 1) { | ||
for (i = 0; i < nop; ++i) { | ||
core_num_dims_array[i] = ufunc->core_num_dims[0]; |
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.
minor nit: I'd find this easier to read with an assignment all_num_dims = ufunc->core_num_dims[0]
or similar outside the loop - too easy to read this as an elementwise copy. But maybe I'm just tired
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
numpy/core/src/umath/ufunc_object.c
Outdated
/* Fill in 'iter_shape' and 'op_axes' for this output */ | ||
for (idim = 0; idim < num_dims; ++idim) { | ||
iter_shape[j] = core_dim_sizes[ | ||
iter_shape[j] = keepdims ? 1 : core_dim_sizes[ |
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.
Possibly clearer with an if
, because then that gives you somewhere to add a comment explaining why it is 1,
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 expanded the comment above the loop (which is not very useful as is...)
/* Fill in 'iter_shape' and 'op_axes' for this output */ | ||
for (idim = 0; idim < num_dims; ++idim) { | ||
iter_shape[j] = core_dim_sizes[ | ||
iter_shape[j] = keepdims ? 1 : core_dim_sizes[ | ||
ufunc->core_dim_ixs[dim_offset + idim]]; | ||
op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim); | ||
++j; |
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 to this PR: I'm struggling to understand what j
is, and why its state is kept between outputs
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 agree, I turn from thinking it clear to having no clue almost every time I look at this... Mostly because I only partially understand how the iterator works. But this would be a good place to comment it more heavily. For another PR...
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.
Only minor things to complain about here - generally looks like an unobtrusive change
The argument can only be used if all inputs share the same number of core dimension, and no output has any core dimensions.
Both in the general documentation and in the release notes. For the latter, also include a description of ``axes``, which was missing so far.
@eric-wieser - OK, I made the last changes. Once merged, I'll go ahead and rebase the |
correctly against the inputs. This option can only be used for generalized | ||
ufuncs that operate on inputs that all have the same number of core | ||
dimensions and with outputs that have no core dimensions , i.e., with | ||
signatures like ``(i),(i)->()`` or ``(m,m)->()``. If used, the location of |
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.
Do we also want to support (i),(i)->(),(i)
?
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 thought about this when I was revisiting the text, and wasn't sure. In principle, there is no reason not too, except I then have to be more careful with the shape of the output... What do you think?
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 don't think we need to decide in this PR - we can add that feature later if we can justify it. I think we should open an issue suggesting it as an enhancement, and merge this PR without any further changes.
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.
OK, agreed; see #11118. I'll merge this one then.
This is a single commit from #11018, to try to find the reason for the travis build failure.EDIT: this is a split-off of #11018, just implementing
keepdims
, to keep discussion more clearly separate.