[MATRIX-SIG] Making pseudo-ufuncs?

Johann Hibschman johann@physics.berkeley.edu
Thu, 11 Sep 1997 14:12:43 -0700 (PDT)


Hi all,

I've been trying to write a class that wraps a normal unary or binary
function to allow it to be simply (if slowly) applied to arrays.
Hopefully, it would increase the readibility of some code.

First, is there a better way to do this?

Second, is the method I use to detect the types of the arrays a good idea? 
It would seem to fall apart for special Matrix and Vector classes.

Third, is there a good way to implement outer, accumulate, and reduce for
binary functions?

So far, I have (FuncOps is my function operations class):

class FuncBinder(FuncOps):
    def __init__(self, a_f):
	if ((type(a_f) == UfuncType) or
	    (type(a_f) == InstanceType and
	     FuncOps in a_f.__class__.__bases__)):
	    self.__call__ = a_f        # overwrite the existing method
	self.f = a_f

    def __call__(self, arg):
	"Default call routine, used for ordinary functions."
	if type(arg) == ArrayType:
	    return array_map(self.f, arg)
	else:
	    return self.f(arg)

where

def array_map(f, ar):
    "Apply an ordinary function to all values in an array."
    flat_ar = ravel(ar)
    out = zeros(len(flat_ar), typecode=flat_ar.typecode())
    for i in xrange(len(flat_ar)):
	out[i] = f(flat_ar[i])
    out.shape = ar.shape
    return out

for unary functions, and (for binary functions), I've defined methods:

def reduce(self, a, axis=0):
    result = take(a, [0], axis)
    for i in range(1, a.shape[axis]):
	result = self(result, take(a, [i], axis))
    return result

def accumulate(self, a, axis=0):
    n = len(a.shape)
    sum = take(a, [0], axis)
    out = zeros(a.shape, a.typecode())
    for i in range(1, a.shape[axis]):
	out[all_but_axis(i, axis, n)] = self(sum, take(a, [i], axis))
    return out

def all_but_axis(i, axis, num_axes):
    """
    Return a slice covering all combinations with coordinate i along
    axis.  (Effectively the hyperplane perpendicular to axis at i.)
    """
    the_slice  = ()
    for j in range(num_axes):
	if j == axis:
	    the_slice = the_slice + (i,)
	else:
	    the_slice = the_slice + (slice(None),)
    return the_slice

def outer(op, a, b):
    n_a = len(a.shape)
    n_b = len(b.shape)
    a2  = reshape(a, a.shape + (1,)*n_b)
    b2  = reshape(b, (1,)*n_a + b.shape)
    
    # duplicate each array in the appropriate directions
    a3  = a2
    for i in range(n_b):
	a3 = repeat(a3, (b.shape[i],), n_a+i)
    b3 = b2
    for i in range(n_a):
	b3 = repeat(b3, (a.shape[i],), i)

    answer = array_map_2(op, a3, b3)
    return answer

These all seem over-complex to me.  Is there are more straightforward way
to do this?

---
Johann A. Hibschman         | Grad student in Physics, working in Astronomy.
johann@physics.berkeley.edu | Probing pulsar pair production processes.


_______________
MATRIX-SIG  - SIG on Matrix Math for Python

send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
_______________