numpy and extending python with c
Tom Loredo
loredo at astro.cornell.edu
Fri Jan 11 19:07:38 EST 2002
Les-
If you look in the NumPy documentation, there is a lot of
info on how to do this. As a simple example, the module
below shows how to return a vector and a matrix:
>>> from mkarray import *
>>> vec3()
array([1.0, 2.0, 3.0], 'd')
>>> mat3()
array([[0.0, 1.0, 2.0], [10.0, 11.0, 12.0], [20.0, 21.0, 22.0]], 'd')
>>>
Note the use of the DDATA macro, which I stole from some of
the NumPy code.
Good luck!
Tom Loredo
--------- mkarraymodule.c -----------
/*******************************************************************************
* mkarray: "MaKe ARRAY"
*
* Simple functions for learning how to create NumPy arrays and set
* their contents in C.
*
* Created: 07 Apr 2000 Tom Loredo
* Last mod: 11 May 2000 Cleanup error handling
*******************************************************************************/
/*******************************************************************************
* Headers, prototypes, and macros.
*******************************************************************************/
/* Include Python headers; needed for *all* extensions. */
#include "Python.h"
/* Other includes needed for this extension. */
#include "arrayobject.h" /* Header needed to use Numeric array types. */
/* We need the math standard C library; but according
to the NumPy folks, for the Mac we have to wrap it up with a bit of
protection; this is done in "mymath.h" which is in the NumPy include
directory. This may only be needed for CFM68k builds. */
#ifdef macintosh
#include "mymath.h"
#else
#include <math.h>
#endif
/* Prototypes for functions defined and used in this extension file that aren't
taken care of in an include file. */
/* None are needed for this example. */
/* Error handler macro for use in any extension; adapted from Dubois & Yang. */
#define Py_TRY(BOOLEAN) {if (!(BOOLEAN)) goto FAIL;}
/* Macro to cast NumPy array data to a 1-d double array. */
#define DDATA(p) ((double *) (((PyArrayObject *)p)->data))
/*------------------------------------------------------------------------------
* Some handy error handling functions, from lapack_litemodule.c in NumPy.
------------------------------------------------------------------------------*/
static PyObject *ErrorObject;
static PyObject *mkarrayError()
{ if (! ErrorObject)
ErrorObject = PyString_FromString("mkarrayError");
Py_INCREF(ErrorObject);
return ErrorObject;
}
static PyObject *ErrorReturn(char *mes)
{ if (!ErrorObject)
ErrorObject = PyString_FromString("mkarrayError");
PyErr_SetString(ErrorObject,mes);
return NULL;
}
#define TRY(E) if( ! (E)) return NULL
static int NumPy_CheckObject(PyObject *ob, int t, char *obname,
char *tname, char *funname)
{ char buf[255];
if (! PyArray_Check(ob))
{ sprintf(buf,"Expected an array for parameter %s in %s",obname, funname);
ErrorReturn(buf);
return 0;
}
if (!(((PyArrayObject *)ob)->flags & CONTIGUOUS))
{ sprintf(buf,"Parameter %s is not contiguous in %s",obname, funname);
ErrorReturn(buf);
return 0;
}
if (!(((PyArrayObject *)ob)->descr->type_num == t))
{ sprintf(buf,"Parameter %s is not of type %s in %s",obname,tname, funname);
ErrorReturn(buf);
return 0;
}
return 1;
}
/*------------------------------------------------------------------------------
* Make a new 2D double array, and provide a reference to its data that can
* be used as a 2D C array (i.e., pointers to pointers).
*
* Declare the reference as "double **data" and pass it as "&data".
* When done with it, free its memory with "free(*data)".
*
* This is patterned after PyArray_As2D in "arrayobject.c" in the NumPy sources.
------------------------------------------------------------------------------*/
static int
PyArray_New2DD(int n1, int n2, PyArrayObject **app, double ***data)
{
int dims[2], i;
PyArrayObject *ap;
/** Make a new (thus contiguous) 2D array. */
dims[0] = n1;
dims[1] = n2;
ap = (PyArrayObject *)PyArray_FromDims(2, dims, PyArray_DOUBLE);
if (ap == NULL) return -1;
/** Make a 1D array of pointers to each row. */
*data = (double **)malloc(n1*sizeof(double *));
for(i=0; i<n1; i++) {
*data[i] = (double *) (ap->data + i*ap->strides[0]);
}
/** Make the correct cast so "data" is a 2D C array. */
*app = ap;
return 0;
}
/* ptrs = (double **)malloc(n1*sizeof(double *));
for(i=0; i<n1; i++) {
ptrs[i] = (double *) (ap->data + i*ap->strides[0]);
}
*data = ptrs;
*app = ap;
*/
/*******************************************************************************
* Methods for this module.
*
* The functions that will actually be called by Python to implement the methods
* are defined here, as well as any global variables they may use to communicate
* with each other and any other "helper" functions not defined elsewhere. The
* functions that will actually be called by Python must return PyObject
* pointers.
*
* Be sure to define a documentation string for each method.
*
* The functions and global variables here should all be static (i.e., accessible
* only from within this module) to prevent side effects from name collisions
* outside this extension. They can have any names (the names Python will
* use to access them are defined separately, below); they here follow a
* convention that should be self-explanatory.
*******************************************************************************/
/*------------------------------------------------------------------------------
* Return a 3-vector of doubles (Python floats).
------------------------------------------------------------------------------*/
static char mkarray_vec3_doc[] =
"vec3(): Return a filled 3-vector.";
static PyObject *
mkarray_vec3 (PyObject *self, PyObject *args) {
int dims[1];
PyArrayObject *result;
double *data;
/** Create a 3-vector for the result. */
dims[0] = 3;
/* printf("creating vector of size %d...\n",dims[0]); */
result = (PyArrayObject *)PyArray_FromDims(1, dims, PyArray_DOUBLE);
if (result == NULL) return NULL;
/* printf("vector created\n"); */
/** Assign values to its 3 elements. */
if (NumPy_CheckObject((PyObject *)result, PyArray_DOUBLE, "result", "PyArray_DOUBLE", "vec3") ==
0) goto FAIL;
data = DDATA(result);
data[0] = 1.;
data[1] = 2.;
data[2] = 3.;
/** Return a NumPy array. */
return PyArray_Return(result);
/** Misbehavior ends up here! */
FAIL:
Py_XDECREF(result);
return NULL;
}
/*------------------------------------------------------------------------------
* Return a 3x3 matrix of doubles (Python floats).
------------------------------------------------------------------------------*/
static char mkarray_mat3_doc[] =
"mat3(): Return a filled 3x3 matrix.";
static PyObject *
mkarray_mat3 (PyObject *self, PyObject *args) {
int dims[2] = {3, 3};
int i, j;
PyArrayObject *result;
double *data;
/** Create a 3x3 matrix for the result. */
result = (PyArrayObject *)PyArray_FromDims(2, dims, PyArray_DOUBLE);
if (result == NULL) goto FAIL;
/** Assign values to its 3 elements. */
if (NumPy_CheckObject((PyObject *)result, PyArray_DOUBLE, "result", "PyArray_DOUBLE", "mat3") ==
0) goto FAIL;
data = DDATA(result);
for (i=0; i<3; i++) {
for (j=0; j<3; j++) {
*(data+i*3+j) = 10*i + j;
}
}
/** Return a NumPy array. */
return PyArray_Return(result);
/** Misbehavior ends up here! */
FAIL:
Py_XDECREF(result);
return NULL;
}
/*******************************************************************************
* The methods table defining how Python accesses this module's methods. This
* must have an element for each new method and end with the "sentinel" element.
* It should have 4 entries for each method (except the sentinel):
* {call name, function to call, argument type, doc string},
* with each entry ending with a comma.
*******************************************************************************/
static PyMethodDef methods[] = {
{"vec3", mkarray_vec3, METH_VARARGS, mkarray_vec3_doc},
{"mat3", mkarray_mat3, METH_VARARGS, mkarray_mat3_doc},
{NULL, NULL, 0} /* sentinel */
};
/*******************************************************************************
* The initialization function---it must be named for the module ("init" followed
* by the module name).
*
* This should be the only non-static global object in this file.
*******************************************************************************/
void initmkarray() {
PyObject *m;
/* Create the module and add the functions */
m = Py_InitModule("mkarray", methods);
import_array();
/*
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module mkarray");
}
More information about the Python-list
mailing list