Creating a new universal function

The umath module is a computer-generated C-module that creates many ufuncs. It provides a great many examples of how to create a universal function. Creating your own ufunc that will make use of the ufunc machinery is not difficult either. Suppose you have a function that you want to operate element-by-element over its inputs. By creating a new ufunc you will obtain a function that handles

• broadcasting

• N-dimensional looping

• automatic type-conversions with minimal memory usage

• optional output arrays

It is not difficult to create your own ufunc. All that is required is a 1-d loop for each data-type you want to support. Each 1-d loop must have a specific signature, and only ufuncs for fixed-size data-types can be used. The function call used to create a new ufunc to work on built-in data-types is given below. A different mechanism is used to register ufuncs for user-defined data-types.

PyUFunc_FromFuncAndData (PyObject*) (PyUFuncGenericFunction* func, void** data, char* types, int ntypes, int nin, int nout, int identity, char* name, char* doc, int check_return)

func A pointer to an array of 1-d functions to use. This array must be at least ntypes long. Each entry in the array must be a PyUFuncGenericFunction function. This function has the following signature. An example of a valid 1d loop function is also given. void loopld (char** args, npy_intp* dimensions, npy_intp* steps, void* data)

args An array of pointers to the actual data for the input and output arrays. The input arguments are given first followed by the output arguments.

dimensions A pointer to the size of the dimension over which this function is looping.

steps A pointer to the number of bytes to jump to get to the next element in this dimension for each of the input and output arguments. data Arbitrary data (extra arguments, function names, etc.) that can be stored with the ufunc and will be passed in when it is called.

static void double.add (char *args, npy.intp *dimensions, npy.intp *steps, voi {

npy_intp i;

npy_intp isl = steps [0] , is2 = steps [1] ; npy_intp os = steps [2] , n=dimensions[0] ; char *i1=args[0], *i2=args[1], *op=args[2]; for (i = 0; i<n; i + +) {

data An array of data. There should be ntypes entries (or NULL) — one for every loop function defined for this ufunc. This data will be passed in to the 1-d loop. One common use of this data variable is to pass in an actual function to call to compute the result when a generic 1-d loop (e.g. PyUFUnc_d_d) is being used.

types An array of type-number signatures (type char). This array should be of size (nin+nout)*ntypes and contain the data-types for the corre sponding 1-d loop. The inputs should be first followed by the outputs. For example, suppose I have a ufunc that supports 1 integer and 1 double 1-d loop (length-2 func and data arrays) that takes 2 inputs and returns 1 output that is always a complex double, then the types array would be char my_sigs [] = \



The bit-width names can also be used (e.g. NPY.INT32, NPY.COMPLEX12 8) if desired.

ntypes The number of data-types supported. This is equal to the number of 1-d loops provided.

nin The number of input arguments.

nout The number of output arguments.

identity Either PyUFunc_One, PyUFunc_Zero, PyUFunc_None. This specifies what should be returned when an empty array is passed to the reduce method of the ufunc.

name A NULL-terminated string providing the name of this ufunc (should be the Python name it will be called).

doc A documentation string for this ufunc (will be used in generating the response to <ufunc_name>._doc__). Do not include the function signature or the name as this is generated automatically.

check_return Not presently used, but this integer value does get set in the structure-member of similar name.

The returned ufunc object is a callable Python object. It should be placed in a (module) dictionary under the same name as was used in the name argument to the ufunc-creation routine. The following example is adapted from the umath module:

static PyUFuncGenericFunction atan2_functions[]=\ {PyUFunc_f f _f , PyUFunc_dd_d, PyUFunc_gg_g, PyUFunc_00_0_method} ;

{(void *)atan2f,(void *) atan2, (void *)atan2l,(void *)"arctan2"};

static char atan2_signatures[]=\


/* in the module initialization code */ PyObject *f, *dict, *module;

diet = PyModule.GetDict(module);

f = PyUFunc-FromFuncAndData (atan2_functions , atan2_data, atan2_signatures, 4, 2, 1, PyUFunc _None, "arctan2", "a safe and correct arctan(x1/x2)", 0); PyDict_SetItemString(diet, "arctan2", f) ; Py_DECREF (f) ;

Was this article helpful?

0 0

Post a comment