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
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
# 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 |
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
# 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) |
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
/* | |
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; | |
} |
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
/* | |
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); | |
}; | |
} |
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
# 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'), | |
) |