numpy中的快速标量三重产品

时间:2017-02-10 11:24:59

标签: python numpy

我有大量的矢量三元组,我想为它们计算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)表明可能有更好(更快)的方法来计算它,但我似乎无法弄明白。

任何提示?

3 个答案:

答案 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)

我已经对答案中提到的方法进行了比较。 结果:

enter image description here

@Divakar's击败了einsum-cross one。

为了完整起见,请注意,还有另一种方法完全依赖点积和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,
        )