在einsum中

时间:2016-02-22 08:37:54

标签: python numpy optimization linear-algebra numpy-einsum

我已阅读einsum manual和ajcr的basic introduction

我在非编码环境中没有使用爱因斯坦求和的经验,尽管我已经尝试通过一些互联网研究来解决这个问题(提供链接但是还没有两个以上的声誉)。我也尝试用einsum在python中进行实验,看看能不能更好地处理事情。

然而,我仍然不清楚它是否可行且有效,如下所示:

在两个相等长度(3)和高度(n)的数组(a和b)数组上,逐行产生(行i:a在b上)加上外积(行i: b)a),然后将所有外积矩阵求和,输出一个最终矩阵。

我知道'我,j-> ij'会在另一个矢量上产生一个矢量的外积 - 这是我失去了我的下一步。 ('ijk,jik-> ij'绝对不是它)

我的另一个可用选项是循环遍历数组并从我用cython编写的函数调用基本函数(双外积和矩阵加法)(使用numpy内置的外部和求和函数不是选项,它太慢了)。我很可能最终将循环本身移动到cython。

这样:

  1. 我如何才能简单地表达上述程序?

  2. 它会比在cython中做所有事情提供真正的收获吗?还是有其他我不知道的替代方案? (包括我使用numpy的可能性低于我可能......)

  3. 感谢。

    使用示例进行编辑:

    A=np.zeros((3,3))
    arrays_1=np.array([[1,0,0],[1,2,3],[0,1,0],[3,2,1]])
    arrays_2=np.array([[1,2,3],[0,1,0],[1,0,0],[3,2,1]])
    for i in range(len(arrays_1)):
      A=A+(np.outer(arrays_1[i], arrays_2[i])+np.outer(arrays_2[i],arrays_1[i]))
    

    (但是,请注意,实际上我们正在处理更长的数组(即每个内部成员仍然长度为3,但最多只有几千个这样的成员),此部分代码(不可避免地)被多次调用)

    万一它有用,这里是两个外部产品总和的cython:

    def outer_product_sum(np.ndarray[DTYPE_t, ndim=1] a_in, np.ndarray[DTYPE_t, ndim=1] b_in):
        cdef double *a = <double *>a_in.data
        cdef double *b = <double *>b_in.data
        return np.array([
    [a[0]*b[0]+a[0]*b[0], a[0]*b[1]+a[1]*b[0], a[0] * b[2]+a[2] * b[0]],
    [a[1]*b[0]+a[0]*b[1], a[1]*b[1]+a[1]*b[1], a[1] * b[2]+a[2] * b[1]],
    [a[2]*b[0]+a[0]*b[2], a[2]*b[1]+a[1]*b[2], a[2] * b[2]+a[2] * b[2]]])
    

    现在,我从'i in range(len(array))'循环中调用,如上所示。

2 个答案:

答案 0 :(得分:1)

爱因斯坦求和只能用于问题的乘法部分(即外部产品)。幸运的是,求和不必以元素方式执行,但您可以在reduce矩阵上执行此操作。使用示例中的数组:

arrays_1 = np.array([[1,0,0],[1,2,3],[0,1,0],[3,2,1]])
arrays_2 = np.array([[1,2,3],[0,1,0],[1,0,0],[3,2,1]])
A = np.einsum('ki,kj->ij', arrays_1, arrays_2) + np.einsum('ki,kj->ij', arrays_2, arrays_1)

输入数组的形状为(4,3),求和发生在第一个索引(名为'k')上。如果应在第二个索引上进行求和,请将下标字符串更改为'ik,jk->ij'

答案 1 :(得分:1)

无论您使用np.einsum做什么,通常都可以使用np.dot做得更快。在这种情况下,A是两个点积的总和:

arrays_1 = np.array([[1,0,0],[1,2,3],[0,1,0],[3,2,1]])
arrays_2 = np.array([[1,2,3],[0,1,0],[1,0,0],[3,2,1]])

A1 = (np.einsum('ki,kj->ij', arrays_1, arrays_2) +
      np.einsum('ki,kj->ij', arrays_2, arrays_1))

A2 = arrays_1.T.dot(arrays_2) + arrays_2.T.dot(arrays_1)

print(np.allclose(A1, A2))
# True

%timeit (np.einsum('ki,kj->ij', arrays_1, arrays_2) +
         np.einsum('ki,kj->ij', arrays_2, arrays_1))
# 100000 loops, best of 3: 7.51 µs per loop

%timeit arrays_1.T.dot(arrays_2) + arrays_2.T.dot(arrays_1)
# 100000 loops, best of 3: 4.51 µs per loop