Skip to content

DOC: update binary-op / ufunc interactions and recommendations in ufunc overrides NEP #9027

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 5 commits into from
May 5, 2017

Conversation

shoyer
Copy link
Member

@shoyer shoyer commented Apr 30, 2017

Split off and extended from #9014

Adds:

  • More explicit language about how binary operators work, hopefully assuming less knowledge about how Python defines binary operations (I'm guessing this NEP will be read by users not as familiar with them as we are).
  • A recommendation to use "Option (1)" for implementing special methods on array-like classes.
  • More explanation of our reasoning for why opting out of numpy style binary operations requires __array_ufunc__ = None.
  • A table of binary operators and corresponding numpy ufuncs. I think this will be useful for implementors, e.g., for someone writing a unit library or another mixin class like NDArrayOperatorsMixin.

Because we have listed authors on the NEP, I would like to make sure that every listed author approves of the document before we merge this change:

(Feel free to check off your name on this list if you have edit rights, or I will do so, after you've responded affirmatively in a comment.)

@shoyer shoyer added this to the 1.13.0 release milestone Apr 30, 2017
@shoyer shoyer force-pushed the ufunc-overrides-nep-update branch from 6b9d342 to 490578e Compare April 30, 2017 05:18
provides overrides for all binary operators with corresponding ufuncs.

implementation for :class:`ndarray` itself. We decided to require disabling
ufuncs to opt out to ensure that a class cannot define Ufuncs to return
Copy link
Member

Choose a reason for hiding this comment

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

Bit confusing, "ufuncs to opt out in order to ensure" would be clearer I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

``-`` ``sub`` :func:`subtract`
``*`` ``mul`` :func:`multiply`
``/`` ``truediv`` :func:`true_divide`
(Python 3)
Copy link
Member

Choose a reason for hiding this comment

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

Would be better to put this on the line above, same below.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sphinx does a weird thing if I put it on one line, making the column very wide (I think this is basic on the longer length of the column header ======). So this version ends up looking better when rendered to HTML.

``&`` ``and_`` :func:`bitwise_and`
``^`` ``xor_`` :func:`bitwise_xor`
``|`` ``or_`` :func:`bitwise_or`
\ ``divmod`` :func:`floor_divide`, :func:`mod` [*]_
Copy link
Member

Choose a reason for hiding this comment

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

Maybe "--" instead of spaces.

Copy link
Member Author

Choose a reason for hiding this comment

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

I put in "NA" instead. I am concerned that "--" could be confused with an infix operator.

``^`` ``xor_`` :func:`bitwise_xor`
``|`` ``or_`` :func:`bitwise_or`
\ ``divmod`` :func:`floor_divide`, :func:`mod` [*]_
``@`` ``matmul`` Not yet implemented
Copy link
Member

Choose a reason for hiding this comment

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

Maybe "Not yet implemented as a ufunc".

Copy link
Member Author

Choose a reason for hiding this comment

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

done

@mhvk
Copy link
Contributor

mhvk commented Apr 30, 2017

@shoyer - thanks for doing this. I'm happy with your changes.

@@ -620,8 +620,8 @@ make more sense to ask classes like ``MyObject`` to implement a full
``__array_ufunc__`` [6]_. In the end, allowing classes to opt out was
preferred, and the above reasoning led us to agree on a similar
implementation for :class:`ndarray` itself. We decided to require disabling
ufuncs to opt out to ensure that a class cannot define Ufuncs to return
different results than the corresponding binary operations (i.e., if
ufuncs to opt out in order to ensure that a class cannot define Ufuncs to
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, now that I look at this in combination with "We decided to require disabling" I still can't figure out what it says ;) I think what this is trying to say is that ufuncs will raise TypeError when one of the arguments opts out in order to ensure ...

Copy link
Member Author

Choose a reason for hiding this comment

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

I took another pass at this.

disabling Ufuncs so a class cannot define a Ufuncs to return a different
result than the corresponding binary operations (i.e., if
``np.add(x, y)`` is defined, it should match ``x + y``). Our goal was to
simply the dispatch logic for binary operations with NumPy arrays
Copy link
Member

Choose a reason for hiding this comment

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

simply <- simplify

@charris
Copy link
Member

charris commented May 1, 2017

Looks good apart from the wrong word. At some point it could use a bit more polish but for now it is good enough.

- Otherwise, :class:`ndarray` unilaterally calls the corresponding Ufunc.
Ufuncs never return ``NotImplemented``, so **reflexive methods such
as** ``other.__rmul__`` **cannot be used to override arithmetic with
NumPy arrays if** ``__array_ufunc__`` **is set**. Instead, the resulting
Copy link
Member

Choose a reason for hiding this comment

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

maybe make it "set to any value besides None"?

Copy link
Member

Choose a reason for hiding this comment

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

That's better.

@@ -482,12 +506,12 @@ are not compatible, i.e., implementations should be something like::

# Option 1: call ufunc directly
Copy link
Member

Choose a reason for hiding this comment

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

BTW, feel free to declare this out-of-scope for this PR, but maybe we should drop the discussion of "option 2" from the NEP now that we've pretty much settled on "option 1" as recommended?

binary arithmetic with :class:`ndarray` (i.e., they must either implement
``__array_ufunc__`` or set it to :obj:`None`). We believe this is a good
thing, because it ensures the consistency of ufuncs and arithmetic on all
objects that support them.
Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see, you added more discussion of option 1 vs 2 instead. That's also a plausible approach!

I probably wouldn't use "viral" for something we want to recommend... it's a pretty loaded word? Plus it kinda doesn't matter whether people want their classes to be "viral" or not, because this is what ndarray does, so to the extent it's viral they have to follow, and if they don't follow it's not so viral then is it...?

It might be simplest to just delete the option 2 stuff from the NEP; the history's always available in git. Or this is probably fine; honestly it's 11:55 pm and I don't really have brain to track all the details in this giant block of text and that's probably influencing my opinion...

Copy link
Contributor

Choose a reason for hiding this comment

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

I think as long as the interface is provisional, we should keep both options; even for ndarray itself, we will want to optimize things and might well find it is easier in a slightly different way.

Clarify the role of `array_ufunc` in overriding Python numeric operators.
@charris charris merged commit b7d3097 into numpy:master May 5, 2017
@shoyer
Copy link
Member Author

shoyer commented May 5, 2017

I guess we're taking silence from @pv and @cowlicks as consent? :)

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.

4 participants