8000 BUG: __numpy_ufunc__ passes on only a single output argument · Issue #4160 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: __numpy_ufunc__ passes on only a single output argument #4160

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

Closed
mhvk opened this issue Dec 31, 2013 · 8 comments
Closed

BUG: __numpy_ufunc__ passes on only a single output argument #4160

mhvk opened this issue Dec 31, 2013 · 8 comments

Comments

@mhvk
Copy link
Contributor
mhvk commented Dec 31, 2013

I'm trying to use __numpy_ufunc__ for the Quantity subclass in astropy (which adds and propagates units associated with the array), and while mostly it is a joy compared to the previous __array_prepare__ etc tangle, for functions with two outputs there is a problem, in that only one output is passed on among the keyword arguments. This can be seen from the following example:

import numpy as np
class trial(np.ndarray):               
    def __new__(cls, *args, **kwargs):
        return np.array(*args, **kwargs).view(cls)
    def __numpy_ufunc__(self, ufunc, method, i, inputs, **kwargs):
        print(self, ufunc, method, i, inputs, kwargs)
        inputs = [arg.view(np.ndarray) for arg in inputs]
        return ufunc(*inputs, **kwargs)

a = trial(100)
b = np.array([1.])
c = np.array([2.])
np.modf(a, b)
#100 <ufunc 'modf'> __call__ 0 (trial(100),) {'out': array([ 1.])}
# (array([ 0.]), array([ 100.]))
np.modf(a, b, c)
#100 <ufunc 'modf'> __call__ 0 (trial(100),) {'out': array([ 2.])}
# (array([ 0.]), array([ 100.]))
@njsmith
Copy link
Member
njsmith commented Dec 31, 2013

This looks like a pretty serious bug in __numpy_ufunc__, since it makes it impossible for modf and other multiple-output ufuncs to work correctly with any class defining a __numpy_ufunc__ method when out= is used, and causes wrong results to be returned silently. (It's not even possible to fall back on the default implementation, because the way __numpy_ufunc__ works its the job of the called method to do this, and when this bug kicks in then the arguments are already lost before it can delegate.) I'm going to tentatively mark it as a 1.9 blocker.

CC: @pv, @cowlicks

@pv
Copy link
Member
pv commented Jan 1, 2014

Yep, 1.9 blocker. The case if multiple output args is ignored in the implementation. How to deal with them is also unspecified in the spec.

This is probably simple to fix, however, now that we are not bound by any backward compatibility constraints.

@mhvk
Copy link
Contributor Author
mhvk commented Jan 1, 2014

@pv - the docstrings of both np.modf and np.frexp start with

modf(x[, out1, out2])

This would suggest one possible choice, of having out1 and/or out2 among kwargs.

Note, though, that one cannot give these as keyword arguments in the direct call either. While np.modf(a, b, c) and np.modf(a, out=b) work, np.modf(a, out=b, out2=c) or anything equivalent does not; rather, it raises

ValueError: cannot specify 'out' as both a positional and keyword argument

ps. while trying the above, I realised the test on whether something is a keyword argument is simply whether it starts with "out"; np.modf(100.5, outblabla=a) does fine.

@njsmith
Copy link
Member
njsmith commented Jan 1, 2014

Iirc the clearer ufunc calling convention is out=(arr1, arr2), so that's
what we'd want to normalize to for numpy_ufunc.

The outblahblah thing sounds like a bug too, but a different one - maybe
file it separately?
On 31 Dec 2013 19:45, "Marten van Kerkwijk" notifications@github.com
wrote:

@pv https://github.com/pv - the docstrings of both np.modf and np.frexpstart with

modf(x[, out1, out2])

This would suggest one possible choice, of having out1 and/or out2 among
kwargs.

Note, though, that one cannot give these as keyword arguments in the
direct call either. While np.modf(a, b, c) and np.modf(a, out=b) work, np.modf(a,
out=b, out2=c) or anything equivalent does not; rather, it raises

ValueError: cannot specify 'out' as both a positional and keyword argument

ps. while trying the above, I realised the test on whether something is a
keyword argument is simply whether it starts with "out"; np.modf(100.5,
outblabla=a) does fine.


Reply to this email directly or view it on GitHubhttps://github.com//issues/4160#issuecomment-31417092
.

@cowlicks
Copy link
Contributor
cowlicks commented Jan 2, 2014

So in the PyUFunc_CheckOverride function we implicitly assume there is only one output here:

    /* If we have more args than nin, the last one must be `out`.*/
    if (nargs > nin) {
        obj = PyTuple_GET_ITEM(args, nargs - 1);
        PyDict_SetItemString(normal_kwds, "out", obj);
    }

I think what we actually want to do is grab all the positional arguments after the ninth one and pass them like out = (arr1, arr2, ...) (as @njsmith suggests) in the kwargs dict.

I'll try this out and get back to y'all.

edit: override was supposed to be kwargs

@cowlicks
Copy link
Contributor
cowlicks commented Jan 4, 2014

Here is how I propose we handle output variables:

  • one positional output variable x is passed in the kwargs dict as out : x
  • multiple positional output variables x0, x1, ... are passed as a tuple in the kwargs dict as out : (x0, x1, ...)
  • keyword output variables like out = x and out = (x0, x1, ...) are passed unchanged to the kwargs dict like out : x and out : (x0, x1, ...) respectively.
  • combinations of positional and keyword output variables are not supported.

Note that this does not change the proposed interface. Does this sound okay? cc @njsmith @pv

@cowlicks
Copy link
Contributor
cowlicks commented Jan 4, 2014

See pull 4171.

@pv
Copy link
Member
pv commented Jan 8, 2014

Fixed in gh-4171

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

No branches or pull requests

4 participants
0