我有一个外部库,可以计算给定函数的最小值,例如minima。说它的标题给我一个功能
double[] minimizer(ObjFun f)
标题定义
typedef double (*ObjFun)(double x[])
我为这个库生成了Cython包装器。我现在想给用户参数化函数,具体来说,我想写一个函数
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
用户使用其参数(q11,q12,q22)调用以获取可用作C优化库的回调的函数。
(上面的例子是设计和简化的,在Cython中这样做的重点是我想要几乎C高效的回调来提供给库。)
在C中无法做到这一点,正如我在其他问题中所观察到的那样。但在Cython中我可以编译:
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]
(我还没有尝试使用像这样的生成函数作为库的输入。)
我的问题 - 此评估中是否存在Python开销?我想让参数化函数几乎与用C编写的函数一样高效。
如果可以的话,Cython如何实现这一目标?
答案 0 :(得分:1)
(除非这是Cython的一个非常新的补充),这个不是有效的Cython代码,并且无法工作。错误
让我失败了此处不允许使用C函数定义
原因在于 - 正如您已经知道的那样 - 它是C的真正限制:Cython没有有效的C代码可以生成以创建闭包。原因是C需要能够在运行时动态生成一个函数来访问闭包变量,这在标准C中是不可能的。
这有三种方式(至少)两种。首先(也可能是最明智的)你改变接口以接受数据指针:
double[] minimizer(ObjFun f, void* data)
与
typedef double (*ObjFun)(double x[], void* data)
(最小化程序将data
传递给f
。)如果您使用的是外部库,则无法进行此更改,但如果您做出此更改,我会感到惊讶它没有使用这样的东西 - 它是标准解决方案。
第二种方法是使用ctypes
将Python可调用转换为C函数指针。这通过在运行时动态生成机器代码以匹配C调用约定来解决问题(因此可以通过禁止使内存可执行的安全功能来阻止)。我在this answer的后半部分展示了如何执行此操作的基本示例。 (我确定我有另一个更全面的答案显示这个,但我现在无法找到它。)
使用ctypes
涉及合理数量的Python开销,这是不可避免的。
(已编辑添加)模拟闭包的第三个选项(有点令人不满意)是将数据存储在全局变量中:
# at global scope
q11 = 1.2
q22 = 3.2
q33 = 4.0
cdef double f(double x[]):
# as before
最大的限制是你一次只能使用f
的一个版本,所以你不能并行或类似地运行,但考虑到可以接受的约束。
关于你的cdef类版本:调用它非常有效但是它实际上有一个签名:
`double (*)(Quadratic*, double [])`
因此它不适合您的minimizer
函数定义的界面。