[Matrix-SIG] Re: blitz++ vs. NumPy?

Phil Austin phil@geog.ubc.ca
09 Dec 1998 10:24:32 -0800


The following message is a courtesy copy of an article
that has been posted as well.



posting to comp.lang.python, henk13@my-dejanews.com writes:

> 
> I just wondered what the advantages and disadvantages would be of doing
> Python numerical computiation in a Blitz++ (http://seurat.uwaterloo.ca/blitz)
> framework rather than NumPy's Lapack/Blas framework (this is _not_ meant to
> offend the people at LLNL). The inherint OO-design of Blitz nicely extends
> the mental concept in which Python programs are developed. For instance, the
> Array class could be straightforwardly mapped to a Python Array extension
> type.
> 
> Is this some utopia or does it make sense?
> 
> Comments are welcome!
> 

Yes, the fit you describe is very close to completion thanks to Paul
Dubois,Todd Veldhuizen, Geoff Furnish and others.  Here's an example
of a Python extension I wrote in about 20 minutes last night, which
calculates the 2nd order structure function for a 256 x 256 pixel
satellite image of cloud liquid water path.  It uses Paul's CXX_Array.h
(see the source, http://xfiles.llnl.gov/CXX_Objects/cxx.htm and
particularly the last pages of
http://starship.skyport.net/~da/freebie). I've added a constructor
that creates an n-dimensional PyArray from dimensions specified by a
Blitz 1-d array.

Blitz arrays are reference-counted, and Todd's provided a constructor
that takes a raw C pointer (in this case to the PyArray data).
I can mirror the PyArrays with Blitz arrays of the
same dimensions, denoted by the "tied" prefix below and get the
template metaprograms, 2-dimensional subscripts etc., without paying
for a copy.  I return the structure function and the pixel counts to
Python in a dictionary.  The program strucfun.cxx below is compiled into
strucfun.so and then called from Python like this:

from Numeric import *
import strucfun

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


Some comments/questions:

1)  From my perspective the combination of Blitz and CXX_Objects pushes
NumPy across a threshold: extension writing has become something that
Fortran77 and Matlab programming scientists (like me) can do.  

2) Two things I wasn't able to figure out in 20 minutes are:

  a) How to add my Blitz-based Py::Array constructor to Paul's code
     using inheritence.  It looks like:

    explicit Array (blitz::Array<int,1> dimens, PyArray_TYPES t = PyArray_DOUBLE)
        : Sequence(FromAPI(PyArray_FromDims(dimens.size(), dimens.data(), t))) {
        validate();
    }

  b) Is there a general way to convert between a Blitz TinyVector of
     length N (where N isn't known at compile time)
     and a blitz::Array<int,1>?  I'd like to avoid specifying the
     dimensions to the Py::Array and the blitz::Array separately.

3) This worked the first time I tried it; everyone responsible for that
little miracle has my heartfelt thanks.

__________strucfun.cxx_____________

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

USING(namespace Py)
USING(namespace std)

blitz::Array<int,1> convertit( blitz::TinyVector<int,2> tinyDimens)
{
  blitz::Array<int,1> junk(2);
  for(int j=0;j < tinyDimens.length();++j){
    junk(j)=tinyDimens(j);
  }
  return junk;
}


static PyObject *
ex_strucout(PyObject* self, PyObject* args)
{
  Py_Initialize();
  import_array();
  PyImport_ImportModule("Numeric");

  Tuple t(args);
  Array tau(t[0]);
//
// change these to exceptions later
//
  assert(tau.rank()==2);
  assert(tau.dimension(1)==tau.dimension(2));
  assert(tau.species()==6);
  int N=tau.dimension(1);
  int Nbig=2*N-1;
  blitz::TinyVector<int,2> tinyDimens(Nbig,Nbig);
  Array sf(convertit(tinyDimens),PyArray_FLOAT);
  Array count(convertit(tinyDimens),PyArray_FLOAT);
  // make blitz arrays that point to the same data and have the
  // same shape
  blitz::Array<float,2> tiedTau((float*) sf.to_C(),tinyDimens);
  blitz::Array<float,2> tiedSf((float*) sf.to_C(),tinyDimens);
  blitz::Array<float,2> tiedCount((float*) count.to_C(),tinyDimens);
  tiedSf=0.;
  tiedCount=0.;

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


extern "C" void initstrucfun();

static ExtensionModule* strucfun;

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






-- 
------------------------------------------------------------
Phil Austin  (phil@geog.ubc.ca)
Department of Geography,                 Tel: (604) 822-2663
University of British Columbia, B.C.     Fax: (604) 822-6150