Creating an array squared function using NumPy C-API

In this section, we will create a function to square all the values of the NumPy Array. The aim here is to demonstrate how to get a NumPy Array in C and then iterate over it. In a real-world scenario, this can be done in an easier way using a map or by vectorizing a square function. We are using the same PyArg_ParseTuple function with the O! format string. This format string has a (object) [typeobject, PyObject *] signature and takes the Python type object as the first argument. Users should go through the official API doc to take a look at what other format strings are permissible and which one suits their needs:

Note

If the passed value does not have the same type, then a TypeError is raised.

The following code snippet explain how to parse the argument using PyArg_ParseTuple.

// Implementation of square of numpy array 
 
static PyObject* square_nparray_func(PyObject* self, PyObject* args) 
{ 
 
// variable declarations 
PyArrayObject *in_array; 
PyObject      *out_array; 
NpyIter *in_iter; 
NpyIter *out_iter; 
NpyIter_IterNextFunc *in_iternext; 
NpyIter_IterNextFunc *out_iternext; 
 
// Parse the argument tuple by specifying type "object" and putting the reference in in_array 
if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &in_array)) 
return NULL; 
...... 
...... 

The next step is to create an array to store its output value and iterators in order to iterate on Numpy Arrays. Note that there is a {handle failure} code at each step when we create an object. This is to ensure that, if anything goes wrong, we can pinpoint the location of the faulty code via debugging:

//Construct the output from the new constructed input array 
out_array = PyArray_NewLikeArray(in_array, NPY_ANYORDER, NULL, 0); 
// Test it and if the input is nothing then just return nothing. 
{handle failure} 
 
//  Create the iterators 
in_iter = NpyIter_New(in_array, NPY_ITER_READONLY, NPY_KEEPORDER, 
NPY_NO_CASTING, NULL); 
 
// {handle failure} 
 
out_iter = NpyIter_New((PyArrayObject *)out_array, NPY_ITER_READWRITE, 
NPY_KEEPORDER, NPY_NO_CASTING, NULL); 
{handle failure} 
 
in_iternext = NpyIter_GetIterNext(in_iter, NULL); 
out_iternext = NpyIter_GetIterNext(out_iter, NULL); 
{handle failure} 
 
double ** in_dataptr = (double **) NpyIter_GetDataPtrArray(in_iter); 
double ** out_dataptr = (double **) NpyIter_GetDataPtrArray(out_iter); 
 
A simple handle failure module is like 
// {Start handling failure} 
if (in_iter == NULL) 
// remove the ref and return null 
Py_XDECREF(out_array); 
return NULL; 
// {End handling failure} 

After taking a look at the preceding boilerplate code, we finally come to the part where all the real action takes place. Those of you who are familiar with C++ will find the method of iteration to be similar to the iteration done over vectors. The in_iternext function that we have defined previously comes in handy here and is used to iterate over the Numpy Array. After our while loop, we make sure that we call NpyIter_Deallocate on both iterators and Py_INCREF on the output array; failing to call these functions is the most common type of mistake that causes memory leaks. Memory leak problems are mostly quite subtle and normally make an appearance when you have a long-running code (such as services or a daemon). To catch these, there is unfortunately no easier way than using a debugger and looking deeper. Sometimes, it helps to just write a couple of printf statements, which output the total memory usage:

/*  iterate over the arrays */ 
do { 
**out_dataptr =pow(**in_dataptr,2); 
} while(in_iternext(in_iter) && out_iternext(out_iter)); 
 
/*  clean up and return the result */ 
NpyIter_Deallocate(in_iter); 
NpyIter_Deallocate(out_iter); 
Py_INCREF(out_array); 
return out_array; 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.19.185