Skip to content

Instantly share code, notes, and snippets.

@lkilcher
Last active January 30, 2016 01:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lkilcher/d1e2193cbc366adb49c8 to your computer and use it in GitHub Desktop.
Save lkilcher/d1e2193cbc366adb49c8 to your computer and use it in GitHub Desktop.
Bugs related to NumPy's new __numpy_ufunc__ hook.
import numpy as np
class buggy(object):
def __init__(self, arr):
self.arr = arr
def __pow__(self, other):
print "In __pow__"
return self.arr ** other
def __rpow__(self, other):
print "In __rpow__"
return other ** self.arr
def __ipow__(self, other):
print "In __ipow__"
self.arr **= other
return self
def __add__(self, other):
print "In __add__"
return self.arr + other
def __radd__(self, other):
print "In __radd__"
return other + self.arr
def __iadd__(self, other):
print "In __iadd__"
self.arr += other
return self
def __numpy_ufunc__(self, ufunc, method, i, inputs, **kwargs):
print "In __numpy_ufunc__"
a = np.arange(1, 6)
g = buggy(np.arange(2, 7))
print 'DOING __iadd__'
g += a
print g.arr
a = np.arange(1, 6)
g = buggy(np.arange(2, 7))
print 'DOING reversed __iadd__'
a += g
print a
a = np.arange(1, 6)
g = buggy(np.arange(2, 7))
print 'DOING __ipow__'
g **= a
print g.arr
a = np.arange(1, 6)
g = buggy(np.arange(2, 7))
print 'DOING reversed __ipow__'
a **= g
@lkilcher
Copy link
Author

This code generates the following output and error:

DOING __iadd__
In __iadd__
[ 3  5  7  9 11]
DOING reversed __iadd__
In __radd__
[ 3  5  7  9 11]
DOING __ipow__
In __ipow__
[   2    9   64  625 7776]
DOING reversed __ipow__
Traceback (most recent call last):
  File "test_power.py", line 60, in <module>
    a **= g
TypeError: unsupported operand type(s) for ** or pow(): 'numpy.ndarray' and 'buggy'

What's strange is that the presence of __numpy_ufunc__ lets NumPy know that it can redirect the a += g line to the g.__radd__ method, but the same is not true for the a **= g line. Why isn't g.__rpow__ called here?

I get this behavior on OSX 10.10.5, with Python 2.7 and NumPy 1.10.4. I also get this error with bleeding-edge NumPy (0bba66).

@lkilcher
Copy link
Author

I believe this is an unrelated sidenote and probably expected behavior. However, because it throws a similar error I'm pointing it out here.

I noticed that when the __numpy_ufunc__ is not present in the buggy class, line 48 (a += g) fails with:

Traceback (most recent call last):
  File "test.py", line 48, in <module>
    a += g
TypeError: ufunc 'add' output (typecode 'O') could not be coerced to provided output parameter (typecode 'l') according to the casting rule ''same_kind''

I suppose this is expected behavior because __numpy_ufunc__ indicates to NumPy that it should be able to handle this type of object. That is, when __numpy_ufunc__ is not present it throws this error.

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