C++ Python+;alglib&x2B;NumPy:如何避免将数组转换为列表?
上下文: 我最近发现了(用于数值计算),这似乎是我一直在寻找的东西(鲁棒插值、数据分析…),但在numpy或scipy中找不到 但是,我担心的是,它不接受numpy数组作为有效的输入格式,而只接受常规python列表对象 问题: 我对代码和文档进行了深入研究,发现(正如预期的那样)此列表格式只是用于转换,因为库会将其转换为ctypes(cpython库只是底层C/C++库的接口) 这就是我担心的地方:在我的代码中,我正在使用numpy数组,因为这对于我正在执行的科学计算来说是一个巨大的性能提升。因此,我担心必须将传递给alglib例程的任何数据转换为list(将转换为ctypes)将对性能产生巨大影响(我使用的数组可能有数十万个浮点,并且有数千个数组) 问题: 您是否认为我确实会有性能损失,或者您认为我应该开始修改alglib代码(仅python接口),以便它可以接受numpy数组,并且只进行一次转换(从numpy数组到ctype)?我甚至不知道这是否可行,因为这是一个相当大的图书馆。。。 也许你们有更好的想法或建议(即使是在相似但不同的库上)C++ Python+;alglib&x2B;NumPy:如何避免将数组转换为列表?,c++,python,performance,numpy,alglib,C++,Python,Performance,Numpy,Alglib,上下文: 我最近发现了(用于数值计算),这似乎是我一直在寻找的东西(鲁棒插值、数据分析…),但在numpy或scipy中找不到 但是,我担心的是,它不接受numpy数组作为有效的输入格式,而只接受常规python列表对象 问题: 我对代码和文档进行了深入研究,发现(正如预期的那样)此列表格式只是用于转换,因为库会将其转换为ctypes(cpython库只是底层C/C++库的接口) 这就是我担心的地方:在我的代码中,我正在使用numpy数组,因为这对于我正在执行的科学计算来说是一个巨大的性能提升。
编辑 我的问题似乎没有引起太多的兴趣,或者我的问题不清楚/不相关。或许没有人有解决方案或建议,但我怀疑周围这么多专家:) 无论如何,我已经写了一个小的,快速的,肮脏的测试代码来说明这个问题
#!/usr/bin/env python
import xalglib as al
import timeit
import numpy as np
def func(x):
return (3.14 *x**2.3 + x**3 -x**2.34 +x)/(1.+x)**2
def fa(x, y, val=3.14):
s = al.spline1dbuildakima(x, y)
return (al.spline1dcalc(s, val), func(val))
def fb(x, y, val=3.14):
_x = list(x)
_y = list(y)
s = al.spline1dbuildakima(_x, _y)
return (al.spline1dcalc(s, val), func(val))
ntot = 10000
maxi = 100
x = np.random.uniform(high=maxi, size=ntot)
y = func(x)
xl = list(x)
yl = list(y)
print "Test for len(x)=%d, and x between [0 and %.2f):" % (ntot, maxi)
print "Function: (3.14 *x**2.3 + x**3 -x**2.34 +x)/(1.+x)**2"
a, b = fa(xl, yl)
err = np.abs(a-b)/b * 100
print "(x=3.14) interpolated, exact =", (a, b)
print "(x=3.14) relative error should be <= 1e-2: %s (=%.2e)" % ((err <= 1e-2), err)
if __name__ == "__main__":
t = timeit.Timer(stmt="fa(xl, yl)", setup="from __main__ import fa, xl, yl, func")
tt = timeit.Timer(stmt="fb(x, y)", setup="from __main__ import fb, x, y, func")
v = 1000 * t.timeit(number=100)/100
vv = 1000 * tt.timeit(number=100)/100
print "%.2f usec/pass" % v
print "%.2f usec/pass" % vv
print "%.2f %% less performant using numpy arrays" % ((vv-v)/v*100.)
#/usr/bin/env python
将xalglib作为al导入
导入时间信息
将numpy作为np导入
def func(x):
返回(3.14*x**2.3+x**3-x**2.34+x)/(1.+x)**2
def fa(x,y,val=3.14):
s=al.花键1dbuildakima(x,y)
报税表(会计科目、会计科目、会计科目)
def fb(x,y,val=3.14):
_x=列表(x)
_y=列表(y)
s=al.夹板1dbuildakima(_x,_y)
报税表(会计科目、会计科目、会计科目)
ntot=10000
最大值=100
x=np.随机.均匀(高=最大值,大小=ntot)
y=func(x)
xl=列表(x)
yl=列表(y)
打印“len(x)=%d的测试,以及[0和%.2f]之间的x):”%(ntot,maxi)
打印“功能:(3.14*x**2.3+x**3-x**2.34+x)/(1.+x)**2”
a、 b=fa(xl,yl)
误差=np.abs(a-b)/b*100
打印“(x=3.14)插值,精确=”,(a,b)
打印(x=3.14)相对误差应该是使C++ AlgLIB接受数字数组是可行的:SciPy这样做。问题是它有多困难。你可能想试试半自动C++。→ Python包装程序,如(从我将开始的程序开始–警告:我不是专家):
关于另一个主题:过去我在SciPy中成功地使用了插值样条线。不过,我不确定这是否足以满足您的需要,因为您在SciPy中没有找到您想要的所有内容。除了EOL的答案,您还可以尝试
为了生成一个Python接口,该接口处理NumPy数组,但使用适当的参数调用底层C/C++
我发现文档足够清晰,可以为一个小型的科学C库执行此操作,而之前从未执行过此操作,也没有丰富的C和Python接口经验。您可以创建自己的wrap函数,将numpy数组的数据缓冲区直接传递给向量的数据指针,这不会复制数据,并加快wrap速度下面的代码将x.ctypes.data传递给x_vector.ptr.p_ptr,其中x是一个numpy数组
传递numpy数组时,必须确保该数组的元素位于连续内存中
import xalglib as al
import numpy as np
import ctypes
def spline1dbuildakima(x, y):
n = len(x)
_error_msg = ctypes.c_char_p(0)
__c = ctypes.c_void_p(0)
__n = al.c_ptrint_t(n)
__x = al.x_vector(cnt=n, datatype=al.DT_REAL, owner=al.OWN_CALLER,
last_action=0,ptr=al.x_multiptr(p_ptr=x.ctypes.data))
__y = al.x_vector(cnt=n, datatype=al.DT_REAL, owner=al.OWN_CALLER,
last_action=0,ptr=al.x_multiptr(p_ptr=y.ctypes.data))
al._lib_alglib.alglib_spline1dbuildakima(
ctypes.byref(_error_msg),
ctypes.byref(__x),
ctypes.byref(__y),
ctypes.byref(__n),
ctypes.byref(__c))
__r__c = al.spline1dinterpolant(__c)
return __r__c
def func(x):
return (3.14 *x**2.3 + x**3 -x**2.34 +x)/(1.+x)**2
def fa(x, y, val=3.14):
s = spline1dbuildakima(x, y)
return al.spline1dcalc(s, val), func(val)
def fb(x, y, val=3.14):
s = al.spline1dbuildakima(x, y)
return al.spline1dcalc(s, val), func(val)
ntot = 10000
maxi = 100
x = np.random.uniform(high=maxi, size=ntot)
y = func(x)
xl = list(x)
yl = list(y)
import time
start = time.clock()
for i in xrange(100):
a, b = fa(x, y)
print time.clock()-start
err = np.abs(a-b)/b * 100
print a, b, err
start = time.clock()
for i in xrange(100):
a, b = fb(xl, yl)
print time.clock()-start
err = np.abs(a-b)/b * 100
print a, b, err
输出为:
0.722314760822 <- seconds of numpy array version
3.68672728107 3.68672785313 1.55166878281e-05
3.22011891502 <- seconds of list version
3.68672728107 3.68672785313 1.55166878281e-05
0.722314760822感谢您的回答。SciPy软件包中的样条曲线按预期工作,但我缺少了一些样条曲线,如Akima(我确实有一些数据需要线性插值,但“性能不好”)“足以使用三次或更高阶样条曲线,而不会产生一些不必要的伪影(如结果中的振荡)此外,AlgLB有一些其他的数据分析和统计工具,Spyy没有,所以我使用这种外部库是有意义的。对于包层,似乎AlgLB开发人员已经使用了一个用于C+Python…@ MHVELL:可以包装C++代码,以便它直接使用NUMPY数组而不是Python列表;令人惊讶的是,alglib的Python包装不允许这样做。自己包装所需的函数可能比修改alglib Python包装更容易:如果需要的话,您将能够使用自己的包装来更轻松地跟踪alglib的更新。我列出的三个包装应该对您有很大帮助。哦,好吧:)我不是在Python中有专家,我甚至可以尝试一下。谢谢。MHAVEL:谢谢。我不擅长为Python包C++代码,但是研究一些列出的C++包装可能是一个很好的投资,对于有兴趣的快速科学计算。谢谢你的回答,我来看看。你可以所有x=np.ascontiguousarray(x)
以确保它在内存中是连续的。哇,太好了!非常感谢HYRY。我以前从未使用过包装方法,所以我正在阅读一些EOL很好地指出的文档。您的示例对我来说是一个好时机,所以我现在有了一些具体的培训内容。@Sebastian感谢您的评论。这也将对我有所帮助。
0.722314760822 <- seconds of numpy array version
3.68672728107 3.68672785313 1.55166878281e-05
3.22011891502 <- seconds of list version
3.68672728107 3.68672785313 1.55166878281e-05