如何加快矢量叉积计算

时间:2014-01-03 16:55:15

标签: python performance numpy outer-join

嗨,我在这里比较新,并尝试用numpy做一些计算。我从一次特定的计算中经历了很长的一段时间,并且无法以更快的方式实现同​​样的目标。

基本上它是光线三角形交叉算法的一部分,我需要从两个不同大小的矩阵计算所有矢量交叉乘积。

我使用的代码是:

allhvals1 = numpy.cross( dirvectors[:,None,:], trivectors2[None,:,:] )

其中dirvectorsn* vectors (xyz)trivectors2的数组,是m*vectors(xyz)的数组。 allhvals1是大小为n*M*vector (xyz)的交叉积的数组。 这有效,但速度很慢。它基本上是来自每个阵列的每个矢量的n * m矩阵。希望你明白。每个的大小从大约1-4000变化,具体取决于参数(我基本上根据大小来设置dirvectors)。

任何建议表示赞赏。不幸的是,我的矩阵数学有点不稳定。

2 个答案:

答案 0 :(得分:16)

如果查看np.cross的{​​{3}},它基本上将xyz维度移动到所有数组的形状元组的前面,然后计算每个组件拼写如下:

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]

在您的情况下,每个产品都需要分配大型数组,因此整体行为效率不高。

让我们设置一些测试数据:

u = np.random.rand(1000, 3)
v = np.random.rand(2000, 3)

In [13]: %timeit s1 = np.cross(u[:, None, :], v[None, :, :])
1 loops, best of 3: 591 ms per loop

我们可以尝试按如下方式计算the source codenp.einsum

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

In [14]: %timeit s2 = np.einsum('ijk,uj,vk->uvi', eijk, u, v)
1 loops, best of 3: 706 ms per loop

In [15]: np.allclose(s1, s2)
Out[15]: True

因此,虽然它有效,但它的性能更差。问题是np.einsum在有两个以上的操作数时遇到麻烦,但已经优化了两个或更少的路径。所以我们可以尝试分两步重写它,看它是否有帮助:

In [16]: %timeit s3 = np.einsum('iuk,vk->uvi', np.einsum('ijk,uj->iuk', eijk, u), v)
10 loops, best of 3: 63.4 ms per loop

In [17]: np.allclose(s1, s3)
Out[17]: True

宾果!接近一个数量级的改进......

NumPy 1.11.0的一些效果数据a=numpy.random.rand(n,3)b=numpy.random.rand(n,3)

using Levi-Civita symbols

对于测试的最大einsum,嵌套cross的速度约为n的两倍。

答案 1 :(得分:0)

在为水下航行器编写动态模拟时,我发现了这种快速交叉产品的方法:

https://github.com/simena86/Simulink-Underwater-Robotics-Simulator/blob/master/3rdparty/gnc_mfiles/Smtrx.m

哪个效果很好,它是用Matlab编写的,但代码很简单。请阅读顶部的评论。