许多小矩阵加速循环

时间:2015-07-22 15:07:21

标签: python for-loop numpy scipy blas

我有一个大的坐标网格(矢量a和b),我为此生成并求解矩阵(10x10)方程。有scipy.linalg.solve接受矢量输入的方法吗?到目前为止,我的解决方案是在坐标数组上运行 循环。

import time
import numpy as np
import scipy.linalg

N = 10
a = np.linspace(0, 1, 10**3)
b = np.linspace(0, 1, 2*10**3)
A = np.random.random((N, N))      # input matrix, not static

def f(a,b,n):     # array-filling function
   return a*b*n

def sol(x,y):   # matrix solver
   D = np.arange(0,N)
   B = f(x,y,D)**2 + f(x-1, y+1, D)      # source vector
   X = scipy.linalg.solve(A,B)
   return X    # output an N-size vector

start = time.time()

answer = np.zeros(shape=(a.size, b.size)) # predefine output array

for egg in range(a.size):             # an ugly double-for cycle
   for ham in range(b.size):
       aa = a[egg]
       bb = b[ham]
       answer[egg,ham] = sol(aa,bb)[0]

print time.time() - start

2 个答案:

答案 0 :(得分:2)

为了说明我对广义ufuncs的观点,以及消除蛋和火腿循环的能力,请考虑以下代码:

import numpy as np
A = np.random.randn(4,4,10,10)
AI = np.linalg.inv(A)
#check that generalized ufuncs work as expected
I = np.einsum('xyij,xyjk->xyik', A, AI)
print np.allclose(I, np.eye(10)[np.newaxis, np.newaxis, :, :])

答案 1 :(得分:1)

@yevgeniy你是对的,有效地解决了多个独立的线性系统 A x = b ,scipy有点棘手(假设 A 数组在每次迭代时都会发生变化)。

例如,这是一个解决1000个系统形式的基准, A x = b ,其中A是10x10矩阵,b a {{ 1}}元素向量。令人惊讶的是,将所有这些放入一个块对角矩阵并且调用10一次的方法对于密集和稀疏矩阵来说确实较慢。

scipy.linalg.solve

具有稀疏数组的线性系统的解决方案更快,但创建此块对角线阵列的时间实际上非常慢。对于密集阵列,在这种情况下它们只是速度较慢(并占用大量RAM)。

也许我错过了一些关于如何使用稀疏数组有效地工作的东西,但是如果你保留import numpy as np from scipy.linalg import block_diag, solve from scipy.sparse import block_diag as sp_block_diag from scipy.sparse.linalg import spsolve N = 10 M = 1000 # number of coordinates Ai = np.random.randn(N, N) # we can compute the inverse here, # but let's assume that Ai are different matrices in the for loop loop bi = np.random.randn(N) %timeit [solve(Ai, bi) for el in range(M)] # 10 loops, best of 3: 32.1 ms per loop Afull = sp_block_diag([Ai]*M, format='csr') bfull = np.tile(bi, M) %timeit Afull = sp_block_diag([Ai]*M, format='csr') %timeit spsolve(Afull, bfull) # 1 loops, best of 3: 303 ms per loop # 100 loops, best of 3: 5.55 ms per loop Afull = block_diag(*[Ai]*M) %timeit Afull = block_diag(*[Ai]*M) %timeit solve(Afull, bfull) # 100 loops, best of 3: 14.1 ms per loop # 1 loops, best of 3: 23.6 s per loop 循环,那么你可以做两件事来进行优化。

从纯python中,查看scipy.linalg.solve的源代码:删除不必要的测试并分解循环外的所有重复操作。例如,假设您的数组不是对称的正数,我们可以做

for

导致6.5倍的加速。

如果你需要更好的性能,你必须在Cython中将它移植到for循环中并直接在C中连接from scipy.linalg import get_lapack_funcs gesv, = get_lapack_funcs(('gesv',), (Ai, bi)) def solve_opt(A, b, gesv=gesv): # not sure if copying A and B is necessary, but just in case (faster if arrays are not copied) lu, piv, x, info = gesv(A.copy(), b.copy(), overwrite_a=False, overwrite_b=False) if info == 0: return x if info > 0: raise LinAlgError("singular matrix") raise ValueError('illegal value in %d-th argument of internal gesv|posv' % -info) %timeit [solve(Ai, bi) for el in range(M)] %timeit [solve_opt(Ai, bi) for el in range(M)] # 10 loops, best of 3: 30.1 ms per loop # 100 loops, best of 3: 3.77 ms per loop BLAS函数,如所讨论的here,或者更好地使用Cython API for BLAS / Scacky 0.16中的LAPACK。

编辑:正如@Eelco Hoogendoorn所说,如果你的 A 矩阵是固定的,那么有一种更简单,更有效的方法。