numpy/numpy — pull request #462 by jayvius (2012-09-27 18:23:35+00:00)
Title: Create ufunc for struct array

This change allows ufuncs to be registered for structured arrays. For example, a ufunc could be registered to take two arrays
of type 'u8,u8,u8' and return an array of type 'u8,u8,u8'.

The following are the key changes:

1) Add new API method called PyUFunc_RegisterLoopForStructType. This method behaves the same as PyUFunc_RegisterLoopForType,
except that instead of taking an array of type nums for the ufunc operands, it takes an array of struct dtype objects.

2) Add arg_dtypes array to PyUFunc_Loop1d struct. This stores the dtype objects passed in through call to RegisterLoopForStructType.

3) Add nargs int value to PyUFunc_Loop1d struct. This stores the number of input and output arguments to ufunc, so that dtype objects
in PyUFunc_Loop1d struct can be DECREFed when PyUFUnc_Loop1d struct is destroyed.

4) Add logic in ufunc_loop_matches function that checks struct dtype object in arg_dtypes if current operand being checked is type NPY_VOID.

The following is a trivial 1-d loop function example written in Cython that adds corresponding fields in two arrays of type 'u8,u8,u8':

```
cdef void add_uint64_triplet(char **args,
npy_intp *dimensions,
npy_intp *steps,
void *innerloopdata):

cdef npy_intp i
cdef npy_intp is1=steps[0]
cdef npy_intp is2=steps[1]
cdef npy_intp os=steps[2]
cdef npy_intp n=dimensions[0]
cdef uint64_t *x, *y, *z

cdef char *i1=args[0], *i2=args[1], *op=args[2]

for i in range(n):

# Initialize pointers to each operand
x = i1
y = i2
z = op

# Add each field in struct array record
z[0] = x[0] + y[0]
z[1] = x[1] + y[1]
z[2] = x[2] + y[2]

i1 += is1; i2 += is2; op += os

def test_ufunc:
cdef PyArray_Descr *types[3]
dtype = np.dtype('u8,u8,u8')

types[0] = dtype
types[1] = dtype
types[2] = dtype

ufunc = PyUFunc_FromFuncAndData(NULL, NULL, NULL, 0,
2, 1, PyUFunc_None,
NULL, NULL, 0)

# Call new method for registering ufunc for struct array
PyUFunc_RegisterLoopForStructType(ufunc,
dtype,
&add_uint64_triplet,
types,
NULL)

a = np.array([(1,2,3)], dtype=dtype)
b = np.array([(1,2,3)], dtype=dtype)

result = ufunc(a, b)
```