Passing numpy arrays between python and c++ using cython


Passing numpy arrays between Python and c++ using Cython is a handy way of taking advantage of the ease and flexibility of python with the speed of c++.

This code illustrates how simple it is to pass n-dimensional (or in this case 2D) numpy arrays from python to c++ vectors and back, allowing computationally expensive code to be easily written in c++ and linked to high level python code.

The example code is hosted as a gist here and is an adaption from the standard Cython example  but also includes the 2D numpy example

Rectangle.cpp and Rectangle.h is a an example c++ class to be wrapped

This is wrapped using cython with an interface file rect.pyx and compiled using setup.py in the standard cython way using the command:

python setup.py build_ext --inplace

Then run the demo:

python python_test.py

Shows the standard examples as well as how to pass 2D arrays between python and cython


Python <-> c++ interface example for multidimensional arrays

view raw README.md hosted with ❤ by GitHub
# Python running example
from __future__ import print_function
import numpy as np
from rect import PyRectangle
# Initialising the wrapped c++ function
R1 = PyRectangle(0, 0, 9, 9);
#Test1: Doing the area example calculation
print("Test 1, Area: ", R1.getArea())
#Test2: Passing a list from python to c++
list1 = [1,2,3,4,5]
print("Test 2, Sum list: ", R1.sum_vec(list1))
#Test3: Passing a numpy array from python to c++
list2 = np.array([1,2,3,4,5])
print(" Test 3, Sum list: ", R1.sum_vec(list2))
#Test4: Passing a 2D list from python to c++
list3 = [[1, 2, 3], [4, 5, 6]]
print("Test 4, Sum list: ", R1.sum_mat(list3))
#Test5: Passing a 2D numpy array from python to c++
# Passing by value makes a second copy so best to pass by reference as demonstrated in the next example
list4 = np.array([[1, 2, 3], [4, 5, 6]])
print("Test 5, Sum list: ", R1.sum_mat(list4))
#Test6: Passing a 2D numpy array from python to c++ as a constant reference
list5 = np.array([[1, 2, 3], [4, 5, 6]])
print("Test 6, Sum list: ", R1.sum_mat_ref(list5))
#Test7: Returning a 2D numpy array from c++
list6 = np.array([[1, 2, 3], [4, 5, 6]])
print("Test 7, Sum list: ", R1.ret_mat(list6))
# This should work with any n-dimensional array
view raw python_test.py hosted with ❤ by GitHub
# distutils: language = c++
# distutils: sources = Rectangle.cpp
# Cython interface file for wrapping the object
#
#
from libcpp.vector cimport vector
# c++ interface to cython
cdef extern from "Rectangle.h" namespace "shapes":
cdef cppclass Rectangle:
Rectangle(int, int, int, int) except +
int x0, y0, x1, y1
int getLength()
int getHeight()
int getArea()
void move(int, int)
double sum_vec(vector[double])
double sum_mat(vector[vector[double]])
double sum_mat_ref(vector[vector[double]] &)
vector[vector[double]] ret_mat(vector[vector[double]])
# creating a cython wrapper class
cdef class PyRectangle:
cdef Rectangle *thisptr # hold a C++ instance which we're wrapping
def __cinit__(self, int x0, int y0, int x1, int y1):
self.thisptr = new Rectangle(x0, y0, x1, y1)
def __dealloc__(self):
del self.thisptr
def getLength(self):
return self.thisptr.getLength()
def getHeight(self):
return self.thisptr.getHeight()
def getArea(self):
return self.thisptr.getArea()
def move(self, dx, dy):
self.thisptr.move(dx, dy)
def sum_vec(self, sv):
return self.thisptr.sum_vec(sv)
def sum_mat(self, sv):
return self.thisptr.sum_mat(sv)
def sum_mat_ref(self, sv):
return self.thisptr.sum_mat_ref(sv)
def ret_mat(self, sv):
return self.thisptr.ret_mat(sv)
view raw rect.pyx hosted with ❤ by GitHub
/*
Passing variables / arrays between cython and cpp
Example from
http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html
Adapted to include passing of multidimensional arrays
*/
#include "Rectangle.h"
using namespace shapes;
Rectangle::Rectangle(int X0, int Y0, int X1, int Y1)
{
x0 = X0;
y0 = Y0;
x1 = X1;
y1 = Y1;
}
Rectangle::~Rectangle()
{
}
int Rectangle::getLength()
{
return (x1 - x0);
}
int Rectangle::getHeight()
{
return (y1 - y0);
}
int Rectangle::getArea()
{
return (x1 - x0) * (y1 - y0);
}
void Rectangle::move(int dx, int dy)
{
x0 += dx;
y0 += dy;
x1 += dx;
y1 += dy;
}
/*
Inputting a 1D vectoror list and returning its sum
*/
double Rectangle::sum_vec(std::vector<double> sv)
{
double tot=0;
int svs = sv.size();
std::cout << "vector length " << svs << std::endl;
for (int ii=0; ii<svs; ii++)
{
tot = tot + sv.at(ii);
}
return tot;
}
/*
Inputting a 2D vector or list and returning its sum
*/
double Rectangle::sum_mat(std::vector< std::vector<double> > sv)
{
double tot=0;
int svrows = sv.size();
int svcols = sv[0].size();
std::cout << "vector length " << svrows << " , " << svcols << std::endl;
for (int ii=0; ii<svrows; ii++)
{
for (int jj=0; jj<svcols; jj++)
{
tot = tot + sv.at(ii).at(jj);
}
}
return tot;
}
/*
Passing a 2D vector by reference or list and returning its sum
*/
double Rectangle::sum_mat_ref(const std::vector< std::vector<double> > & sv)
{
double tot=0;
int svrows = sv.size();
int svcols = sv[0].size();
std::cout << "vector length " << svrows << " , " << svcols << std::endl;
for (int ii=0; ii<svrows; ii++)
{
for (int jj=0; jj<svcols; jj++)
{
tot = tot + sv.at(ii).at(jj);
}
}
return tot;
}
/*
Inputting a 2D vector, performing a simple operation and returning a new 2D vector
*/
std::vector< std::vector<double> > Rectangle::ret_mat(std::vector< std::vector<double> > sv)
{
int svrows = sv.size();
int svcols = sv[0].size();
std::vector< std::vector<double> > tot;
tot.resize(svrows, std::vector<double> (svcols, -1));
std::cout << "vector length " << svrows << " , " << svcols << std::endl;
for (int ii=0; ii<svrows; ii++)
{
for (int jj=0; jj<svcols; jj++)
{
tot.at(ii).at(jj) = (2*sv.at(ii).at(jj));
}
}
return tot;
}
view raw Rectangle.cpp hosted with ❤ by GitHub
/*
Passing variables / arrays between cython and cpp
Example from
http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html
Adapted to include passing of multidimensional arrays
*/
#include <vector>
#include <iostream>
namespace shapes {
class Rectangle {
public:
int x0, y0, x1, y1;
Rectangle(int x0, int y0, int x1, int y1);
~Rectangle();
int getLength();
int getHeight();
int getArea();
void move(int dx, int dy);
double sum_vec(std::vector<double> sv);
double sum_mat(std::vector< std::vector<double> > sv);
double sum_mat_ref(const std::vector< std::vector<double> > & sv);
std::vector< std::vector<double> > ret_mat(std::vector< std::vector<double> > sv);
};
}
view raw Rectangle.h hosted with ❤ by GitHub
# Cython compile instructions
from distutils.core import setup
from Cython.Build import cythonize
# Use python setup.py build --inplace
# to compile
setup(
name = "rectangleapp",
ext_modules = cythonize('*.pyx'),
)
view raw setup.py hosted with ❤ by GitHub

Related Posts

The move towards library agnostic machine learning

Introduction to Keras Autoencoders

Live tracking of python 3 usage based on a small set of scientific libraries

Thoughts on web tools for visualisation in medical image analysis research

Packaging a python app for Ubuntu using dh-virtualenv

Drag and drop files into a GUI using python and pyside

Interactive visualisations for research papers

Jabref gtk theme

Rendering volumes on the web with x3dom