Hi,

I'm just coding a package for uncertain arrays using the accelerated
numpy functionality intensively.  I'm sorry, but I have to give some
background information first.  The package provides a class
upy.undarray, which holds the nominal value and the uncertainty
information.  It has methods __add__(other), __radd__(other), ...,
__eq__(other), __ne__(other), which accept both upy.undarrays and all
other values suitable for coercion, thus also native numpy.ndarrays.
But because numpy treats in the statement:

result = numpyarray * upyarray

upyarray as a scalar, because it's not an numpy.ndarray, I have to
overload the numpy arithmetics by own objects by using
numpy.set_numeric_ops(add = ..., ..., equal = equal, not_equal =
not_equal).  The arguments are defined by the module (it will be
clearifiied below).

Because numpy.add etc. are ufuncs exhibiting attributes, I wrote a
class to wrap them:

class ufuncWrap:
        """Wraps numpy ufuncs.  Behaves like the original, with the exception
        that __call__() will be overloaded."""

        def __init__(self, ufunc, overload):
                """UFUNC is the ufunc to be wrapped.  OVERLOAD is the name 
(string)
                of the undarray method to be used in overloading __call__()."""

                self.ufunc = ufunc
                self.overload = overload

        def __call__(self, a, b, *args, **kwargs):
                """When B is an undarray, call B.overload(a), else .ufunc(a, 
b)."""

                if isinstance(b, undarray):
                        return getattr(b, self.overload)(a)
                else:
                        return self.ufunc(a, b, *args, **kwargs)

        def __getattr__(self, attr):
                """Return getattr(.ufunc, ATTR)."""

                return getattr(self.ufunc, attr)

I only have to wrap binary operators.

Then, e.g.:

class Equal(ufuncWrap):
        def __init__(self):
                ufuncWrap.__init__(self, numpy.equal, '__eq__')

equal = Equal()

This works as expected.

But this approach fails (in first iteration) for a similar class
NotEqual. I have let the module output the arguments passed to
ufuncWrap.__call__(), and I found that the statement:

result = (numpyarray != upyarray)

with:

numpyarray = numpy.asarray([1.0])
upyarray = upy.ndarray([2.0], error = [0.1])

is passed on to NotEqual.__call__() as the arguments:

a = a numpy-array array([1.0])
b = a numpy-array array(shape = (), dtype = numpy.object), which is a
scalar array holding the upy.ndarray instance passed to !=.

I can work around the exhbited behaviour by:

class NotEqual(ufuncWrap):
        def __init__(self):
                ufuncWrap.__init__(self, numpy.not_equal, '__ne__')

        def __call__(self, a, b, *args, **kwargs):
                # numpy's calling mechanism of not_equal() seems to have a bug,
                # such that b is always a numpy.ndarray.  When b should be an 
undarray,
                # it is a numpy.ndarray(dtype = numpy.object, shape = ()) ...

                # Make the call also compatible with future, bug-fixed versions.
                if isinstance(b, numpy.ndarray):
                        if b.ndim == 0:
                                # Implement some conversion from scalar array 
to stored object.
                                b = b.sum()
                
                return ufuncWrap.__call__(self, a, b, *args, **kwargs)

What is the reason for the behaviour observed?

I'm using numpy 1.4.0 with Python 2.5.

Friedrich
_______________________________________________
NumPy-Discussion mailing list
NumPy-Discussion@scipy.org
http://mail.scipy.org/mailman/listinfo/numpy-discussion

Reply via email to