2013-08-30 2 views
5

У меня есть k*n матрица X, и k*k матрицы A. Для каждого столбца X, я хотел бы, чтобы вычислить скалярноеВычислить «v^TA v» для матрицы векторов v

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 вместо цикла).

ответ

5

Это, кажется, сделать это красиво: (X.T.dot(A)*X.T).sum(axis=1)

Edit: Это немного быстрее. np.einsum('...i,...i->...', X.T.dot(A), X.T). Оба работают лучше, если X и A являются фортранами непрерывными.

+0

Появляется на * handily * beat мой исходный код: для 'n = 10000, k = 10', мой код - 76,2мс, новый код - 1.64ms *. Ницца! – nneonneo

+0

Это намного быстрее, чем 'np.einsum' для высоких' n', когда ATLAS масштабируется до более 1 ядра ... Я добавил некоторые тайминги к ответу ниже ... –

0

Вы не можете сделать это быстрее, если вы не распараллелите все это: один поток за столбец. Вы по-прежнему будете использовать циклы - вы не можете уйти от этого.

Сокращение карты - это хороший способ взглянуть на эту проблему: умножить кратные шаги, уменьшить суммы шага.

+1

Конечно, я не могу ускориться с точки зрения сложности, но избегая циклов Python (в пользу конструкций NumPy) обычно обеспечивает ускорение просто, избегая более медленного кода Python. – nneonneo

3

Вы можете использовать numpy.einsum:

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

Это позволит получить тот же результат. Давайте посмотрим, если это намного быстрее:

enter image description here

Похоже 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' 
    ) 
+1

Это значительно медленнее для больших размеров на современных процессорах благодаря возможности использования резьбового BLAS. – Daniel

+0

@Ophion хорошая точка, но я считаю, что она все равно будет быстрее, чем цикл Python 'for' ... что-то стоит проверить –

+0

Python' for' loop cython/numpy 'for' loop не имеет значения. Время действительно не в цикле. – Daniel

Смежные вопросы