问题描述
通常,我会像下面的示例一样在for
循环中反转3x3矩阵的数组.不幸的是,for
循环很慢.有没有更快,更有效的方法来做到这一点?
Normally I would invert an array of 3x3 matrices in a for
loop like in the example below. Unfortunately for
loops are slow. Is there a faster, more efficient way to do this?
import numpy as np
A = np.random.rand(3,3,100)
Ainv = np.zeros_like(A)
for i in range(100):
Ainv[:,:,i] = np.linalg.inv(A[:,:,i])
推荐答案
事实证明,您在numpy.linalg代码中将精力消耗了两层.如果您查看numpy.linalg.inv,您会发现它只是对numpy.linalg.solve(A,inv(A.shape [0])的调用.这具有在每次迭代中重新创建身份矩阵的效果. for循环.由于所有数组的大小都相同,所以浪费时间.通过预分配单位矩阵来跳过此步骤可节省20%的时间(fast_inverse).我的测试表明预分配数组或分配从结果列表中获取并没有多大区别.
It turns out that you're getting burned two levels down in the numpy.linalg code. If you look at numpy.linalg.inv, you can see it's just a call to numpy.linalg.solve(A, inv(A.shape[0]). This has the effect of recreating the identity matrix in each iteration of your for loop. Since all your arrays are the same size, that's a waste of time. Skipping this step by pre-allocating the identity matrix shaves ~20% off the time (fast_inverse). My testing suggests that pre-allocating the array or allocating it from a list of results doesn't make much difference.
再深入一层,您会发现对lapack例程的调用,但该调用包含在多个健全性检查中.如果您除去所有这些,然后在for循环中调用lapack(因为您已经知道矩阵的尺寸,并且也许知道它是真实的,而不是复杂的),则运行的速度会更快(请注意,我已经完成了数组较大):
Look one level deeper and you find the call to the lapack routine, but it's wrapped in several sanity checks. If you strip all these out and just call lapack in your for loop (since you already know the dimensions of your matrix and maybe know that it's real, not complex), things run MUCH faster (Note that I've made my array larger):
import numpy as np
A = np.random.rand(1000,3,3)
def slow_inverse(A):
Ainv = np.zeros_like(A)
for i in range(A.shape[0]):
Ainv[i] = np.linalg.inv(A[i])
return Ainv
def fast_inverse(A):
identity = np.identity(A.shape[2], dtype=A.dtype)
Ainv = np.zeros_like(A)
for i in range(A.shape[0]):
Ainv[i] = np.linalg.solve(A[i], identity)
return Ainv
def fast_inverse2(A):
identity = np.identity(A.shape[2], dtype=A.dtype)
return array([np.linalg.solve(x, identity) for x in A])
from numpy.linalg import lapack_lite
lapack_routine = lapack_lite.dgesv
# Looking one step deeper, we see that solve performs many sanity checks.
# Stripping these, we have:
def faster_inverse(A):
b = np.identity(A.shape[2], dtype=A.dtype)
n_eq = A.shape[1]
n_rhs = A.shape[2]
pivots = zeros(n_eq, np.intc)
identity = np.eye(n_eq)
def lapack_inverse(a):
b = np.copy(identity)
pivots = zeros(n_eq, np.intc)
results = lapack_lite.dgesv(n_eq, n_rhs, a, n_eq, pivots, b, n_eq, 0)
if results['info'] > 0:
raise LinAlgError('Singular matrix')
return b
return array([lapack_inverse(a) for a in A])
%timeit -n 20 aI11 = slow_inverse(A)
%timeit -n 20 aI12 = fast_inverse(A)
%timeit -n 20 aI13 = fast_inverse2(A)
%timeit -n 20 aI14 = faster_inverse(A)
结果令人印象深刻:
20 loops, best of 3: 45.1 ms per loop
20 loops, best of 3: 38.1 ms per loop
20 loops, best of 3: 38.9 ms per loop
20 loops, best of 3: 13.8 ms per loop
编辑:我对解决方案返回的结果不太满意.事实证明,"b"矩阵被覆盖,并且最后包含结果.现在,这段代码给出了一致的结果.
I didn't look closely enough at what gets returned in solve. It turns out that the 'b' matrix is overwritten and contains the result in the end. This code now gives consistent results.
这篇关于有没有办法用numpy有效地反转矩阵数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!