如何直接调用ctypes SciPy积分?

时间:2017-03-07 18:55:58

标签: python python-3.x scipy ctypes

我有一些在SciPy中运行的数字代码。它涉及多项式指数的复杂有理函数,因此计算量很大;我已经用C语言编写了它,并使用ctypes调用它。最重要的用例是作为scipy.integrate.quad的集成,但我偶尔也需要直接调用它。

我的功能的“自然”签名是

double f(double x, double y, double z){}
ctypes documentation建议使用的

和相应的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)

然而,无论如何将其称为被积函数SciPy requires a certain function signature

double f(int n, double args[n]){}

在Python代码中指定为

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)

要在执行积分时传递参数yz,它们会作为名为quad的元组传递给args

scipy.integrate.quad(f, lowerlimit, upperlimit, args=(y,z))

这使得不清楚如何直接致电f。我天真的尝试是

f(3, (x,y,z))

但是这会产生参数2的类型错误。几个变体同样无法工作。这并不奇怪; ctypes期望一个函数调用只有一个整数参数,后跟一个双参数。

我完全不知道quad如何yz进入f。我试着查看SciPy源代码,但我必须承认我试图跟踪Python中的调用到C语言中的丢失。

我可以编写另一个函数作为直接调用或集成的包装器,但只使用一个表单似乎更优雅,至少,我想了解SciPy调用的工作原理。

如何直接调用f的SciPy被学习形式,传递所有三个参数xyz

我正在使用Python 3.4.3,NumPy 1.11.2和SciPy 0.18.1。

修改:请注意,可以通过更改其argtypes来调用f

f.argtypes = (ctypes.c_int, 3*ctypes.c_double)
f(3, (3*ctypes.c_double)(x, y, z))

我仍然很好奇SciPy正在做什么。一直来回设置argtypes仍然是不优雅和不方便,充其量。

编辑2 请注意,在上一次修改之后,此问题现在基本上与this one重复,并在右栏中弹出了这个问题。

3 个答案:

答案 0 :(得分:2)

您无法在Python级别修复此问题。文档https://docs.scipy.org/doc/scipy/reference/tutorial/integrate.html#faster-integration-using-ctypes陈述

  

使用函数签名double在C中编写一个被积函数   f(int n,double args [n])

在您的情况下,您必须在C级别添加一个

的函数
double f_for_scipy(int n, double args[n]) {
    return f(args[0], args[1], args[2]);
}

并将其提供给四元组。

答案 1 :(得分:0)

我不知道这对ctypes情况是否有帮助,但在调用Python integrad时,这些scipy函数会将自由变量与args连接起来。换句话说

def bar(x,y,z):
   return np.sin(x*y*z)
In [43]: quad(bar,0,1, args=(1,2))
Out[43]: (0.7080734182735712, 7.861194120923578e-15)

当评估为0.5时,(x,)+args

In [49]: bar(*(.5,)+(1,2))
Out[49]: 0.8414709848078965

In [50]: bar(.5,1,2)
Out[50]: 0.8414709848078965

使用c签名:

f_for_scipy(int n, double args[n])

n是参数的数量,args[n]是指向数组的指针。

我正在使用cython及其扩展类型

尝试这些类型的调用

https://cython.readthedocs.io/en/latest/src/tutorial/cdef_classes.html

Passing a cython function vs a cython method to scipy.integrate

答案 2 :(得分:0)

您需要具有正确的函数签名,但类型Python argtypes应为POINTER(c_double)。 C中的数组衰减到函数参数中的指针:

C示例(Windows)

#include <stdio.h>
__declspec(dllexport) double f(int n, double args[])
{
    double sum = 0;
    int nn;
    for(nn = 0; nn < n; ++nn)
    {
        printf("args[%d] = %ld\n",nn,args[nn]);
        sum += args[nn];
    }
    return sum;
}

ctypes示例

>>> from ctypes import *
>>> dll = CDLL('x')
>>> dll.f.restype = c_double
>>> dll.f.argtypes = c_int,POINTER(c_double)
>>> L=[1.1,2.2,3.3,4.4,5.5]
>>> args = (c_double*len(L))(*L)
>>> dll.f(len(args),args)
args[0] = 1.100000
args[1] = 2.200000
args[2] = 3.300000
args[3] = 4.400000
args[4] = 5.500000
16.5