我有大量的矢量三元组,我想为它们计算scalar triple product。我能做到
import numpy
n = 871
a = numpy.random.rand(n, 3)
b = numpy.random.rand(n, 3)
c = numpy.random.rand(n, 3)
# <a, b x c>
omega = numpy.einsum('ij, ij->i', a, numpy.cross(b, c))
但是numpy.cross
相当慢。问题的对称性(其Levi-Civita表达式为eps_{ijk} a_i b_j c_k
)表明可能有更好(更快)的方法来计算它,但我似乎无法弄明白。
任何提示?
答案 0 :(得分:3)
它只是决定因素。
omega=det(dstack([a,b,c]))
但它慢了......
另一个等效的解决方案是omega=dot(a,cross(b,c)).sum(1)
。
但我认为你必须为每个det计算大约9(对于交叉)+ 3(对于点)+ 2(对于总和)= 14个操作,因此它似乎接近最优。在最好的情况下,你将赢得numpy中的两个因素。
修改强>
如果速度至关重要,则必须降低速度。 numba
是一种简单的方法,可以在15X因素中实现这一目标:
from numba import njit
@njit
def multidet(a,b,c):
n=a.shape[0]
d=np.empty(n)
for i in range(n):
u,v,w=a[i],b[i],c[i]
d[i]=\
u[0]*(v[1]*w[2]-v[2]*w[1])+\
u[1]*(v[2]*w[0]-v[0]*w[2])+\
u[2]*(v[0]*w[1]-v[1]*w[0]) # 14 operations / det
return d
一些测试:
In [155]: %timeit multidet(a,b,c)
100000 loops, best of 3: 7.79 µs per loop
In [156]: %timeit numpy.einsum('ij, ij->i', a, numpy.cross(b, c))
10000 loops, best of 3: 114 µs per loop
In [159]: allclose(multidet(a,b,c),omega)
Out[159]: True
答案 1 :(得分:1)
这是一种利用def slicing_summing(a,b,c):
c0 = b[:,1]*c[:,2] - b[:,2]*c[:,1]
c1 = b[:,2]*c[:,0] - b[:,0]*c[:,2]
c2 = b[:,0]*c[:,1] - b[:,1]*c[:,0]
return a[:,0]*c0 + a[:,1]*c1 + a[:,2]*c2
和求和 -
c0, c1, c2
我们可以使用单行替换计算b[:,[1,2,0]]*c[:,[2,0,1]] - b[:,[2,0,1]]*c[:,[1,2,0]]
及其堆叠版本的前三个步骤,如此 -
(n,3)
这将创建另一个a
数组,该数组必须与(n,)
一起用于减少和,从而产生slicing_summing
形状的数组。使用建议的(n,)
方法,我们直接使用这三个切片的求和来获得(n,3)
形状的数组,从而避免使用中间In [86]: # Setup inputs
...: n = 871
...: a = np.random.rand(n, 3)
...: b = np.random.rand(n, 3)
...: c = np.random.rand(n, 3)
...:
In [87]: # Original approach
...: omega = np.einsum('ij, ij->i', a, np.cross(b, c))
In [88]: np.allclose(omega, slicing_summing(a,b,c))
Out[88]: True
数组。
示例运行 -
In [90]: %timeit np.einsum('ij, ij->i', a, np.cross(b, c))
10000 loops, best of 3: 84.6 µs per loop
In [91]: %timeit slicing_summing(a,b,c)
1000 loops, best of 3: 63 µs per loop
运行时测试 -
define()
答案 2 :(得分:1)
我已经对答案中提到的方法进行了比较。 结果:
为了完整起见,请注意,还有另一种方法完全依赖点积和sqrt,请参阅here。该方法比einsum-cross和slice-sum略慢。
情节是使用perfplot,
创建的import numpy
import perfplot
def einsum_cross(data):
a, b, c = data
return numpy.einsum('ij, ij->i', a, numpy.cross(b, c))
def det(data):
a, b, c = data
return numpy.linalg.det(numpy.dstack([a, b, c]))
def slice_sum(data):
a, b, c = data
c0 = b[:, 1]*c[:, 2] - b[:, 2]*c[:, 1]
c1 = b[:, 2]*c[:, 0] - b[:, 0]*c[:, 2]
c2 = b[:, 0]*c[:, 1] - b[:, 1]*c[:, 0]
return a[:, 0]*c0 + a[:, 1]*c1 + a[:, 2]*c2
perfplot.show(
setup=lambda n: (
numpy.random.rand(n, 3),
numpy.random.rand(n, 3),
numpy.random.rand(n, 3)
),
n_range=[2**k for k in range(1, 20)],
kernels=[einsum_cross, det, slice_sum],
logx=True,
logy=True,
)