[Matrix-SIG] numpy interface

Phil Austin phil@geog.ubc.ca
Tue, 20 Apr 1999 10:48:54 -0700


Christian Tismer writes:
 > 
 > 
 > I don't believe that you need SWIG to call NumPy from C, since
 > NumPy's C API can be used like Python's C API.

Right -- and you might also want to have a look at Paul Dubois'
CXX_Array.h (http://xfiles.llnl.gov/CXX_Objects/cxx.htm), which
provides a very clean C++ interface to PyArrays (albeit with 1-d
subscripting only), dicts, tupples, etc.  

From the C++ side, your code would look something
like this:

Py::Array count(pyob);   //construct a 1-d array Pn with pyob's data
Py::Dict output;  //construct a C++ version of a Python dictionary
output["count"]=count; //put the count array in the output dictionary
return Py::new_reference_to(output);  //send the dictionary with the 
                                      // count array back to Python

To get around the 1-d subscripting limitation I use a class I
call TiedArray, which creates a Py::Array and a Blitz++ array
(http://monet.uwaterloo.ca/blitz)
that share the same data.  I can then use Blitz notation on the
C++ side, while easily shipping the data back to Python.  

Here's an example I posted about 4 months ago which calculates
something called the 2 dimensional isotropic 
2nd order structure function for a
satellite image.  I've modified the Py::Array constructor
to support Blitz TinyVectors -- I'm happy to send you this
two-line modification if you're a Blitz user.


#include "Python.h"
#include "CXX_Objects.h"
#include "CXX_Extensions.h"
#include <blitz/array.h>
#include "CXX_Array.h"
#include <math.h>



template<typename T,int Ndim> class TiedArray;


template<> class TiedArray<float,2> 
//take an existing PyObject or a N-length TinyVector and
//construct an N-dimensional array shared by Python and Blitz.
//Note that the Python array controls the memory deallocation;
//the Blitz array just holds a reference to the Pn's data
{
public:
  Py::Array Pn;
  blitz::Array<float,2> Bz;
  TiedArray(blitz::TinyVector<int,2> dimens):Pn(dimens,PyArray_FLOAT) //added to CXX_Array by PA: 98/12/30
    {
      blitz::Array<float,2> tempArray((float*) Pn.to_C(),dimens);
      Bz.reference(tempArray);
    };  
  TiedArray(Py::seqref<Py::Object> pyob):Pn(pyob)
    {
      assert(Pn.rank()==2);
      assert(Pn.species()==PyArray_FLOAT);
      blitz::Array<float,2> tempArray((float*) Pn.to_C(),blitz::shape(Pn.dimension(1),
	      Pn.dimension(2)));
      Bz.reference(tempArray);
    };
};


static PyObject *
ex_strucout(PyObject* self, PyObject* args)
//
// calculate the 2nd order structure function given
// a 2-d NumPy array of floats
//
{
  Py_Initialize();
  import_array();
  PyImport_ImportModule("Numeric");

  Py::Tuple t(args);
  TiedArray<float,2> tau(t[0]);
  assert(tau.Pn.dimension(1)==tau.Pn.dimension(2));
  int N=tau.Pn.dimension(1);
  int Nbig=2*N-1;
  blitz::TinyVector<int,2> tinyDimens(Nbig,Nbig);
  TiedArray<float,2> sf(tinyDimens);
  TiedArray<float,2> count(tinyDimens);

  sf.Bz=0.;
  count.Bz=0.;

  for(int i=0;i<N;i++){
    for(int j=0;j<N;j++){
      if(!isnan(tau.Bz(i,j)))
	{
	  for(int k=0;k<N;k++){
	    for(int l=0;l<N;l++){
	      if(!isnan(tau.Bz(k,l)))
		{
		  sf.Bz(k-i+N,l-j+N)=sf.Bz(k-i+N,l-j+N) + pow((tau.Bz(k,l) - tau.Bz(i,j)),2);
		  count.Bz(k-i+N,l-j+N)=count.Bz(k-i+N,l-j+N) + 1.;
		}
	    }
	  }
	}
    }
  }
  for (int i=0; i<Nbig; i++) {
    for (int j=0; j<Nbig; j++) {
      if(count.Bz(i,j) > 0.){
	sf.Bz(i,j)=sf.Bz(i,j)/count.Bz(i,j);
      }
    }
  }
  Py::Dict output;
  output["count"]=count.Pn;
  output["struct"]=sf.Pn;
  return Py::new_reference_to(output);
}


extern "C" void initstrucfun();

static Py::ExtensionModule* strucfun;

void initstrucfun()
{
    // experimental initialization stuff
    strucfun = new Py::ExtensionModule("strucfun");
    strucfun->add("strucout", ex_strucout, "calculate the structure function");
    Py::Dict d = strucfun->initialize();
}

___________compile above into strucfun.so and use this from python

from Numeric import *
import strucfun

test=arange(4,typecode=Float32)
test.shape=(2,2)
print strucfun.strucout(test)