0

I have an external library that computes the optima, say minima, of a given function. Say its headers give me a function

double[] minimizer(ObjFun f)

where the headers define

typedef double (*ObjFun)(double x[])

I have generated Cython wrappers for this library. I now want to give user parameterized functions to it, specifically, I want to write a function

def getFunction(double q11, double q12, double q22):
    cdef f(double x[]):
        return x[0]*x[0]*q11 + 2*x[0]*x[1]*q12 +  x[1]*x[1]*q22
return f

that the user calls with their parameters (q11, q12, q22) to get a function that can be used as a callback for the C optimization library.

(The example above is contrived and simplified, the whole point of doing this in Cython is that I want nearly-C-efficient callbacks to give to the library.)

There is no way to do this in C, as observed by people in my other question. But in Cython I can compile this:

cdef class Quadratic:
    cdef double q11
    cdef double q12
    cdef double q22
    def __cinit__(self, double a, double b, double c):
        self.q11 = a
        self.q12 = b
        self.q22 = c
    cdef f(self, double x[]):
        return self.q11*x[0]*x[0] + self.q12*x[0]*x[1] + self.q22*x[1]*x[1]

(I have not yet tried using a generated function like this as an input to the library).

My question - is there Python overhead in this evaluation? I would like to have the parameterized function be nearly as efficient as if it were written in C.

If that is possible, how does Cython achieve this?

akka
  • 23
  • 5
  • Hi, I have the exact same problem as you had: I want to generate an efficient callback to feed into a function minimiser. Have you found a solution for this? – FizzleDizzle Aug 10 '22 at 10:39

1 Answers1

1

(Unless this is a very new addition to Cython then) this is not valid Cython code and can't be made to work. It fails for me with the error

C function definition not allowed here

The reason for this is that - as you already know - it is a genuine limitation of C: there is no valid C code that Cython could generate to create a closure. The reason being is that C would need to be able to dynamically generate a function at runtime to access the closured variables, and that isn't possible in standard C.

There are (at least) two three ways round this. First (and probably most sensible) you change your interface to accept a data pointer:

double[] minimizer(ObjFun f, void* data)

with

typedef double (*ObjFun)(double x[], void* data)

(where minimizer passes data to f.) If you're using an external library then you can't make this change, but I'd be surprised if it didn't use something like this - it's the standard solution.

The second way is to use ctypes to convert a Python callable into a C function pointer. This gets round the problem by dynamically generating machine code at runtime to match the C calling convention (and so can be blocked by security features that prohibit making memory executable). I show a basic example of how to do this in the second half of this answer. (I'm sure I have another more thorough answer showing this but I can't find it right now).

Using ctypes involves a reasonable amount of Python overhead, which is unavoidable.

(Edited addition) The third option to emulate a closure (somewhat unsatisfactorily) is to store the data in global variables:

# at global scope
q11 = 1.2
q22 = 3.2
q33 = 4.0

cdef double f(double x[]):
    # as before

The big limitation is that you can only use one version of f at once, so you can't run things in parallel or anything like that, but given the constraints that might be acceptable.


With respect to your cdef class version: calling it is pretty efficient but it effectively has a signature of:

`double (*)(Quadratic*, double [])`

so it won't fit the interface defined by your minimizer function.

Community
  • 1
  • 1
DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Thanks. I edited my question with Cython code that I have now seen compile. Can you comment on the validity/efficiency of that now? – akka Mar 19 '17 at 19:47
  • See edit. It won't work I'm afraid. I've added a possible alternative solution that might be acceptable though (although not really a closure) – DavidW Mar 19 '17 at 20:13
  • i am trying to find a way to change a Python function that i am submitting to the external C library to a C-efficient function and want to do this with the least code refactoring as possible. Hence all this thinking. – akka Mar 19 '17 at 20:40
  • One final suggestion that I think might work pretty well: [try numba](http://numba.pydata.org/numba-doc/dev/user/cfunc.html). It generates code at runtime so should be able to make closures that work. I've never used it myself though, so if you have issues with it ask a new question rather than asking me! – DavidW Mar 19 '17 at 20:49
  • Thanks. I think your global solution might work - I could try creating a function that modifies the global variables. I will try it out. – akka Mar 19 '17 at 20:54
  • I am so sorry for pestering, but one more question: any comments on the efficiency of the ctypes route? If the callback needs to be evaluated many times (say in a search routine in an optimization function) do we basically incur all the Python overhead each time, right? – akka Mar 19 '17 at 21:21
  • Yes. ctypes will have quite a bit of Python overhead on each call so is unlikely to be good for a function that is called lots of times. – DavidW Mar 19 '17 at 21:48