使用Numpy的成对vdot

时间:2016-04-08 01:18:51

标签: python numpy vectorization

我试图用自己计算复杂2D数组np.vdot的成对x。所以我想要的行为是:

X = np.empty((x.shape[0], x.shape[0]), dtype='complex128')
for i in range(x.shape[0]):
    for j in range(x.shape[0]):
        X[i, j] = np.vdot(x[i], x[j])

有没有办法在没有显式循环的情况下执行此操作?我尝试使用pairwise_kernel中的sklearn,但它假设输入数组是实数。我也试过广播,但vdot使其输入变得扁平。

2 个答案:

答案 0 :(得分:4)

X = np.einsum('ik,jk->ij', np.conj(x), x)

相当于

X = np.empty((x.shape[0], x.shape[0]), dtype='complex128')
for i in range(x.shape[0]):
    for j in range(x.shape[0]):
        X[i, j] = np.vdot(x[i], x[j])

np.einsum 需要一些产品。下标'ik,jk->ij'告诉np.einsum第二个参数, np.conj(x)是一个包含下标ik的数组,第三个参数是x 下标jk。因此,为所有人计算乘积np.conj(x)[i,k]*x[j,k] ijk。总和取自重复的下标k,从那以后 留下ij,它们将成为结果数组的下标。

例如,

import numpy as np

N, M = 10, 20
a = np.random.random((N,M))
b = np.random.random((N,M))
x = a + b*1j

def orig(x):
    X = np.empty((x.shape[0], x.shape[0]), dtype='complex128')
    for i in range(x.shape[0]):
        for j in range(x.shape[0]):
            X[i, j] = np.vdot(x[i], x[j])
    return X

def alt(x):
    return np.einsum('ik,jk->ij', np.conj(x), x)

assert np.allclose(orig(x), alt(x))
In [307]: %timeit orig(x)
10000 loops, best of 3: 143 µs per loop

In [308]: %timeit alt(x)
100000 loops, best of 3: 8.63 µs per loop

答案 1 :(得分:4)

要将np.vdot扩展到所有行,您可以使用np.tensordot,我会直接从@unutbu's solution借用共轭点,就像这样 -

np.tensordot(np.conj(x),x,axes=(1,1))

基本上使用np.tensordot,我们指定要减少的轴,在这种情况下,它是x的共轭版本的最后一个轴和数组本身,当应用于这两个轴时。

运行时测试 -

让时间@unutbu's solution with np.einsum以及此帖中提出的解决方案 -

In [27]: import numpy as np # From @unutbu's` solution again
    ...: 
    ...: N, M = 1000, 1000
    ...: a = np.random.random((N,M))
    ...: b = np.random.random((N,M))
    ...: x = a + b*1j
    ...: 

In [28]: %timeit np.einsum('ik,jk->ij', np.conj(x), x) # @unutbu's` solution
1 loops, best of 3: 4.45 s per loop

In [29]: %timeit np.tensordot(np.conj(x),x,axes=(1,1))
1 loops, best of 3: 3.76 s per loop