2016-04-08 19 views
3

我正在嘗試計算複雜的二維數組x自身的成對np.vdot。所以我想要的行爲是:使用Numpy的成對虛擬點

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]) 

有沒有辦法做到這一點沒有顯式循環?我嘗試從sklearn使用pairwise_kernel,但它假定輸入數組是實數。我也試過廣播,但vdot平坦的投入。

回答

3
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]是針對所有的 i,j,k計算的。總和取自重複的下標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 
3

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