hildensia
hildensia

Reputation: 1740

How to create a wrapper for a Matrix class to Numpy with SWIG?

I have an old and grown C++ library containing a Matrix class and a whole lot of code using it. It is basically

class Matrix {
  double* p;      // the actual data
  int nd;         // number of dimensions
  int d0, d1, d2; // the actual dimensionality

  // ... (a whole lot of functions computing various things, like SVDs, dotproduct etc.
}

Now we write a python wrapper, using SWIG. We want to use NumPy arrays on the python side to keep compatible with the rest of the world. So we actually don't need the functionality of our C++ Matrix class, but we want to use some other parts of our library, which expects this C++ Matrix. So the perfect situation would be, if we could write a typemap from a NumPy array to our Matrix class, which transparently converts a NumPy array on every call and keeps the memory in sync. Let's say we have some function in our library, which is swigged:

int some_function(Matrix& in) { /* do some stuff */ }

Now it would be great if in python we could do something like:

a = numpy.array[1,2,3,4]
b = some_function(a)

I understand that there is numpy.i, but that seems to be more about function mapping and plain old C arrays. I also understand that a typemap should acomplish what I want, but I don't really understand how I can actually access the numpy data. Is there any (relatively) easy way to do that?

I would also appreciate a pointer to some tutorial.

Upvotes: 2

Views: 784

Answers (1)

Oliver
Oliver

Reputation: 29591

Based on the info you provided, typemaps would work. But my experience as an intermittent SWIG user (typically a couple weeks where I use it a lot then hiatus till next project/phase) is that few people have the time to grok that feature.

In your case I believe SWIG typemaps are more of a convenience than a requirement so I would use one of two approaches:

  1. create a Python function that does the conversion from numpy.array to Matrix (Matrix is the one exported from C++ to Python by SWIG)
  2. use the builtin numpy.i typemap to call a C++ function that accepts a C++ type in that typemap, and define that function to call int some_function(Matrix& in)

The advantage of option 1 is that you can automate the transformation by rebinding the Python function:

old_some_func = some_function
def some_function(numpy_array):
    tempMat = Matrix()
    # convert numpy_array to SWIG'd Matrix class
    old_some_func(tempMat)

The performance hit of doing this is likely to be negligible, but you should test. Without SWIG (i.e. if you were using the C API) this technique would have the additional advantage of not requiring you to change your C++ library (see the extend directive of SWIG).

The advantage of option 2 is that the conversion is at the C/C++ level, so depending on what's involved, you may get improved performance. Say one of the numpy.i typemaps maps numpy.array to float[numValues] array, and your C++ Matrix holds single precision floating point values. In that case you define a C++ some_function(float*, numValues) in the .i file for your project, and that function calls some_function(Matrix). Check whether your C++ Matrix class can store a pointer to array data, this way you will avoid copying data across one and maybe even two layers (Python -> SWIG some_function(array) -> some_function(Matrix)).

But keep in mind: your computations inside your C++ some_function may make any performance difference between the two options insignificant. You have to test. Then go with the one that is easiest and most maintainable (probably option 1).

Upvotes: 1

Related Questions