Numpy matrix trickery - сумма матриц обратных времен

Я пытаюсь сделать следующее и повторять до сближения:

JbF9ech.png

где каждый X i равен n x p, и из них r из них в массиве r x n x p, который называется samples. U - n x n, V - p x p. (Я получаю MLE нормальное распределение матрицы.) Размеры все потенциально большие; Я ожидаю вещей, по крайней мере, порядка r = 200, n = 1000, p = 1000.

Мой текущий код

V = np.einsum('aji,jk,akl->il', samples, np.linalg.inv(U) / (r*n), samples)
U = np.einsum('aij,jk,alk->il', samples, np.linalg.inv(V) / (r*p), samples)

Это работает нормально, но, конечно же, вы никогда не должны находить обратные и умножаемые вещи. Было бы также хорошо, если бы я мог каким-то образом использовать тот факт, что U и V симметричны и позитивно определены. Мне бы хотелось просто вычислить коэффициент Холески U и V на итерации, но я не знаю, как это сделать из-за суммы.

Я мог бы избежать обратного, делая что-то вроде

V = sum(np.dot(x.T, scipy.linalg.solve(A, x)) for x in samples)

(или что-то подобное, которое использовало psd-ness), но затем существует цикл Python, и это заставляет плакать числовые феи.

Я мог бы также представить изменение формы samples таким образом, что я мог бы получить массив A^-1 x, используя solve для каждого x, не выполняя цикл Python, но это делает большой вспомогательный массив, который потеря памяти.

Есть ли какая-то линейная алгебра или трюк numpy, который я могу сделать, чтобы получить лучшее из всех трех: нет явных инверсий, нет цикла Python и нет больших массивов aux? Или это лучший способ реализовать тот, у кого есть цикл Python, на более быстром языке и вызывать его? (Просто портировать его непосредственно в Cython может помочь, но все равно будет задействован много вызовов метода Python, но, возможно, не будет слишком трудным делать соответствующие процедуры blas/lackack напрямую без особых проблем.)

(Как оказалось, на самом деле мне не нужны матрицы U и V в конце - только их детерминанты или фактически только детерминант их продукта Кронекера. Поэтому, если у кого-то есть умная идея для как делать меньше работы и по-прежнему вызывать детерминанты, это было бы высоко оценено.)

Ответ 1

Пока кто-то придумает более вдохновенный ответ, если бы я был вами, я бы позволил фей плакать...

r, n, p = 200, 400, 400

X = np.random.rand(r, n, p)
U = np.random.rand(n, n)

In [2]: %timeit np.sum(np.dot(x.T, np.linalg.solve(U, x)) for x in X)
1 loops, best of 3: 9.43 s per loop

In [3]: %timeit np.dot(X[0].T, np.linalg.solve(U, X[0]))
10 loops, best of 3: 45.2 ms per loop

Таким образом, имея петлю на питоне и сводя вместе все результаты, занимает 390 мс более 200 раз, что необходимо для решения каждой из 200 систем, которые необходимо решить. Вы получите менее 5% улучшения, если цикл и суммирование были бесплатными. Возможно, некоторые вызовы также накладные функции функции python, но это, вероятно, все же будет незначительным против фактического времени решения уравнений, независимо от того, на каком языке вы его кодируете.