Самый быстрый способ получить кросс-произведение
Похоже, что вычисление перекрестного произведения массива векторов намного быстрее, чем использование np.cross
, Я пробовал vector-first и vector-last, похоже, это не имеет значения, хотя это было предложено в ответе на аналогичный вопрос. Я использую это неправильно, или это только медленнее?
Явное вычисление, по-видимому, занимает около 60 нс для каждого продукта на ноутбуке. Это примерно так быстро, как скоро? В этом случае, похоже, нет никаких причин переходить на Cython или PyPy или писать специальные ufunc
еще.
Я также вижу ссылки на использование einsum, но я не очень понимаю, как его использовать, и подозреваю, что это не быстрее.
a = np.random.random(size=300000).reshape(100000,3) # vector last
b = np.random.random(size=300000).reshape(100000,3)
c, d = a.swapaxes(0, 1), b.swapaxes(0, 1) # vector first
def npcross_vlast(): return np.cross(a, b)
def npcross_vfirst(): return np.cross(c, d, axisa=0, axisb=0)
def npcross_vfirst_axisc(): return np.cross(c, d, axisa=0, axisb=0, axisc=0)
def explicitcross_vlast():
e = np.zeros_like(a)
e[:,0] = a[:,1]*b[:,2] - a[:,2]*b[:,1]
e[:,1] = a[:,2]*b[:,0] - a[:,0]*b[:,2]
e[:,2] = a[:,0]*b[:,1] - a[:,1]*b[:,0]
return e
def explicitcross_vfirst():
e = np.zeros_like(c)
e[0,:] = c[1,:]*d[2,:] - c[2,:]*d[1,:]
e[1,:] = c[2,:]*d[0,:] - c[0,:]*d[2,:]
e[2,:] = c[0,:]*d[1,:] - c[1,:]*d[0,:]
return e
print "explicit"
print timeit.timeit(explicitcross_vlast, number=10)
print timeit.timeit(explicitcross_vfirst, number=10)
print "np.cross"
print timeit.timeit(npcross_vlast, number=10)
print timeit.timeit(npcross_vfirst, number=10)
print timeit.timeit(npcross_vfirst_axisc, number=10)
print all([npcross_vlast()[7,i] == npcross_vfirst()[7,i] ==
npcross_vfirst_axisc()[i,7] == explicitcross_vlast()[7,i] ==
explicitcross_vfirst()[i,7] for i in range(3)]) # check one
explicit
0.0582590103149
0.0560920238495
np.cross
0.399816989899
0.412983894348
0.411231040955
True
3 ответа
Прежде всего, если вы хотите ускорить свой код, вам, вероятно, следует попытаться вообще избавиться от перекрестных продуктов. Это возможно во многих случаях, например, при использовании в сочетании с точечными продуктами <a x b, c x d> = <a, c><b, d> - <a, d><b, c>
,
В любом случае, если вам действительно нужны явные перекрестные продукты, проверьте
eijk = np.zeros((3, 3, 3))
eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1
eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[1, 0, 2] = -1
np.einsum('ijk,aj,ak->ai', eijk, a, b)
np.einsum('iak,ak->ai', np.einsum('ijk,aj->iak', eijk, a), b)
Эти два эквивалентны np.cross
где второй использует два einsum с двумя аргументами каждый, метод, предложенный в аналогичном вопросе.
Результаты неутешительные: оба варианта медленнее, чем np.cross
(кроме крошечных n
):
Сюжет был создан с
import numpy as np
import perfplot
eijk = np.zeros((3, 3, 3))
eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1
eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[1, 0, 2] = -1
perfplot.show(
setup=lambda n: np.random.rand(2, n, 3),
n_range=[2**k for k in range(14)],
kernels=[
lambda X: np.cross(X[0], X[1]),
lambda X: np.einsum('ijk,aj,ak->ai', eijk, X[0], X[1]),
lambda X: np.einsum('iak,ak->ai', np.einsum('ijk,aj->iak', eijk, X[0]), X[1]),
],
labels=['np.cross', 'einsum', 'double einsum'],
xlabel='len(a)',
logx=True,
logy=True,
)
Производительность np.cross
значительно улучшилось в 1.9.x
выпуск NumPy.
%timeit explicitcross_vlast()
%timeit explicitcross_vfirst()
%timeit npcross_vlast()
%timeit npcross_vfirst()
%timeit npcross_vfirst_axisc()
Это время, которое я получаю за 1.8.0
100 loops, best of 3: 4.47 ms per loop
100 loops, best of 3: 4.41 ms per loop
10 loops, best of 3: 29.1 ms per loop
10 loops, best of 3: 29.3 ms per loop
10 loops, best of 3: 30.6 ms per loop
И эти сроки 1.9.0
:
100 loops, best of 3: 4.62 ms per loop
100 loops, best of 3: 4.19 ms per loop
100 loops, best of 3: 4.05 ms per loop
100 loops, best of 3: 4.09 ms per loop
100 loops, best of 3: 4.24 ms per loop
Я подозреваю, что ускорение было введено запросом на слияние # 4338.
Просто меняя vlast
в
def stacked_vlast(a,b):
x = a[:,1]*b[:,2] - a[:,2]*b[:,1]
y = a[:,2]*b[:,0] - a[:,0]*b[:,2]
z = a[:,0]*b[:,1] - a[:,1]*b[:,0]
return np.array([x,y,z]).T
т.е. замена назначения столбца на стек, как (старый) cross
замедляет скорость в 5 раз.
Когда я использую локальную копию разработки cross
функция, я получаю небольшое улучшение скорости по сравнению с вашим explicit_vlast
, Тот cross
использует out
параметр в попытке сократить временные массивы, но мои грубые тесты показывают, что это не имеет большого значения в скорости.
https://github.com/numpy/numpy/blob/master/numpy/core/numeric.py
Если у вас явная версия работает, я бы не стал обновлять numpy
просто чтобы получить этот новый cross
,