These notes describe the Numeric compatability functions which enable numarray to utilize a subset of the extensions written for Numeric (NumPy). Not all Numeric C-API features and therefore not all Numeric extensions are currently supported. Users should be able to utilize suitable extensions written for Numeric within the numarray environment by:
Numarray's compatability with Numeric consists of 3 things:
The basic use of numarrays by Numeric extensions is achieved in the extension function's wrapper code by:
Unlike prior versions of numarray, this version *does* support access to array objects straight out of PyArg_ParseTuple. This is a consequence of a change to the underlying object model, where a class instance has been replaced by PyArrayObject. Nevertheless, the ``right'' way to access arrays is either via the high level interface or via emulated Numeric factory functions. That way, access to other python sequences is supported as well. Using the ``right'' way for numarray is also more important than for Numeric because numarray arrays may be byteswapped or misaligned and hence unusable from simple C-code. It should be noted that the numarray and Numeric are not completely compatible, and therefore this API does not provide support for string arrays or object arrays.
The creation of array objects is illustrated by the following of wrapper code for a 2D convolution function:
#include "python.h" #include "arrayobject.h" static PyObject * Py_Convolve2d(PyObject *obj, PyObject *args) { PyObject *okernel, *odata, *oconvolved=Py_None; PyArrayObject *kernel, *data, *convolved; if (!PyArg_ParseTuple(args, "OO|O", &okernel, &odata, &oconvolved)) { return PyErr_Format(_Error, "Convove2d: Invalid parameters."); goto _fail; }
The first step was simply to get object pointers to the numarray parameters to the convolution function: okernel, odata, and oconvolved. Oconvolved is an optional output parameter, specified with a default value of Py_None which is used when only 2 parameters are supplied at the python level. Each of the ``o'' parameters should be thought of as an arbitrary sequence object, not necessarily an array.
The next step is to call simulation functions which convert sequence objects into PyArrayObjects. In a Numeric extension, these calls map tuples and lists onto Numeric arrays and assert their dimensionality as 2D. The Numeric simulation functions first map tuples, lists, and misbehaved numarrays onto well-behaved numarrays. Calls to these functions transparently use the numarray high level interface and provide visibility only to aligned and non-byteswapped array objects.
kernel = (PyArrayObject *) PyArray_ContiguousFromObject( okernel, PyArray_DOUBLE, 2, 2); data = (PyArrayObject *) PyArray_ContiguousFromObject( odata, PyArray_DOUBLE, 2, 2); if (!kernel || !data) goto _fail;
Extra processing is required to handle the output array convolved, cloning it from data if it was not specified. Code should be supplied, but is not, to verify that convolved and data have the same shape.
if (convolved == Py_None) convolved = (PyArrayObject *) PyArray_FromDims( data->nd, data->dimensions, PyArray_DOUBLE); else convolved = (PyArrayObject *) PyArray_ContiguousFromObject( oconvolved, PyArray_DOUBLE, 2, 2); if (!convolved) goto _fail;
After converting all of the input paramters into PyArrayObjects, the actual convolution is performed by a seperate function. This could just as well be done inline:
Convolve2d(kernel, data, convolved);
After processing the arrays, they should be DECREF'ed or returned using PyArray_Return. It is generally not possible to directly return a numarray object using Py_BuildValue because the shadowing of mis-behaved arrays needs to be undone. Calling PyArray_Return destroys any temporary and passes the numarray back to Python.
Py_DECREF(kernel); Py_DECREF(data); if (convolved != Py_None) { Py_DECREF(convolved); Py_INCREF(Py_None); return Py_None; } else return PyArray_Return(convolved); _fail: Py_XDECREF(kernel); Py_XDECREF(data); Py_XDECREF(convolved); return NULL; }
Byteswapped or misaligned arrays are handled by a process of shadowing which works like this:
The following functions are currently implemented:
int nd, int *dims, int type) |
An array created with PyArray_FromDims can be used as a temporary or returned using PyArray_Return.
Used as a temporary, calling Py_DECREF deallocates it.
int nd, int *dims, int type, char *data) |
PyObject *op, int type, int min_dim, int max_dim) |
min_dim==max_dim
specifies an exact rank. min_dim==max_dim==0
specifies any rank.
PyObject *op, int type, int min_dim, int max_dim) |
PyObject *op, int type, int min_dim, int max_dim) |
If 'op' is a byteswapped or misaligned numarray, FromObject creates a temporary copy and the simulation object refers to it.
If 'op' is a nonswapped, aligned numarray, the simulation object refers to it.
If 'op' is some other sequence, it is converted to a numarray and the simulation object refers to that.
PyArrayObject *apr) |
An additional check is (or eventually will be) performed to guarantee that rank-0 arrays are converted to appropriate python scalars.
PyArray_Return has no net effect on the reference count of the underlying numarray.
PyObject **op, char **ptr, int *d1, int typecode) |
PyObject **op, char ***ptr, int *d1, int *d2, int typecode) |
PyObject *op, char *ptr) |
PyObject *op) |
PyObject *op) |
PyArrayObject *op) |
PyArrayObject *op) |
PyArrayObject *op, int type) |
PyArrayObject *op, int type) |
int type) |
T) |
Send comments to the NumArray community.