Well you can with a little gem called numpy.i which is a SWIG interface wrapper for the numpy array. With it, you specify certain arguments you want to be treated as numpy arrays and it does so automatically.
So on with the example. I have two C functions which fill an array sequentially and can take the dot product of two arrays. They are found below in two files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
double dot(int n, double *a, int m, double *b){ | |
double sum = 0.0; | |
for (int i=0; i<n; i++){ | |
sum += a[i]*b[i]; | |
} | |
return sum; | |
} | |
void create_list(int size, double *arr){ | |
for (int i=0; i<size; i++) | |
arr[i] = i; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
double dot(int n, double *a, int m, double *b); | |
void create_list(int size, double *arr); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%module simple | |
%{ | |
#define SWIG_FILE_WITH_INIT | |
#include "simple.h" | |
%} | |
%include "numpy.i" | |
%init %{ | |
import_array(); | |
%} | |
%apply (int DIM1, double* INPLACE_ARRAY1) {(int n0, double *a0)}; | |
%apply (int DIM1, double* IN_ARRAY1) {(int n, double *a), (int m, double *b)}; | |
%apply (int DIM1, double* ARGOUT_ARRAY1) {(int size, double *arr)}; | |
%include "simple.h" |
Then, we need to build the module and test. To make this module we used a setup.py script (which will not be the case for future SWIG posts). I'm not sure of the details here, but it is listed for your information.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from distutils.core import setup, Extension | |
import numpy | |
import os | |
os.environ['CC'] = 'g++'; | |
setup(name='matt_simple_test', version='1.0', ext_modules =[Extension('_simple', | |
['simple.cc', 'simple.i'], include_dirs = [numpy.get_include(),'.'])]) |
After that, let's build with `python setup.py build` and run some speed tests. For example,
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import simple | |
import numpy | |
from time import time | |
a = simple.create_list(1000000) | |
numpy_start = time() | |
print "Numpy says:", numpy.dot(a,a) | |
numpy_end = time() | |
swig_start = time() | |
print "SWIG says:", simple.dot(a,a) | |
swig_end = time() | |
print "Time of numpy:", (numpy_end-numpy_start) | |
print "Time of SWIG:", (swig_end-swig_start) |
Returns
Numpy says: 3.33332833333e+17
SWIG says: 3.33332833333e+17
Time of numpy: 0.0032000541687
Time of SWIG: 0.00278091430664
Wahoo, we are actually slightly faster than the built-in numpy dot product with just a little bit of work. Next time, moving on to SWIGing classes and incorporating numpy with them as well.