Python/Cython: Overhead with classes stored in numpy arrays - python

This slow code can be improved by changing the structure, but this is difficult to work around sometimes. The cause, I think, comes from classes stored in an array. I've heard memory views are used to link python and c arrays, but I'm still pretty new to this (only some python knowledge).
Is there a way to do the following efficiently?
An example class:
cdef class ClassWithAdditionFunction:
cdef double value
def __init__(self, double value):
self.value = value
cpdef add_one(self):
self.value += 1
A slow function:
cdef unsigned long int i, ii
cdef unsigned long int loops = pow(10, 8)
cdef double value
addition_classes = np.array([None] * 10)
for i in range(len(addition_classes)):
addition_classes[i] = ClassWithAdditionFunction(value=0)
for i in range(loops/10):
for ii in range(10):
addition_classes[ii].add_one()
Thank you very much for any suggestions!

There are some small things that you could do that should help a little. Really the line of code you want to speed up is addition_classes[ii].add_one(). If you use cython -a to see what's really happening under the hood you'll see that you're making a call to Pyx_GetItemInt, then PyObject_GetAttr, then PyObject_Call. You want to structure your code to avoid these 3 calls.
To avoid the GetItem Call, you'll want to use either numpy's buffer interface or memory views. This tells cython the structure of your array and allows it to pull items from the array more efficiently. In the example bellow I've used a memory view. If you do something similar, make sure that the array is in fact an array full of ClassWithAdditionFunction instances, otherwise you'll likely get a segfault.
To avoid the GetAttr call, declare a variable of type ClassWithAdditionFunction and make the method calls on that variable, that way cython knows that the variable has a compiled version of the method which it can use for faster calls.
Lastly you've already defined add_one with a cpdef method, but I would suggest also adding a return type. Normally we could just put void, but because this is a cpdef function and not a cdef function you could use int instead.
If you put all that together it should look something like:
import numpy as np
cimport cython
cdef class ClassWithAdditionFunction:
cdef double value
def __init__(self, double value):
self.value = value
cpdef int add_one(self):
self.value += 1
return 0
#cython.boundscheck(False)
#cython.wraparound(False)
def main():
cdef:
unsigned long int i, ii, loops = 10 ** 6
ClassWithAdditionFunction addInstance
double value, y
addition_classes = np.array([None] * 10)
cdef ClassWithAdditionFunction[:] arrayview = addition_classes
for i in range(len(addition_classes)):
addition_classes[i] = ClassWithAdditionFunction(value=0)
for i in range(loops/10):
for ii in range(10):
addInstance = arrayview[ii]
addInstance.add_one()
return None

Related

How to wrap a C++ class/struct with attributes that are vectors for fluid use in python

I'm wrapping a C++ API for use in Python, so I'd like the functionality of the Python wrapper classes to pretty closely mirror that of the C++ classes. In one case, I have two objects that are actually nested structs:
// myheader.hpp
#include <vector>
namespace mynames{
struct Data{
struct Piece{
double piece1;
int piece2;
std::vector<double> piece3;
};
std::vector<Piece> pieces;
};
}
I'd like to interact with this object fluidly in Python as if it were a typical Python class using numpy and extension types. So I began by declaring two extension types:
# mydeclarations.pxd
from libcpp.vector cimport vector
cdef extern from "myheader.hpp" namespace "mynames":
cdef cppclass Data:
vector[Piece] pieces
cdef extern from "myheader.hpp" namespace "mynames::Data":
cdef cppclass Piece:
double piece1
int piece2
vector[double] piece3
Then wrapping in Python:
# mytypes.pyx
cimport mydeclarations as cpp
from cython cimport NULL
from libcpp.vector cimport vector
import numpy as np
cdef class Piece:
cdef cpp.Piece *_cppPiece
def __cinit__(self):
self._cppPiece = new cpp.Piece()
def __dealloc__(self):
if self._cppPiece is not NULL:
del self._cppPiece
#property
def piece1(self):
return self._cppPiece.piece1
#piece1.setter
def piece1(self, double d):
self._cppPiece.piece1 = d
#property
def piece2(self):
return self._cppPiece.piece2
#piece2.setter
def piece2(self, int i):
self._cppPiece.piece2 = i
# Use cython's automatic type conversion: (cpp)vector <---> (py)list (COPIES)
#property
def piece3(self):
return np.asarray(self._cppPiece.piece3, dtype=np.double)
#piece3.setter
def piece3(self, arr):
self._cppPiece.piece3 = <vector[double]>np.asarray(arr, dtype=np.double)
#----------------------------------------------------------------------
cdef class Data:
cdef cpp.Data *_cppData
def __cinit__(self):
self._cppData = new cpp.Data()
def __dealloc__(self):
if self._cppData is not NULL:
del self._cppData
#property
def pieces(self):
# Create a list of Python objects that hold copies of the C++ data
cdef Piece pyPiece
cdef int i
pyPieces = []
for i in range(self._cppData.pieces.size()):
pyPiece = Piece()
pyPiece._cppPiece[0] = self._cppData.pieces.at(i)
pyPieces.append(pyPiece)
return np.asarray(pyPieces, dtype=Piece)
#pieces.setter
def pieces(self, arr):
# Clear the existing vector and create a new one containing copies of the data in arr
cdef Piece pyPiece
self._cppData.pieces.clear()
for pyPiece in arr:
self._cppData.pieces.push_back(deref(pyPiece._cppPiece))
This is a simple implementation and as far as I can tell it works, but there are some issues:
Since we use copies, there's no in-place functionality that you might expect if, for example, Piece().piece3 was a python class attribute holding a numpy array. For example,
a = Piece()
a.piece3 = [1,2,3]
a.piece3[0] = 55 # No effect, need to do b = a.piece3; b[0]=55; a.piece3=b
There's a lot of iterating over data and copying. This is probably an issue when the size of Data.pieces is very large.
Can anyone suggest some better alternatives to address these issues? Although Data is more complicated that Pieces, I think they are related and ultimately boil down to wrapping C++ classes with vector attributes for use in Python.
If you want to avoid data copying then it probably involves creating a wrapper class.
cdef class DoubleVector:
cdef vector[double] *vec
cdef owner
def __dealloc__(self):
if owner is not None:
del self.vec
#staticmethod
cdef create_from_existing_Piece(Piece obj):
out = DoubleVector()
out.owner = obj
out.vec = obj._cppData.piece3
return out
# create len/__getindex__/__setindex__ functions
# You could also have this class expose the buffer protocol
Here I've assumed that DoubleVector doesn't own its own data the majority of the time. Therefore it keeps a reference to the Python class that owns the C++ class that owns that data (thus ensuring that object lifetimes are preserved).
Some details (mainly creating a nice sequence interface) is left for you to fill in.
Exposing vector[Piece] is more difficult, largely because any changes to the vector (including resizing it) would invalidate any pointers into the vector. Therefore I'd give serious thought to having a different Python interface to C++ interface.
Could you make Data immutable (so that you simply can't change it from Python and so can safely return pointers into it)?
Could you avoid returning Piece from data and have functions like get_ith_piece1, get_ith_piece2, get_ith_piece3 (i.e. remove a layer from your Python wrapping)?
Alternatively you could do something like
cdef class BasePiece:
cdef cpp.Piece* get_piece(self):
raise NotImplementedError
# most of the implementation of your Piece class goes here
cdef class Piece(BasePiece):
# wrapper that owns its own data.
# largely as before but with
cdef cpp.Piece* get_piece(self):
return self._cppPiece
# ...
cdef class UnownedPiece(BasePiece):
cdef Data d
cdef int index
cdef cpp.Piece* get_piece(self):
return self.d._cppClass.pieces[index]
This is at least safe if the contents of the vector changes (it doesn't point to an existing Piece, but just to the indexed position). You obviously need to be careful about changing the size.
Your getter function for Data.pieces might be something like
#property
def pieces(self):
l = []
for i in range(self.pieces.size()):
l.append(UnownedPiece(self.pieces[i], self))
return tuple(l) # convert to tuple so it's immutable and people
# won't be tempted to try to append to it.
There's obviously a number of other approaches that you could take, but you can create a reasonably nice interface with this kind of approach.
The main thing is: restrict the Python interface as much as is possible.

cython wrap cpp structs and function with parameter an array of structs

I am using Cython to wrap a c++ library in python. Unfortunately, I have no access to the c++ library. Therefore, I have to find somehow a way to wrap the structures and functions as exposed by the lib API.
My question regards the best way to wrap a c++ structure and; subsequently, how to create a memory view in python and pass its pointer (first element address) to a c++ function with parameter an array of cpp structures.
For example, let say I have the following h file:
//test.h
struct cxxTestData
{
int m_id;
double m_value;
};
void processData(cxxTestData* array_of_test_data, int isizeArr)
My pyx file will look like the following
cdef extern from "Test.h":
cdef struct cxxTestData:
int m_id
double m_value
cdef class pyTestData:
cdef cxxTestData cstr
def __init__(self, id, value):
self.cstr.m_id = id
self.cstr.m_value = value
#property
def ID(self):
return self.cstr.m_id
#property
def Value(self):
return self.cstr.m_value
Now, I want to create a number of pyTestData and store them in an array of dtype object. Then I want to pass this array as memory view in a cython/python function.
The wrapping function will have the following signature
cpdef void pyProcessData(pyTestData[::1] test_data_arr)
I have tested the above and it compiles successfully. I managed also to modify the members of each structure. However, this is not what I am trying to achieve. My question is how from this point I can pass an array with the c++ structures encapsulated in each pyTestData object (via the self.cstr).
As an example please have a look to the following listing:
cpdef void pyProcessData(pyTestData[::1] test_data_arr):
cdef int isize test_data_arr.shape[0]
# here I want to create/insert an array of type cxxTestData to pass it
# to the cpp function
# In other words, I want to create an array of [test_data_arr.cstr]
# I guess I can use cxxTestData[::1] or cxxTestData* via malloc and
# copy each test_data_arr[i].cstr to this new array
cdef cxxTestData* testarray = <cxxTestData*>malloc(isize*sizeof(cxxTestData))
cdef int i
for i in range(isize):
testarray[i] = test_data_arr[i].cstr
processData(&testarray[0], isize)
for i in range(isize):
arrcntrs[i].pystr = testarray[i]
free(testarray)
Has anyone come across with such a case? Is there any better way to pass my python objects in the above function without having to copy over the cxx structures internally?
Many thanks in advance and apologies if I do something fundamentally wrong.
Since you want an array of cxxTestData to pass to your C++ functions, the best thing to do is to allocate it as an array. Some untested code that illustrates the approach:
cdef class TestDataArray:
cdef cxxTestData* array:
def __init__(self, int length):
self.array = <cxxTestData*>calloc(length,sizeof(cxxTestData))
def __dealloc__(self):
free(self.array)
def __getitem__(self, int idx):
return PyTestData.from_pointer(&self.array[idx],self) # see later
def __setitem__(self, int idx, PyTestData pyobj): # this is optional
self.array[idx] = deref(pyobj.cstr)
You then want to slightly modify your PyTestData class so that it holds a pointer rather than holding the class directly. It should also have a field representing the ultimate owner of the data (e.g. the array). This ensures the array is kept alive, and can also allow for the case where the PyTestData owns its own data:
cdef class PyTestData:
cdef cxxTestData* cstr
cdef object owner
def __init__(self, id, value):
self.owner = None
self.cstr = <cxxTestData*>malloc(sizeof(cxxTestData))
self.cstr.m_id = id
self.cstr.m_value = value
def __dealloc__(self):
if self.owner is None: # i.e. this class owns it
free(self.cstr)
#staticmethod
cdef PyTestData from_pointer(cxxTestData* ptr, owner):
# calling __new__ avoids calling the constructor
cdef PyTestData x = PyTestData.__new__(PyTestData)
x.owner = owner
x.cstr = ptr
return x
There is a little extra effort in creating the TestDataArray class, but it stores the data in a format directly usable from C++, and so I think it's the best solution.

How to type a function in function argument in cython

I have made a function for doing optimization in python (let's call it optimizer). It requires the function to be optimized (let's call it objective) as one of the function arguments. objective is a function that accepts a one-dimensional np.ndarray and return a float number (which is the same as double in C++?).
I have read this post, but I'm not sure if it is actually the same problem as mine and when I use ctypedef int (*f_type)(int, str), but I get the error Cannot convert 'f_type' to Python object during compilation. Does it only work for C functions? How do I type a python function?
Edit: How my code looks like:
cpdef optimizer(objective, int num_particle, int dim,
np.ndarray[double, ndim=1] lower_bound,
np.ndarray[double, ndim=1] upper_bound):
cdef double min_value
cdef np.ndarray[double, ndim=2] positions = np.empty((num_particle,dim), dtype=np.double)
cdef np.ndarray[double, ndim=1] fitness = np.empty(num_particle, dtype=np.double)
cdef int i, j
# do lots of stuff not shown here
# involve the following code:
for i in range(num_particle):
fitness[i] = objective(positions[i])
return min_value
I want to know if it is possible to type objective to make the code run faster.
I get the error message
Cannot convert Python object argument to type 'f_type'
which I think makes a lot more sense than the one you claim to get - you're trying to pass a Python object into the function. Please make sure that the error messages you report are the ones your code actually generates. Your description of the types that objective takes also does not match the code you show.
However, in general: no you cannot give your objective function a type specifier to speed it up. A generic Python callable carries a lot more information than a C function pointer (e.g. reference counts, details of any closure captured variables, etc).
A possible alternative approach would be to inherit from a cdef class with an appropriate cdef function, so you can at least get the appropriate performance in specific cases:
# an abstract function pointer class
cdef class FPtr:
cdef double function(self,double[:] x) except? 0.0:
# I'm assuming you might want to pass exceptions back to Python - use 0.0 to indicate that there might have been an error
raise NotImplementedError()
# an example class that inherits from the abstract pointer type
cdef class SumSq(FPtr):
cdef double function(self,double[:] x) except? 0.0:
cdef double sum=0.0
for i in range(x.shape[0]):
sum += x[i]**2
return sum
# an example class that just wraps a Python callable
# this will be no faster, but makes the code generically usable
cdef class PyFPtr(FPtr):
cdef object f
def __init__(self,f):
self.f = f
cdef double function(self,double[:] x) except? 0.0:
return self.f(x) # will raise an exception if the types don't match
def example_function(FPtr my_callable):
import numpy as np
return my_callable.function(np.ones((10,)))
Using this example_function(SumSq()) works as expected (and with Cython speed); example_function(PyFPtr(lambda x: x[0])) works as expected (without Cython speed in the callable); example_function(PyFPtr(lambda x: "hello")) gives a type error as expected.

how to pass list of numpy arrays to c++ via cython

I want to pass a list of 2d numpy arrays to a c++ function. My first idea is using a std::vector<float *> to receive the list of array, but I can't find a way to pass the list.
The c++ function looks like this:
double cpp_func(const std::vector<const float*>& vec) {
return 0.0;
}
Cython function likes this:
cpdef py_func(list list_of_array):
cdef vector[float*] vec
cdef size_t i
cdef size_t n = len(list_of_array)
for i in range(n):
vec.push_back(&list_of_array[i][0][0]) # error: Cannot take address of Python object
return cpp_func(vec)
I have tried declare list_of_array using list[float[:,:]], but won't work either.
I will slightly change the signature of your function:
for every numpy-array the function also needs to know the number of elements in this array
data is double * rather than float * because this is what corresponds to default np.float-type. But this can be adjusted accordingly to your needs.
That leads to the following c++-interface/code (for convenience I use C-verbatim-code feature for Cython>=0.28):
%%cython --cplus -c=-std=c++11
from libcpp.vector cimport vector
cdef extern from *:
"""
struct Numpy1DArray{
double *ptr;
int size;
};
static double cpp_func(const std::vector<Numpy1DArray> &vec){
// Fill with life to see, that it really works:
double res = 0.0;
for(const auto &a : vec){
if(a.size>0)
res+=a.ptr[0];
}
return res;
}
"""
cdef struct Numpy1DArray:
double *ptr
int size
double cpp_func(const vector[Numpy1DArray] &vec)
...
The struct Numpy1DArray just bundles the needed information for a np-array, because this is more than just a pointer to continuous data.
Naive version
Now, writing the wrapper function is pretty straight forward:
%%cython --cplus -c=-std=c++11
....
def call_cpp_func(list_of_arrays):
cdef Numpy1DArray ar_descr
cdef vector[Numpy1DArray] vec
cdef double[::1] ar
for ar in list_of_arrays: # coerse elements to double[::1]
ar_descr.size = ar.size
if ar.size > 0:
ar_descr.ptr = &ar[0]
else:
ar_descr.ptr = NULL # set to nullptr
vec.push_back(ar_descr)
return cpp_func(vec)
There are some things worth noting:
you need to coerce the elements of list to something what implements buffer protocol, otherwise &ar[0] will obviously not work, because Cython would expect ar[0] to be a Python-object. Btw, this is what you have missed.
I have chosen Cython's memory views (i.e. double[::1]) as target for coersion. The advantages over np.ndarray are that it also works with array.array and it is also automatically checked, that the data is continuous (that is the meaning of ::1).
a common pitfall is to access ar[0] for an empty ndarray - this access must be guarded.
this code is not thread-safe. Another thread could invalidate the the pointers for example by resizing the numpy-arrays in-place or by deleting the numpy-arrays altogether.
IIRC, for Python 2 you will have to cimport array for the code to work with array.array.
Finally, here is a test, that the code works (there is also an array.array in the list to make the point):
import array
import numpy as np
lst = (np.full(3, 1.0), np.full(0, 2.0), array.array('d', [2.0]))
call_cpp_func(lst) # 3.0 as expected!
Thread-safe version
The code above can also be written in thread-safe manier. The possible problems are:
Another thread could trigger the deletion of numpy-arrays by calling for example list_of_arrays.clear() - after that there could be no more refernces of the arrays around and they would get deleted. That means we need to keep a reference to every input-array as long as we use the pointers.
Another thread could resize the arrays, thus invalidating the pointers. That means we have to use the buffer protocol - its __getbuffer__ locks the buffer, so it cannot be invalidated and release the buffer via __releasebuffer__ once we are done with calculations.
Cython's memory views can be used to lock the buffers and to keep a reference of the input-arrays around:
%%cython --cplus -c=-std=c++11
....
def call_cpp_func_safe(list_of_arrays):
cdef Numpy1DArray ar_descr
cdef vector[Numpy1DArray] vec
cdef double[::1] ar
cdef list stay_alive = []
for ar in list_of_arrays: # coerse elements to double[::1]
stay_alive.append(ar) # keep arrays alive and locked
ar_descr.size = ar.size
if ar.size > 0:
ar_descr.ptr = &ar[0]
else:
ar_descr.ptr = NULL # set to nullptr
vec.push_back(ar_descr)
return cpp_func(vec)
There is small overhead: adding memory views to a list - the price of the safety.
Releasing gil
One last improvement: The gil can be released when cpp_fun is calculated, that means we have to import cpp_func as nogil and release it why calling the function:
%%cython --cplus -c=-std=c++11
from libcpp.vector cimport vector
cdef extern from *:
....
double cpp_func(const vector[Numpy1DArray] &vec) nogil
...
def call_cpp_func(list_of_arrays):
...
with nogil:
result = cpp_func(vec)
return result
Cython will figure out, that result is of type double and thus will be able to release the gil while calling cpp_func.

pass different "C" functions with pointer arrays as the function argument to a class

I am trying to pass different functions which have pointers as arguments to a python function. One example of the input function as input parameter is the given normal function:
Sample.pyx
from cpython cimport array
import cython
import ctypes
cimport numpy as np
cpdef void normal(np.ndarray[ndim=1, dtype=np.float64_t] u,
np.ndarray[ndim=1, dtype=np.float64_t] yu,
np.ndarray[ndim=1, dtype=np.float64_t] ypu):
cdef int i
cdef int n=len(u)
for i in prange(n, nogil=True):
yu[i]=-u[i]*u[i]*0.5
ypu[i]=-u[i]
return
cdef class _SampleFunc:
cdef void (*func)(double *, double *, double *)
cdef void sample(int* x, double* hx, double* hxx, void(*func)(double*, double*, double*), int n):
def int i
for i from 0 <= i < n:
func[0](&x[i], &hx[i], &hxx[i])
return
cdef class myClass:
sample_wrapper = _SampleFunc()
sample_wrapper.func = Null
def foo(np.ndarray[ndim=1, dtype=np.float64_t] x,
np.ndarray[ndim=1, dtype=np.float64_t] hx,
np.ndarray[ndim=1, dtype=np.float64_t] hxx,
_SampleFunc sample_func,
int k):
cdef np.ndarray[ndim=1, dtype=np.float64_t] sp
cdef int num=len(x)
func = sample_func.func
assert func is not NULL, "function value is NULL"
cdef int j
for j from 0 <= j <k:
sample(&x[0],&hx[0], &hxx[0], func, num)
sp[j]=hx[0]
return sp
test.py
import numpy as np
from sample import *
x = np.zeros(10, float)
hx = np.zeros(10, float)
hpx = np.zeros(10, float)
x[0] = 0
x[1] = 1.0
x[2] = -1.0
def pynormal(x):
return -x*x*0.5,-x
hx[0], hpx[0] = pynormal(x[0])
hx[1], hpx[1] = pynormal(x[1])
hx[2], hpx[2] = pynormal(x[2])
num=20
ars=myClass()
s=ars.foo( x, hx, hpx, normal, num)
Running the test.py code I am getting this error:
'ars._SampleFunc' object has no attribute 'func'
I am trying to write a wrapper for different C functions which have three pointer arrays as their argument. My conclusion so far was that it can be done with a class, since the class can be accessible in python. I am wondering how I can pass the C functions with pointer arrays as argument to myClass class?
Update: Normal function
cdef void normal(
int n,
double* u,
double* yu,
double* ypu
):
cdef int i
for i in prange(n, nogil=True):
yu[i]=-u[i]*u[i]*0.5
ypu[i]=-u[i]
return
The first thing to deal with is that a function of signature cdef void (*func)(double *, double *, double *) does not pass the array length. You can't know how long these arrays are, and thus you can't safely access their elements. The sensible thing is to change the function signature to pass a length too:
cdef void (*func)(double *, double *, double *, int)
What is extra confusing is that you seem to be iterating over the same axis of a 1D array in both normal and sample. I suspect that isn't what you want to do, but I'm not going attempt to fix that.
Essentially your problem is that you want to pass an arbitrary Python callable as a C function pointer. The bad news is that Cython can't do it - a Python callable has a significant amount of information associated with it, while a C function pointer is simply the address of some executable memory. Therefore a C function pointer does not have the space available to hold the information in a Python callable. In order to make this work you need to generate code at runtime, which Python can't do.
I've recommended the ctypes standard library module as a solution to similar problems previously, since it can create a function pointer from a Python callable. There is a simpler but more limited solution if you only want to call cdef Cython functions.
ctypes
Here's a minimal example which demonstrates how to implement the idea:
import numpy as np
import ctypes
ctypedef void (*func_t)(int, double *)
cdef void sample(int n, double* x, func_t f):
f(n,x)
def call_sample(double[::1] x,
f):
def func_wrapper(n, arg1):
# x is a slightly opaque ctypes type
# first cast it to a ctypes array of known size
# and then create a numpy array from that
arg1_as_ctypes_array = (ctypes.c_double*n).from_address(ctypes.addressof(arg1.contents))
return f(np.asarray(arg1_as_ctypes_array))
FTYPE = ctypes.CFUNCTYPE(None, # return type
ctypes.c_int, # arguments
ctypes.POINTER(ctypes.c_double))
f_ctypes = FTYPE(func_wrapper) # convert Python callable to ctypes function pointer
# a rather nasty line to convert to a C function pointer
cdef func_t f_ptr = (<func_t*><size_t>ctypes.addressof(f_ctypes))[0]
sample(x.shape[0], &x[0], f_ptr)
def example_function(x):
# expects a numpy array like object
print(x)
def test():
a = np.random.rand(20)
print(a)
call_sample(a,example_function)
I realise that there's some slightly messy conversion between ctypes and Cython - this is unavoidable.
A bit of explanation: I'm assuming you want to keep the Python interface simple, hence example_function just takes a numpy array-like object. The function passed by ctypes needs to accept a number of elements and a pointer to match your C interface.
The ctypes pointer type (LP_c_double) can do do indexing (i.e. arg1[5]) so it works fine for simple uses, but it doesn't store its length internally. It's helpful (but not essential) to change it to a numpy array so you can use it more generally and thus we create a wrapper function to do this. We do:
arg1_as_ctypes_array = (ctypes.c_double*n).from_address(ctypes.addressof(arg1.contents))
to convert it to a known length ctypes array and then
np.asarray(arg1_as_ctypes_array)
to convert it to a numpy array. This shares the data rather than makes a copy, so if you change it then your original data will be changed. Because the conversion to a numpy array follows a standard pattern it's easy to generate a wrapper function in call_sample.
(In the comments you ask how to do the conversion if you're just passing a double, not a double*. In this case you don't have to do anything since a ctypes double behaves exactly like a Python type)
Only cdef functions
If you're certain the functions you want to pass will always be cdef functions then you can avoid ctypes and come up with something a bit simpler. You first need to make the function signature match the pointer exactly:
cdef void normal(int N, double *x): # other parameters as necessary
cdef double[::1] x_as_mview = <double[:N:1]>x # cast to a memoryview
# ... etc
You should then be able to use your definition of SampleFunc almost as is to create module level objects:
# in Cython
normal_samplefunc = SampleFunc()
normal_samplefunc.func = &normal
# in Python
s=ars.foo( x, hx, hpx, normal_samplefunc, num)
ars.foo is the way you wrote it (no ctypes code):
func = sample_func.func
# ...
sample(..., func,...)
This code will run quicker, but you want be able to call normal from Python.
Python interface
You mention in the comments that you'd also like the be able to access normal from Python. You're likely to need a different interface for the Python function and the one you pass to C, so I'd define a separate function for both uses, but share the implementation:
def normal(double[::1] u, # ... other arguments
):
# or cpdef, if you really want
implementation goes here
# then, depending on if you're using ctypes or not:
def normal_ctypes(int n, u # other arguments ...
):
u_as_ctypes_array = (ctypes.c_double*n).from_address(ctypes.addressof(x.contents))
normal(u_as_ctypes_array, # other arguments
)
# or
cdef void normal_c(int n, double* u # ...
):
normal(<double[:N:1]>x # ...
)

Categories