# week-6 Ufunc Overrides

This week marked the beginning of the next stage of my proposal ambiguously named "NumPy Interactions". The first item scheduled is "Modify NumPy universal functions (ufuncs) to aware of sparse matrices (1 week)" The first thing I realized this wee is that my time allotment was too short. This is going to take a several weeks.

I'll explain the problem sparse matrices have with sparse matrices first, then describe my proposed solution.

### Problem¶

Ufunc don't play well with objects that are not derived from ndarrays. There is some limited functionality such as `__array_prepare__`

and `__array_wrap__`

but these methods still prohibit you from accessing the array data, or changing it shape (not very useful for coercing sparse matrices into something ufuncable)

Sparse matrices cannot be used with ufuncs reliably, for example:

```
import numpy as np
import scipy.sparse as sp
a = np.random.randint(5, size=(3,3))
b = np.random.randint(5, size=(3,3))
asp = sp.csr_matrix(a)
bsp = sp.csr_matrix(b)
```

```
a, b
```

```
np.multiply(a, b) # The right answer
```

```
np.multiply(asp, bsp).todense() # calls __mul__ which does matrix multiplication
```

```
np.multiply(a, bsp) # Returns NotImplemented to user, bad!
```

Returning `NotImplemented`

to user should not happen. I'm not sure if the blame for this lies in scipy.sparse or numpy, but it should be fixed.

```
np.multiply(asp, b)
```

I'm not sure what happened here either, but I think raising `TypeError`

would be preferable.

### Proposed Solution¶

Basically, I want to implement a mechanism that will let arguments override ufuncs. So any object that ufuncs do not work with can define the attribute `__ufunc_override__`

as a dictionary keyed the name of the ufunc to be overridden, and valued with the callable function which should override it.

Each time a ufunc is run it will check each argument for the `__ufunc_override__`

attribute. So this will add the overhead of one attribute check for each argument. The logic of calling the correct replacement function will come after this check. So it won't impact performance for normal operations that don't want to override the ufunc.

I prototyped this behavior in python. `ufunc_override.py`

(link) defines a decorator that preforms the logic described above. Then all the ufuncs in numpy are decorated with it. So `import ufunc_override`

imports a wrapped version of numpy.

I'm also working with a slightly modified version of scipy.sparse. With this added to the base class.

`__ufunc_override__ = {'multiply':multiply}`

The relavant code on this branch is here.

#### A little demo.¶

```
import ufunc_override as uo
```

```
asp.__ufunc_override__
```

```
uo.multiply(asp, bsp).todense()
```

```
uo.multiply(a, bsp)
```

```
uo.multiply(asp, b)
```

It works! How about we try with something a little more basic too, a bare bones class that should override the add ufunc.

```
class DemoClass(object):
def replacement_func(*args):
return 42 # The answer.
__array_priority__= 66 # Some number that's > 10.1
__ufunc_override__={'add':replacement_func}
foo = DemoClass()
```

```
uo.add(a, foo)
```

```
uo.add(asp, foo)
```

## Comments

Comments powered by Disqus