标度/旋转成对平方欧几里德距离的矢量化计算

时间:2019-04-25 13:16:33

标签: python numpy vectorization numpy-broadcasting

给出一组存储在(n,d)数组中的维度为 d n 个向量,以及第二组我想计算相同尺寸的m 个向量(存储在(m,d)数组中),计算向量之间的平方点方均距离,并由一些矩阵A缩放,其大小为(d,d)

输出应为(n,m)数组。

我希望 m n 的输入范围在1到10.000之间,而 d 的输入范围在1到100之间。

两点之间的距离由下式给出:

d_i_j = (v1_i - v2_j)^T A (v1_i - v2_j)

在未优化但有效的python代码中,该代码如下所示:

import numpy as np

v1 = np.array([[1, 2],
               [3, 4],
               [4, 5]])

v2 = np.array([[1,1],
               [2, 2],
               [2, 2],
               [0, 0]])

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

d = np.zeros((3, 4))

for i in range(0,3):
    for j in range(0,4):
        d[i,j] = (v1[i,:] - v2[j,:]).T @ A @ (v1[i,:] - v2[j,:])

示例点之间的平方距离是:

d = [[  3.   1.   1.  17.]
 [ 43.  17.  17.  81.]
 [ 81.  43.  43. 131.]]

是否有此版本,可避免python中的嵌套循环,例如使用广播黑魔法?

编辑:

视情况

A = np.array([[1,0], [0, 1]])

这是可以计算出的正常平方欧几里德距离

from scipy.spatial.distance import cdist

cdist(v1,v2,'sqeuclidean')

1 个答案:

答案 0 :(得分:1)

我们可以使用np.einsum-

V = v1[:,None,:]-v2
d_out = np.einsum('ijk,kl,ijl->ij',V,A,V)

另外,通过将其设置为optimize以使用BLAS,在np.einsum中使用True标志。

向量化方法的说明

原始代码为-

d[i,j] = (v1[i,:] - v2[j,:]).T @ A @ (v1[i,:] - v2[j,:])

I。我们正在翻译:

v1[i,:] - v2[j,:]

使用broadcasting进行外部操作:

v1[:,None,:]-v2

示意性地放在:

v1[:,None,:]  :  m x 1 x n
v2            :      m x n
output, V     :  m x m x n

More info on outer explanation.

可以在docs中找到有关broadcasting的更多信息。

II。接下来,使用(v1[i,:] - v2[j,:]).T @ A @ (v1[i,:] - v2[j,:])的字符串符号将带有新V的{​​{1}}变成np.einsum('ijk,kl,ijl->ij',V,A,V)。可以在docs中找到更多信息。