Conversation
mrocklin
left a comment
There was a problem hiding this comment.
I'm glad to see this start. Some small comments follow.
Also, to avoid confusion with the term "generalized ufuncs" I recommend changing the title of the PR to something like "implement array_ufunc protocol"
cupy/core/core.pyx
Outdated
| for x in check: | ||
| if not isinstance(x, (ndarray, Number)): | ||
| # do *not* pass any old ndarray-like | ||
| return NotImplemented |
There was a problem hiding this comment.
Why this extra logic on top of what cupy already does?
There was a problem hiding this comment.
Passing to cupy results in
TypeError: Unsupported type <class 'numpy.ndarray'>
or using this as-is results in
TypeError: operand type(s) all returned NotImplemented from __array_ufunc__
Keeping the current means that, in theory, some other implementation could still work in combination with a cupy.ndarray, because NotImplemented implies trying the __array_ufunc__ of the other object involved; but in practice, I think it can only realistically be cupy or a subclass. I thunk best would be to raise a TypeError with a carefully phrased message.
There was a problem hiding this comment.
My understanding is that CuPy could also return NotImplemented if they want to give up and hand to some other system and your solution here would just pass that through. Is this correct? If so then this probably isn't the right place to implement this logic. Moreover, I don't think that you and I have enough experience with how CuPy is used to make policy decisions like this. I think that we should avoid this discussion, or perhaps raise it as a separate issue that lives independently from this effort, which I think should be restricted to implementing the __array_ufunc__ protocol.
| name = ufunc.__name__ | ||
| if method == '__call__': | ||
| if ufunc.signature is not None: | ||
| return NotImplemented |
There was a problem hiding this comment.
This could use a comment explaining that we don't yet support generalized ufuncs
cupy/core/core.pyx
Outdated
| if 'out' in kwargs: | ||
| # need to unfold tuple argument in kwargs | ||
| check = inputs + kwargs['out'] | ||
| kwargs['out'] = kwargs['out'][0] |
There was a problem hiding this comment.
I'm curious, why was this logic necessary? Also, should we be concerned that if out has length greater than one that we've lost information?
|
This could also generally use some tests. I think that this will communicate the intended outcome of this work more clearly to other potential reviewers. |
|
@martindurant is it ok to remove the WIP label? |
|
I am happy with the code that is here as well as the tests, so yes. |
cupy/core/core.pyx
Outdated
| import cupy as cp # top-level ufuncs | ||
| if 'out' in kwargs: | ||
| # need to unfold tuple argument in kwargs | ||
| check = inputs + kwargs['out'] |
There was a problem hiding this comment.
It looks check is not used.
cupy/core/core.pyx
Outdated
| # we don't support generalised-ufuncs (gufuncs) | ||
| return NotImplemented | ||
| try: | ||
| cp_ufunc = getattr(cp, ufunc.__name__) |
cupy/core/core.pyx
Outdated
| return NotImplemented | ||
| try: | ||
| cp_ufunc = getattr(cp, ufunc.__name__) | ||
| return cp_ufunc(*inputs, **kwargs) |
There was a problem hiding this comment.
Put this line outside the try clause.
| # the only ufunc attribute currently | ||
| # http://docs-cupy.chainer.org/en/stable/reference/ufunc.html#ufunc-at | ||
| # self.scatter_add(*inputs, **kwargs) | ||
| else: |
There was a problem hiding this comment.
Do we have a chance to support some of the reduction routines (like add.reduce via sum, maximum.reduce via amax, etc.) here (maybe via ad-hoc manual mapping)? Or do you think it's better to first support reduce in CuPy's ufuncs (so that no ad-hoc coding is needed here)?
There was a problem hiding this comment.
Note that numpy reductions already dispatch if an appropriate method is defined
In [1]: class Foo:
...: def sum(*args, **kwargs):
...: return 123
...:
In [2]: import numpy as np
In [3]: np.sum(Foo())
Out[3]: 123Adding support for ufunc.reduce would also be good, but this seems better to do in the ufuncs themselves rather than in the __array_ufunc__ protocol.
There was a problem hiding this comment.
Ah, sum is already dispatched. I misunderstood.
cupy/core/core.pyx
Outdated
| a number, i.e., raise ValueError instead of silently converting a | ||
| numpy array. | ||
| """ | ||
| import cupy as cp # top-level ufuncs |
There was a problem hiding this comment.
Please remove as cp, use cupy directly.
|
Thank you for useful PR! |
| """ | ||
| import cupy # top-level ufuncs | ||
| if 'out' in kwargs: | ||
| # need to unfold tuple argument in kwargs |
There was a problem hiding this comment.
As far as I understand, the out tuple could in theory have more entries, for some future ufunc that has multiple outputs. I don't think numpy as any of these, never mind cupy - so I agree that an exception could be raised if the tuple length>1. This is what numpy does, e.g., np.sin(x, out=(x, x)) -> ValueError.
|
jenkins, test this please. This PR will be merge in v5.0.0b2. |
|
Is there anything more to do here? |
|
I found error. Can you fix this? |
|
@okuta , |
|
This is message to testing bot. jenkins, test this please. |
|
@okuta Can I merge it? |
|
Please wait. Some tests are failed. I will investigate it. |
|
I merged #1286, so I think this PR is CI ready. |
|
I merged in those changes. |
|
jenkins, test this please. |
|
|
||
|
|
||
| @testing.gpu | ||
| class TestArrayUfunc(unittest.TestCase): |
There was a problem hiding this comment.
Please add @testing.with_requires('numpy>=1.13') because, test is failed with NumPy<1.13.0.
|
@okuta , done |
|
Thanks! jenkins, test this please. |
|
LGTM! |
|
I'm very happy to see this merged! |
|
Hurray |
|
OMG you guys made it happen!! |
So that numpy functions should work on cupy arrays. Would allow cupy arrays to be used transparently in much numpy-oriented code.
The current plan is to error, if in a non-unary ufunc, a numpy (non-device) and a cupy array are combined.
Based on
Fixes #1230 (eventually)