Python + alglib + NumPy: как избежать преобразования массивов в списки?
контекст: Я недавно обнаружил проекта библиотека (для численных вычислений), что, похоже, то, что я искал (надежная интерполяция, анализ данных...) и не мог найти в numpy или scipy.
однако, меня беспокоит тот факт, что (например. для интерполяции) он не принимает массив numpy в качестве допустимого формата ввода, но только обычный список python объекты.
: Я немного покопался в коде и документации и обнаружил (как и ожидалось), что этот формат списка предназначен только для перехода, поскольку библиотека в любом случае преобразует его в ctypes (библиотека cpython-это просто интерфейс для базовой библиотеки C/C++).
вот где моя забота: внутри моего кода я работаю с массивами numpy, потому что это большой прирост производительности для научных вычислений, которые я выполняю на нем. Таким Образом, Я страх необходимости конвертировать любые данные, переданные в проекта подпрограммы в список (который будет преобразован в ctypes) будут иметь огромное влияние на производительность (я работаю с массивами, которые могут иметь сотни тысяч поплавков внутри, и с тысячами массивов).
вопрос: Вы думаете, что у меня действительно будет потеря производительности, или вы думаете, что я должен начать изменять проекта код (только python интерфейс), чтобы он мог принимать массивы numpy и делать только одно преобразование (из массивов numpy в ctypes)? Я даже не знаю, возможно ли это, потому что это довольно большая библиотека... Возможно, у вас есть лучшие идеи или предложения (даже в похожих, но разных библиотеках)...
редактировать
Кажется, моя проблема не получает большого интереса, или что мой вопрос не ясен/релевантен. Или, может быть, ни у кого нет решения или совета, но я сомневаюсь со многими эксперты вокруг :) Во всяком случае, я написал небольшой, быстрый и грязный тестовый код, чтобы проиллюстрировать проблему...
#!/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.)
и запустив его, я получаю:
"""
Test for len(x)=10000, and x between [0 and 100.00):
Function: (3.14 *x**2.3 + x**3 -x**2.34 +x)/(1.+x)**2
(x=3.14) interpolated, exact = (3.686727834705164, 3.6867278531266905)
(x=3.14) relative error should be <= 1e-2: True (=5.00e-07)
25.85 usec/pass
28.46 usec/pass
10.09 % less performant using numpy arrays
"""
потеря производительности колеблется между примерно 8% и 14%, что для меня огромно...
3 ответов
заставить c++ alglib принимать массивы NumPy, безусловно, выполнимо: SciPy делает это. Вопрос в том, насколько это сложно. Возможно, вы захотите попробовать одну из полуавтоматических программ упаковки C++ → Python, например (начиная с той, с которой я бы начал–предупреждение: я не эксперт):
на другую тему: я использовал интерполяцию сплайны в SciPy с успехом, в прошлом. Однако я не уверен, что этого будет достаточно для ваших нужд, поскольку вы не нашли в Сципи всего, что хотели.
Вы можете создать собственную функцию wrap, которая напрямую передает буфер данных массива numpy в указатель данных вектора, это не будет копировать данные и значительно ускорит вашу функцию wrap. Следующий код проходит x.ctypes.данные 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
в дополнение к ответам EOL вы также можете попробовать
для создания интерфейса Python, который имеет дело с массивами NumPy, но вызывает базовый C/C++ с соответствующими аргументами.
Я нашел документы достаточно ясными, чтобы сделать это для небольшой научной библиотеки C, никогда не делая этого раньше или имея огромный опыт взаимодействия C и Python.