[Numpy-discussion] replacing the mechanism for dispatching ufuncs
Mark Wiebe
mwwiebe at gmail.com
Tue Jun 21 13:57:25 EDT 2011
On Tue, Jun 21, 2011 at 12:36 PM, Charles R Harris <
charlesr.harris at gmail.com> wrote:
>
>
> On Mon, Jun 20, 2011 at 12:32 PM, Mark Wiebe <mwwiebe at gmail.com> wrote:
>
>> NumPy has a mechanism built in to allow subclasses to adjust or override
>> aspects of the ufunc behavior. While this goal is important, this mechanism
>> only allows for very limited customization, making for instance the masked
>> arrays unable to work with the native ufuncs in a full and proper way. I
>> would like to deprecate the current mechanism, in particular
>> __array_prepare__ and __array_wrap__, and introduce a new method I will
>> describe below. If you've ever used these mechanisms, please review this
>> design to see if it meets your needs.
>>
>>
>>
> The current approach is at a dead end, so something better needs to be
> done.
>
>
>> Any class type which would like to override its behavior in ufuncs would
>> define a method called _numpy_ufunc_, and optionally an attribute
>> __array_priority__ as can already be done. The class which wins the priority
>> battle gets its _numpy_ufunc_ function called as follows:
>>
>> return arr._numpy_ufunc_(current_ufunc, *args, **kwargs)
>>
>>
>> To support this overloading, the ufunc would get a new support method,
>> result_type, and there would be a new global function, broadcast_empty_like.
>>
>> The function ufunc.empty_like behaves like the global np.result_type, but
>> produces the output type or a tuple of output types specific to the ufunc,
>> which may follow a different convention than regular arithmetic type
>> promotion. This allows for a class to create an output array of the correct
>> type to pass to the ufunc if it needs to be different than the default.
>>
>> The function broadcast_empty_like is just like empty_like, but takes a
>> list or tuple of arrays which are to be broadcast together for producing the
>> output, instead of just one.
>>
>>
> How does the ufunc get called so it doesn't get caught in an endless loop?
> I like the proposed method if it can also be used for classes that don't
> subclass ndarray. Masked array, for instance, should probably not subclass
> ndarray.
>
The function being called needs to ensure this, either by extracting a raw
ndarray from instances of its class, or adding a 'subok = False' parameter
to the kwargs. Supporting objects that aren't ndarray subclasses is one of
the purposes for this approach, and neither of my two example cases
subclassed ndarray.
-Mark
>
>
>> Thanks,
>> Mark
>>
>>
>> A simple class which overrides the ufuncs might look as follows:
>>
>> def sin(ufunc, *args, **kwargs):
>> # Convert degrees to radians
>> args[0] = np.deg2rad(args[0])
>> # Return a regular array, since the result is not in degrees
>> return ufunc(*args, **kwargs)
>>
>> class MyDegreesClass:
>> """Array-like object with a degrees unit"""
>>
>> def __init__(arr):
>> self.arr = arr
>>
>> def _numpy_ufunc_(ufunc, *args, **kwargs):
>> override = globals().get(ufunc.name)
>> if override:
>> return override(ufunc, *args, **kwargs)
>> else:
>> raise TypeError, 'ufunc %s incompatible with MyDegreesClass' %
>> ufunc.name
>>
>>
>>
>> A more complex example will be something like this:
>>
>> def my_general_ufunc(ufunc, *args, **kwargs):
>> # Extract the 'out' argument. This only supports ufuncs with
>> # one output, currently.
>> out = kwargs.get('out')
>> if len(args) > ufunc.nin:
>> if out is None:
>> out = args[ufunc.nin]
>> else:
>> raise ValueError, "'out' given as both a position and keyword
>> argument"
>>
>> # Just want the inputs from here on
>> args = args[:ufunc.nin]
>>
>> # Strip out MyArrayClass, but allow operations with regular ndarrays
>> raw_in = []
>> for a in args:
>> if isinstance(a, MyArrayClass):
>> raw_in.append(a.arr)
>> else:
>> raw_in.append(a)
>>
>> # Allocate the output array
>> if not out is None:
>> if isinstance(out, MyArrayClass):
>> raise TypeError, "'out' must have type MyArrayClass"
>> else:
>> # Create the output array, obeying the 'order' parameter,
>> # but disallowing subclasses
>> out = np.broadcast_empty_like([args,
>> order=kwargs.get('order'),
>> dtype=ufunc.result_type(args),
>> subok=False)
>>
>> # Override the output argument
>> kwargs['out'] = out.arr
>>
>> # Call the ufunc
>> ufunc(*args, **kwargs)
>>
>> # Return the output
>> return out
>>
>> class MyArrayClass:
>> def __init__(arr):
>> self.arr = arr
>>
>> def _numpy_ufunc_(ufunc, *args, **kwargs):
>> override = globals().get(ufunc.name)
>> if override:
>> return override(ufunc, *args, **kwargs)
>> else:
>> return my_general_ufunc(ufunc, *args, **kwargs)
>>
>>
> Chuck
>
> _______________________________________________
> NumPy-Discussion mailing list
> NumPy-Discussion at scipy.org
> http://mail.scipy.org/mailman/listinfo/numpy-discussion
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/numpy-discussion/attachments/20110621/a875eef0/attachment.html>
More information about the NumPy-Discussion
mailing list