Thursday, August 4, 2011

Exploring ctypes (4)

Introducing the subject of ctypes, I want to point you to a neat example from Andrew Dalke (second half of post here) that is a little beyond me, in many respects. But I wanted to give a shout out, and also say that I got it to run with a minor tweak.

• in the example file integrators.c, it should read
double integrate_gq(probe_func_t f) {
..
double integrate_rectangle(probe_func_t f) {

• the sym link for the built library should be (on 10.7):
ln -s build/lib.macosx-10.7-intel-2.7/integrators.so

And here is the output I got running his examples by cut and paste. First the build:


> python setup.py build
running build
running build_ext
building 'integrators' extension
llvm-gcc-4.2 -fno-strict-aliasing -fno-common -dynamic -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -mno-fused-madd -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch i386 -arch x86_64 -pipe -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c integrators.c -o build/temp.macosx-10.7-intel-2.7/integrators.o
creating build/lib.macosx-10.7-intel-2.7
llvm-gcc-4.2 -Wl,-F. -bundle -undefined dynamic_lookup -Wl,-F. -arch i386 -arch x86_64 build/temp.macosx-10.7-intel-2.7/integrators.o -o build/lib.macosx-10.7-intel-2.7/integrators.so


And the exploration in Python. We load the library:


> python
Python 2.7.1 (r271:86832, Jun 16 2011, 16:59:05)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.CDLL("integrators.so")
<h;CDLL 'integrators.so', handle 7fceb0483980 at 1056d9a90>


We fail to get our C integrator function to accept the Python-defined "probe" function as argument:


>>> integrators = _
>>> integrators.integrate_rectangle
<_FuncPtr object at 0x105759050>
>>> def flat_probe(x):
... return 1.0
...
>>> integrators.integrate_rectangle(flat_probe)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1


[ About _ in Python: here and here ]

A simple cytypes.CFUNCTYPE definition does the trick:


>>> PROBE_FUNC = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)
>>>
>>> integrators.integrate_rectangle.argtypes = [PROBE_FUNC]
>>> integrators.integrate_rectangle.restype = ctypes.c_double
>>> integrators.integrate_rectangle(PROBE_FUNC(flat_probe))
2.0
>>> integrators.integrate_gq.argtypes=[PROBE_FUNC]
>>> integrators.integrate_gq.restype = ctypes.c_double
>>> integrators.integrate_gq(PROBE_FUNC(flat_probe))
2.0


And the original example


>>> import math
>>> def probe_function(x):
... return math.cos(x)*(x+1)
...
>>> integrators.integrate_gq(PROBE_FUNC(probe_function))
1.6829416883812192
>>> integrators.integrate_rectangle(PROBE_FUNC(probe_function))
1.7006012905844656
>>>


Where I'm going:

• ctypes library documentation
• learn more about distutils

Getting that build from these four lines is simply amazing:

setup.py

from distutils.core import setup, Extension

setup(name="integrators", version="0.0",
ext_modules = [Extension("integrators", ["integrators.c"])])


And for reference, in case the page ever goes away here are his code listings:

integrators.h

#ifndef DALKE_INTEGRATORS_H
#define DALKE_INTEGRATORS_H

/* Two simple numerical integrators */

typedef double (*probe_func_t)(double);

double integrate_gq(probe_func_t f);
double integrate_rectangle(probe_func_t f);

#endif


integrators.c

/* Two numerical integrators */
#include "integrators.h"

/* 4 point Gaussian quadrature integration */
double integrate_gq(probe_func_t f) {
return
0.652145155*(f(+0.339981044) + f(-0.339981044)) +
0.347854845*(f(+0.861136312) + f(-0.861136312));
}

/* 4 point rectangle rule */
double integrate_rectangle(probe_func_t f) {
return (f(-0.75)+f(-0.25)+f(0.25)+f(0.75))/2;
}