I have some numerical code to run in SciPy. It involves complicated rational functions of exponentials of polynomials, so it's pretty computationally expensive; ergo, I have written it in C and am calling it using ctypes. The most important use case is as an integrand for scipy.integrate.quad, but I also occasionally need to call it directly.
The "natural" signature of my function is
double f(double x, double y, double z){}
which the ctypes documentation suggests using, with the corresponding Python
import ctypes
so = ctypes.CDLL('/path/to/f.so')
f = so.f
f.restype = ctypes.c_double
f.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_double)
Regardless, however, to call it as an integrand, SciPy requires a certain function signature
double f(int n, double args[n]){}
which is specified in the Python code as
import ctypes
so = ctypes.CDLL('/path/to/f.so')
f = so.f
f.restype = ctypes.c_double
f.argtypes = (ctypes.c_int, ctypes.c_double)
To pass the parameters y
and z
when performing the integral, they're passed to quad
as a tuple called args
.
scipy.integrate.quad(f, lowerlimit, upperlimit, args=(y,z))
This makes it unclear how to call f
directly. My naive attempt was
f(3, (x,y,z))
but this yields a type error for argument 2. Several variations have similarly failed to work. It's not really surprising; ctypes is expecting a function call with exactly one integer argument followed by exactly one double argument.
I have absolutely no idea how quad
gets y
and z
into f
. I tried looking into the SciPy source, but I must admit I got lost trying to trace the calls from the Python into the C into the Fortran.
I could just write another function as a wrapper for either the direct calls or the integration, but it seems more elegant to use just one form, and at the very least, I'd like to understand how the SciPy call works.
How can I call the SciPy integrand form of f
directly, passing all three parameters x
, y
, and z
?
I'm using Python 3.4.3, NumPy 1.11.2, and SciPy 0.18.1.
Edit: Note that f
can be called by changing its argtypes:
f.argtypes = (ctypes.c_int, 3*ctypes.c_double)
f(3, (3*ctypes.c_double)(x, y, z))
I'm still curious what it is that SciPy is doing, though. Setting the argtypes back and forth all the time is still inelegant and inconvenient, at best.
Edit 2: Note that this question, after the previous edit, is now basically a duplicate of this one, which has helpfully popped up in the right column.