Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Merged
merged 2 commits into from
May 18, 2018

Conversation

mhvk
Copy link
Contributor

@mhvk mhvk commented May 14, 2018

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.

* arguments have no core dimensions. Sets an error if this is not the case.
*/
static int
_check_keepdims_support(PyUFuncObject *ufunc) {
Copy link
Member

@eric-wieser eric-wieser May 14, 2018

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)

@mhvk mhvk force-pushed the gufunc-keepdims branch from 379eda8 to b589404 Compare May 14, 2018 19:37
@mhvk mhvk added this to the 1.15.0 release milestone May 14, 2018
@mhvk mhvk changed the title DO NOT MERGE Add keepdims argument for generalized ufuncs. ENH: Add keepdims argument for generalized ufuncs. May 14, 2018
@mhvk
Copy link
Contributor Author

mhvk commented May 14, 2018

@eric-wieser - OK, this now contains also the documentation for keepdims (as a second commit), and I corrected the usage of _check_keepdims_support.

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
Copy link
Contributor Author

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.

@mhvk mhvk force-pushed the gufunc-keepdims branch from b589404 to 9579552 Compare May 15, 2018 17:09
@mhvk
Copy link
Contributor Author

mhvk commented May 15, 2018

@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 axis.

*/
if (keepdims != -1) {
retval = _check_keepdims_support(ufunc);
if (retval < 0) {
Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Member

@eric-wieser eric-wieser May 16, 2018

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;
}

Copy link
Contributor Author

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...

Copy link
Member

@eric-wieser eric-wieser May 16, 2018

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?

Copy link
Contributor Author

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...

Copy link
Member

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

@eric-wieser
Copy link
Member

Looks pretty reasonable - I'll take a more thorough look later. I can't say I fully understand the gufunc internals yet.

*/
if (keepdims == 1) {
for (i = 0; i < nop; ++i) {
core_num_dims_array[i] = ufunc->core_num_dims[0];
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

/* 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[
Copy link
Member

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,

Copy link
Contributor Author

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;
Copy link
Member

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

Copy link
Contributor Author

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...

Copy link
Member

@eric-wieser eric-wieser left a 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

mhvk added 2 commits May 18, 2018 09:46
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.
@mhvk mhvk force-pushed the gufunc-keepdims branch from 9579552 to 8ccabd6 Compare May 18, 2018 13:46
@mhvk
Copy link
Contributor Author

mhvk commented May 18, 2018

@eric-wieser - OK, I made the last changes. Once merged, I'll go ahead and rebase the axis implementation on top, so we can look at that again (or, rather, think about how to deal with it for __array_ufunc__).

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
Copy link
Member

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)?

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants