Python numpy中的快速标量三重积
我有很多向量三元组,我想计算它们的值。我能行Python numpy中的快速标量三重积,python,numpy,Python,Numpy,我有很多向量三元组,我想计算它们的值。我能行 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 n=871 a=numpy.random.rand(n,3) b=
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
n=871
a=numpy.random.rand(n,3)
b=numpy.random.rand(n,3)
c=numpy.random.rand(n,3)
#
ω=numpy.einsum('ij,ij->i',a,numpy.cross(b,c))
但是numpy.cross
相当慢。这个问题的对称性(它的Levi-Civita表达式是eps_{ijk}a_i b_j c_k)表明可能有更好(更快)的方法来计算它,但我似乎无法理解
有什么提示吗?这只是决定因素
omega=det(dstack([a,b,c]))
但是速度慢了
另一个等价解是ω=点(a,交叉点(b,c))。和(1)
但我认为你必须为每个det计算大约9(交叉)+3(点)+2(求和)=14次运算,所以它似乎接近最优。在numpy,你最多只能赢得两个因素
编辑:
如果速度很关键,你必须以低水平前进numba
是一种简单的方法,这里的系数为15倍:
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
下面是一种使用
切片
和求和的方法-
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,)
形状的数组。利用所提出的切片(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
我对答案中提到的方法做了比较。
结果是:
@迪瓦卡一点一点地击败了埃因苏姆十字军
为了完整性起见,请允许我注意,还有另一种方法完全依赖于点积和sqrt,请参阅。该方法比einsum交叉和切片求和略慢
这个情节是用
导入numpy
导入性能图
def einsum_交叉点(数据):
a、 b,c=数据
返回numpy.einsum(“ij,ij->i”,a,numpy.cross(b,c))
def det(数据):
a、 b,c=数据
返回numpy.linalg.det(numpy.dstack([a,b,c]))
def切片和(数据):
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]
返回a[:,0]*c0+a[:,1]*c1+a[:,2]*c2
perfplot.show(
设置=λn:(
numpy.random.rand(n,3),
numpy.random.rand(n,3),
numpy.random.rand(n,3),
),
n_范围=[2**k表示范围(1,20)中的k],
内核=[einsum\u交叉、det、切片\u和],
logx=True,
逻辑=正确,
)
来自numpy.linalg.det
:linalgeror:数组的最后两个维度必须是正方形
。请注意,我需要一次计算许多标量三重积。我更正了det版本,但它没有优化。我添加了一个其他的解决方案,希望能有所帮助!当其他一切都失败时!:)过去的问题表明,对于它所做的事情,当前的交叉
是有效的,仅交叉积就可以了,这是一个点积,它赋予了它一种特殊的对称性。我觉得奇怪的是,不应该对它进行进一步优化。如果A、B和C是特定的3向量,那么你要寻找的爱因斯坦符号是\epsilon{ijk}A^i B^j C^k
,而epsilon是Levi Civita符号。它不认为这是在numpy.einsum
中实现的东西。正如@B.M.所说,它完全等同于np.dstack(A,B,C)
。Levi Civita之前在einsum
中的用法。新的optimize
可能会提高这种性能。