【问题标题】:Vectorized (partial) inverse of an N*M*M tensor with numpy带有 numpy 的 N*M*M 张量的矢量化(部分)逆
【发布时间】:2013-07-29 06:21:44
【问题描述】:

一年多前,我和这里的提问者几乎完全一样: fast way to invert or dot kxnxn matrix

所以我有一个具有维度 (N,M,M) 的索引 a[n,i,j] 的张量,我想为 N 中的每个 n 反转 M*M 方阵部分。

例如,假设我有

In [1]:    a = np.arange(12)
           a.shape = (3,2,2)
           a

Out[1]: array([[[ 0,  1],
                  [ 2,  3]],

                  [[ 4,  5],
                  [ 6,  7]],

                  [[ 8,  9],
                  [10, 11]]])

然后 for 循环反转将如下所示:

In [2]: inv_a = np.zeros([3,2,2])
        for m in xrange(0,3):
            inv_a[m] = np.linalg.inv(a[m])
        inv_a

Out[2]: array([[[-1.5,  0.5],
                  [ 1. ,  0. ]],

                  [[-3.5,  2.5],
                  [ 3. , -2. ]],

                  [[-5.5,  4.5],
                 [ 5. , -4. ]]])

这显然将在 NumPy 2.0 中实现,根据 github 上的this issue...

我想我需要按照 github 问题线程中提到的 seberg 安装开发版本,但是现在有没有其他方法可以以 vectorized 方式执行此操作?

【问题讨论】:

  • 答案是否定的,但还不错:Gauss-Jordan matrix inversion is an O(M^3) operation,所以它将主导性能,除非N >>> M^3。
  • 谢谢你的好点,杰米!顺便说一句,当我的代码正常工作时,N 大约在 10^3...10^5 和 M 之间 2...6 并且可能更高...
  • 如果主 for 循环是问题所在,也许 Cython 可以提供帮助。在 for 循环的中间会有一个 Python 函数调用,但应该还是会比较快。
  • 鉴于这已在即将发布的版本中“修复”,我不知道是否值得尝试过于聪明。但是,您可以将矩阵视为块对角线或带状矩阵。 scipy.linalg 中有用于带状矩阵的例程。由于 M 不是太大,您可以使用它们(例如,solved_banded,如果您的矩阵有更多结构,则可以使用它们)。当然你还得建带状矩阵,带状里面还是会有很多零等等,所以不知道你最后会不会赢。
  • 嗯,好点,克雷格...我想我会试试带状矩阵!我想生成非常大的零数组并不是很耗时......

标签: python numpy matrix scipy vectorization


【解决方案1】:

更新: 在 NumPy 1.8 及更高版本中,numpy.linalg 中的函数是泛化的通用函数。 这意味着您现在可以执行以下操作:

import numpy as np
a = np.random.rand(12, 3, 3)
np.linalg.inv(a)

这将反转每个 3x3 数组并将结果作为 12x3x3 数组返回。 请参阅numpy 1.8 release notes


原答案:

由于N 相对较小,我们如何一次手动计算所有矩阵的 LU 分解。 这确保了所涉及的 for 循环相对较短。

以下是使用普通 NumPy 语法的方法:

import numpy as np
from numpy.random import rand

def pylu3d(A):
    N = A.shape[1]
    for j in xrange(N-1):
        for i in xrange(j+1,N):
            #change to L
            A[:,i,j] /= A[:,j,j]
            #change to U
            A[:,i,j+1:] -= A[:,i,j:j+1] * A[:,j,j+1:]

def pylusolve(A, B):
    N = A.shape[1]
    for j in xrange(N-1):
        for i in xrange(j+1,N):
            B[:,i] -= A[:,i,j] * B[:,j]
    for j in xrange(N-1,-1,-1):
        B[:,j] /= A[:,j,j]
        for i in xrange(j):
            B[:,i] -= A[:,i,j] * B[:,j]

#usage
A = rand(1000000,3,3)
b = rand(3)
b = np.tile(b,(1000000,1))
pylu3d(A)
# A has been replaced with the LU decompositions
pylusolve(A, b)
# b has been replaced to the solutions of
# A[i] x = b[i] for each A[i] and b[i]

正如我所写,pylu3d 修改 A 以计算 LU 分解。 在用其 LU 分解替换每个 NxN 矩阵后,pylusolve 可用于求解代表矩阵系统右侧的 MxN 数组 b。 它会修改b 并进行适当的反向替换以解决系统问题。 正如它所写的那样,这个实现不包括旋转,所以它在数值上不稳定,但在大多数情况下它应该可以很好地工作。

根据您的数组在内存中的排列方式,使用 Cython 可能仍然要快一些。 这里有两个 Cython 函数做同样的事情,但它们首先迭代 M。 它没有矢量化,但速度相对较快。

from numpy cimport ndarray as ar
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def lu3d(ar[double,ndim=3] A):
    cdef int n, i, j, k, N=A.shape[0], h=A.shape[1], w=A.shape[2]
    for n in xrange(N):
        for j in xrange(h-1):
            for i in xrange(j+1,h):
                #change to L
                A[n,i,j] /= A[n,j,j]
                #change to U
                for k in xrange(j+1,w):
                    A[n,i,k] -= A[n,i,j] * A[n,j,k]

@cython.boundscheck(False)
@cython.wraparound(False)
def lusolve(ar[double,ndim=3] A, ar[double,ndim=2] b):
    cdef int n, i, j, N=A.shape[0], h=A.shape[1]
    for n in xrange(N):
        for j in xrange(h-1):
            for i in xrange(j+1,h):
                b[n,i] -= A[n,i,j] * b[n,j]
        for j in xrange(h-1,-1,-1):
            b[n,j] /= A[n,j,j]
            for i in xrange(j):
                b[n,i] -= A[n,i,j] * b[n,j]

您也可以尝试使用 Numba,但在这种情况下我无法让它像 Cython 一样快。

【讨论】:

  • 非常感谢 IanH 提供的出色解决方案!我认为相当小的矩阵的显式反演公式将比 pylusolve 方法更快(?)我还没有了解 Cython,但我也会尽快尝试。再次感谢! :)
  • 是的,没问题。祝你好运!
  • 此外,在显式反演公式中,问题是您必须单独实现它们,但您可能已经知道这一点。我不完全确定哪个会更快,lusolve 函数代替了乘法。这两个操作都是 O(N^3),但矩阵乘法函数通常都经过了很好的优化,因此可能会有一些速度增益。这可能取决于数据在内存中的排列方式。
猜你喜欢
  • 2020-11-18
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 2017-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多