5.6.3 : Wrapper of the matrices allocation

The purpose is very similar than the previous part, so let's begin :

The header is really simple :
1
2
3
4
5
6
7
8
9
10
#ifndef __ALLOCMATRIXWRAPPER_H__
#define __ALLOCMATRIXWRAPPER_H__

#include <Python.h>
#include "structmember.h"

PyObject * allocMatrix(long unsigned int nbRow, long unsigned int nbCol);
PyObject * allocMatrixWrapper(PyObject *self, PyObject *args);

#endif

Let's write the source file allocMatrixWrapper.cpp.

Again and again, in this example, since we are using numpy in the module, we have to define the NO_IMPORT_ARRAY to avoid multiple definitions of the same numpy function. And we also have to specify the version of the numpy API to avoid warnings :
1
2
3
4
5
6
#define NO_IMPORT_ARRAY
#ifndef DISABLE_COOL_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL core_ARRAY_API
#endif

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
Then we include appropriate files. Do not forget to include the asterics_alloc.h file to use our allocation function :
1
2
3
4
5
6
7
#include <Python.h>
#include <numpy/arrayobject.h>
#include <bytearrayobject.h>

#include "asterics_alloc.h"

#include "allocMatrixWrapper.h"
In this wrapper we will allocate our proper tables, so python does not know how to deallocate them too. For that, we have to define a deallocation function for our tables. This mechanism is called PyCapsule in python, you can find more documentation here.

In this function, we get a PyCapsule. Then, we ge the associated pointer by name and finally we deallocate it :

1
2
3
4
5
6
7
///Free the capsule memory
/**	@param obj : object with contains the capsule
*/
void empty_freeArray(PyObject* obj){
	float* ptr = (float*) PyCapsule_GetPointer(obj,"emptyMatrix");
	free(ptr);
}
Now, we have to implement the function which allocates numpy matrix :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
///Create a numpy matrix
/**	@param nbRow : number of rows of the matrix
 * 	@param nbCol : number of columns of the matrix
 * 	@return numpy array
*/
PyObject * allocMatrix(long unsigned int nbRow, long unsigned int nbCol){
	//Set the size of the numpy array
	npy_intp attr_size[2];
	attr_size[0] = nbRow;
	attr_size[1] = nbCol;
	
	float* mat = asterics_malloc2f(nbRow, nbCol);
	if(mat == NULL){
		PyObject* objMat = PyArray_EMPTY(2, attr_size, NPY_FLOAT32, 0);
		if(objMat == NULL){
			PyErr_SetString(PyExc_RuntimeError, "allocMatrix : Could not allocated memory\n");
			return NULL;
		}
		return objMat;
	}
	long unsigned int pitch(getPitch(nbCol));
	
	PyArray_Dims strides = {NULL, 0};
	strides.ptr = PyDimMem_NEW(2);
	strides.len = 2;
	PyArray_Descr *descr = PyArray_DescrFromType(NPY_FLOAT32);
	strides.ptr[1] = (npy_intp)sizeof(float);		     // Last strides is equal to element size
	strides.ptr[0] = (pitch + nbCol) *  strides.ptr[1];
	
	PyObject* objMat = PyArray_NewFromDescr(&PyArray_Type, descr, 2, attr_size, strides.ptr, (void *)mat, NPY_ARRAY_WRITEABLE, NULL);
	
	//Desalocation stuff
	PyObject* memory_capsule = PyCapsule_New(mat, "emptyMatrix", empty_freeArray);
	if(PyArray_SetBaseObject((PyArrayObject*)objMat, memory_capsule) < 0){
		PyErr_SetString(PyExc_RuntimeError, "Fail to create PyCapsule\n");
		return NULL;
	}
	return objMat;
}
To parse static parameters, we have to use the function PyArg_ParseTuple, this function wroks the same as the scanf function from the C standard library.

Once we have the desired size of the table (in number of elements), we call the allocMatrix function :

1
2
3
4
5
6
7
8
9
10
11
12
13
///Allocate an aligned matrix of float with a pitch
/**	@param self : pointer to the parent object if it exist
 * 	@param args : arguments passed to the program
 * 	@return allocated numpy array
*/
PyObject * allocMatrixWrapper(PyObject *self, PyObject *args){
	long unsigned int nbRow(0lu), nbCol(0lu);
	if(!PyArg_ParseTuple(args, "kk", &nbRow, &nbCol)){
		PyErr_SetString(PyExc_RuntimeError, "allocMatrixWrapper : wrong set of arguments. Expects two arguments for the matrix size\n");
		return NULL;
	}
	return allocMatrix(nbRow, nbCol);
}
The full allocMatrixWrapper.cpp file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/***************************************
	Auteur : Pierre Aubert
	Mail : aubertp7@gmail.com
	Licence : CeCILL-C
****************************************/

#define NO_IMPORT_ARRAY
#ifndef DISABLE_COOL_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL core_ARRAY_API
#endif

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <Python.h>
#include <numpy/arrayobject.h>
#include <bytearrayobject.h>

#include "asterics_alloc.h"

#include "allocMatrixWrapper.h"

///Free the capsule memory
/**	@param obj : object with contains the capsule
*/
void empty_freeArray(PyObject* obj){
	float* ptr = (float*) PyCapsule_GetPointer(obj,"emptyMatrix");
	free(ptr);
}

///Create a numpy matrix
/**	@param nbRow : number of rows of the matrix
 * 	@param nbCol : number of columns of the matrix
 * 	@return numpy array
*/
PyObject * allocMatrix(long unsigned int nbRow, long unsigned int nbCol){
	//Set the size of the numpy array
	npy_intp attr_size[2];
	attr_size[0] = nbRow;
	attr_size[1] = nbCol;
	
	float* mat = asterics_malloc2f(nbRow, nbCol);
	if(mat == NULL){
		PyObject* objMat = PyArray_EMPTY(2, attr_size, NPY_FLOAT32, 0);
		if(objMat == NULL){
			PyErr_SetString(PyExc_RuntimeError, "allocMatrix : Could not allocated memory\n");
			return NULL;
		}
		return objMat;
	}
	long unsigned int pitch(getPitch(nbCol));
	
	PyArray_Dims strides = {NULL, 0};
	strides.ptr = PyDimMem_NEW(2);
	strides.len = 2;
	PyArray_Descr *descr = PyArray_DescrFromType(NPY_FLOAT32);
	strides.ptr[1] = (npy_intp)sizeof(float);		     // Last strides is equal to element size
	strides.ptr[0] = (pitch + nbCol) *  strides.ptr[1];
	
	PyObject* objMat = PyArray_NewFromDescr(&PyArray_Type, descr, 2, attr_size, strides.ptr, (void *)mat, NPY_ARRAY_WRITEABLE, NULL);
	
	//Desalocation stuff
	PyObject* memory_capsule = PyCapsule_New(mat, "emptyMatrix", empty_freeArray);
	if(PyArray_SetBaseObject((PyArrayObject*)objMat, memory_capsule) < 0){
		PyErr_SetString(PyExc_RuntimeError, "Fail to create PyCapsule\n");
		return NULL;
	}
	return objMat;
}

///Allocate an aligned matrix of float with a pitch
/**	@param self : pointer to the parent object if it exist
 * 	@param args : arguments passed to the program
 * 	@return allocated numpy array
*/
PyObject * allocMatrixWrapper(PyObject *self, PyObject *args){
	long unsigned int nbRow(0lu), nbCol(0lu);
	if(!PyArg_ParseTuple(args, "kk", &nbRow, &nbCol)){
		PyErr_SetString(PyExc_RuntimeError, "allocMatrixWrapper : wrong set of arguments. Expects two arguments for the matrix size\n");
		return NULL;
	}
	return allocMatrix(nbRow, nbCol);
}
You can download it here.