9

我有一个k*n矩阵 X 和一个k*k矩阵 A。对于 的每一列X,我想计算标量

X[:, i].T.dot(A).dot(X[:, i])

(或者,在数学上,Xi' * A * Xi)。

目前,我有一个for循环:

out = np.empty((n,))
for i in xrange(n):
    out[i] = X[:, i].T.dot(A).dot(X[:, i])

但由于n它很大,如果可能的话,我想更快地执行此操作(即使用一些 NumPy 函数而不是循环)。

4

3 回答 3

8

这似乎做得很好: (X.T.dot(A)*X.T).sum(axis=1)

编辑:这有点快。np.einsum('...i,...i->...', X.T.dot(A), X.T). X如果并且A是 Fortran 连续的,两者都可以更好地工作。

于 2013-08-30T22:24:21.087 回答
4

您可以使用numpy.einsum

np.einsum('ji,jk,ki->i',x,a,x)

这将得到相同的结果。让我们看看它是否更快:

在此处输入图像描述

看起来dot仍然是最快的选择,特别是因为它使用线程 BLAS,而不是einsum在一个内核上运行。

import perfplot
import numpy as np


def setup(n):
    k = n
    x = np.random.random((k, n))
    A = np.random.random((k, k))
    return x, A


def loop(data):
    x, A = data
    n = x.shape[1]
    out = np.empty(n)
    for i in range(n):
        out[i] = x[:, i].T.dot(A).dot(x[:, i])
    return out


def einsum(data):
    x, A = data
    return np.einsum('ji,jk,ki->i', x, A, x)


def dot(data):
    x, A = data
    return (x.T.dot(A)*x.T).sum(axis=1)


perfplot.show(
    setup=setup,
    kernels=[loop, einsum, dot],
    n_range=[2**k for k in range(10)],
    logx=True,
    logy=True,
    xlabel='n, k'
    )
于 2013-08-30T22:16:34.843 回答
0

除非您将整个事情并行化,否则您无法更快地做到这一点:每列一个线程。你仍然会使用循环——你无法摆脱它。

Map reduce 是解决这个问题的好方法:map step multiples,reduce step sums。

于 2013-08-30T21:44:34.797 回答