Source files atgithub.com:casperdcl/ctypes-demo, based onbitbucket.org:chris_richardson/ctypes-demo
#include "math.h"void add(double *in_data, double *out_data, int n){ int i; for (i = 0; i != n; ++i) out_data[i] += in_data[i];}
gcc -shared -fPIC -o mylib_shared.so mylib.cnm -a mylib_shared.so # list symbols
ctypes
from ctypes import CDLL, POINTER, c_doubleimport numpy as npMYLIB = CDLL("./mylib_shared.so")def get_ptr(np_arr, dtype=c_double): return np_arr.ctypes.data_as(POINTER(dtype))def add(x, y, n): assert x.dtype == y.dtype == np.float64 MYLIB.add(get_ptr(x), get_ptr(y), n)
Use as:
import numpy as npimport mylibx = np.arange(3, dtype=numpy.float64)y = x.copy()mylib.add(x, y, 3)print(y)
cffi
from cffi import FFIffi = FFI()ffi.cdef("void add(double *in_data, double *out_data, int n);")MYLIB = ffi.dlopen("./mylib_shared.so")def add(x, y, n): MYLIB.add( ffi.cast("double *", x.ctypes.data), ffi.cast("double *", y.ctypes.data), n)
Alternative in-place:
from cffi import FFIffi = FFI()ffi.cdef("void add(double *in_data, double *out_data, int n);")ffi.set_source("mylib_shared", open("mylib.c").read())ffi.compile(verbose=True)from mylib_shared import lib as MYLIBadd = MYLIB.add
Use as withctypes
.
numba
Compile python itself:
from numba import jit@jitdef add(...): ...
Advanced:
from numba import jitimport numpy as np@jitdef calculate_distances(n): pts = np.random.random((n, 3)) d = np.zeros((n, n)) for i in range(n): for j in range(n): d[i, j] = np.linalg.norm(pts[i,:] - pts[j,:]) return dn = 1000d = calculate_distances(n)llvm = calculate_distances.inspect_llvm()for k, v in llvm.items(): print(k, v)print(d)
Even more advanced:
# Example demonstrating numba cfunc to provide a compiled callback kernelfrom ctypes import POINTER, c_double, c_intfrom numba import cfunc, types, carray, jitimport numpy as np# This is the kernel we want to call from our bigger librarydef calculate_distances(_d, _pts, n): d = carray(_d, (n, n), dtype=np.float64) pts = carray(_pts, (n, 3), dtype=np.float64) for i in range(n): for j in range(n): d[i, j] = np.linalg.norm(pts[i, :] - pts[j, :])# C signaturesig = types.void( types.CPointer(types.double), types.CPointer(types.double), types.intc)fn = cfunc(sig, nopython=True)(calculate_distances) # compile with LLVM# Take a look at the codeprint(fn.inspect_llvm())# Show the memory address of the functionprint('0x%0x'%fn.address)n = 12d = np.zeros((n, n), dtype=np.float64)pts = np.random.random((n, 3))# Convenient ctypes wrapper allows us to test it from Pythonfn.ctypes(d.ctypes.data_as(POINTER(c_double)), pts.ctypes.data_as(POINTER(c_double)), n)np.set_printoptions(precision=2)print(d)
class Foo{public: std::vector<double> dist(){ ... }};
pybind11
Replacement forboost::python
#include <cmath>#include <vector>class PointCloud {public: PointCloud(std::vector<double> &points) : _points(points) {} std::vector<double> calculate_distances() { unsigned int n = _points.size() / 3; std::vector<double> result(n * n); for (unsigned int i = 0; i != n; ++i) for (unsigned int j = 0; j != n; ++j) result[i * n + j] = distance(i, j); return result; }private: double distance(unsigned int i, unsigned int j) { double d = 0.0; for (unsigned int k = 0; k != 3; ++k) { double d0 = _points[i * 3 + k] - _points[j * 3 + k]; d += d0 * d0; } return std::sqrt(d); } std::vector<double> _points;};
#include <point_cloud.cpp> // bad practice ...// ... you should have a header and link libs instead, but you get the idea#include <pybind11/pybind11.h>#include <pybind11/stl.h>// Wrappers using pybind11namespace py = pybind11;PYBIND11_MODULE(point_cloud, m) { py::class_<PointCloud>(m, "PointCloud") // class .def(py::init<std::vector<double> &>()) // initialiser .def("calculate_distances", &PointCloud::calculate_distances);}
from distutils.core import setupfrom distutils.extension import Extensionimport pybind11pybind11_include = pybind11.get_include()setup(name='point_cloud', version='1.0', ext_modules=[Extension( "point_cloud", ['wrap_point_cloud.cpp'], language='c++', #include_dirs=[pybind11_include, '/usr/include/eigen3'], include_dirs=[pybind11_include], extra_compile_args=['-std=c++11'])])
Use:
python setup.py buildPYTHONPATH="$PYTHONPATH:$PWD/build/lib*" python -c "
from point_cloud import PointCloudimport numpy as npn = 5pts = np.random.random((n, 3))p = PointCloud(pts.flatten().tolist()) # list -> std::vectord_list = p.calculate_distances() # std::vector -> listd = np.array(d_list).reshape((n, n)) # Reshape with numpyprint(d)
"
swig
Best for multi-language interfaces, not great if only wrapping for Python.
pycuda
Similar to in-placecffi
.