我需要一种有效的方法来对python中的7000x7000空气动力学影响系数(密集)矩阵求逆。我是在FORTRAN例程使用LAPACK的LU分解例程处理该问题之前开始的,我发现该例程在其他相关应用程序中的使用效率很高。不过,我已经读到,NumPy和SciPy线性系统求解器主要基于对C中相同LAPACK / BLAS函数的直接调用,并且想知道是否切换到FORTRAN确实可以将计算时间减少到可以放弃的水平一种更简单,更高级的语言。
如果有python求解器可以保证该大小(1000到10000,正方形)的矩阵具有相似的性能,是吗?
我确实需要矩阵逆,所以切换到迭代Ax = b解决方案不是一种选择。
的确,Numpy和Scipy有效地调用LAPACK例程来执行numpy.linalg.inv
和numpy.linalg.inv
。
为了使通用矩阵求逆,scipy.linalg.inv
求解scipy.linalg.inv
。函数numpy.linalg.inv
调用numpy.linalg.inv
,其中A.x=np.eye((n,n))
inv(),其中ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)
是恒等矩阵,calls是call_@lapack_func@(¶ms);
之一,它们是通用矩阵的线性求解器。
另一方面,定义为params.B
的@lapack_func@
sgesv, dgesv, cgesv, zgesv
scipy.linalg.inv
。它对应于lapack的calls函数,旨在使用由getri
计算的LU分解来计算矩阵的逆。 因此,如果您在Fortran中使用get_lapack_funcs(('getri'),(a1,))
,则在python中使用DGETRI()
可能会获得类似的性能和结果。
大多数Lapack函数可以使用DGETRI()
调用。这是在cython模块中使用DGETRF()
的示例:DGETRI()
这是一个示例代码,比较了scipy.linalg.cython_lapack.dgetrf()+ scipy.linalg.cython_lapack.dgetri(),numpy和scipy.linalg .inv()在1000x1000矩阵上:
scipy.linalg.inv()
输出为:
scipy.linalg.lapack
对于2000x2000矩阵:
scipy.linalg.lapack
scipy.linalg.cython_lapack.dgetri()
中提供了Fortran代码链DGETRF()+ DGETRI()进行一些更改后,让我们运行:
How to compile C extension for Python where C function uses LAPACK library?
一旦使用import numpy as np
from scipy import linalg
import time
import myinverse
n=1000
A=np.random.rand(n,n)
start= time.time()
Am,info,string=myinverse.invert(A.copy())
end= time.time()
print 'DGETRF+DGETRI, ', end-start, ' seconds'
if info==0:
print 'residual ',np.linalg.norm(A.dot(Am)-np.identity(n), np.inf)
else :
print "inversion failed, info=",info, string
start= time.time()
Am=np.linalg.inv(A.copy())
end= time.time()
print 'np.linalg.inv ', end-start, ' seconds'
print 'residual ', np.linalg.norm(A.dot(Am)-np.identity(n), np.inf)
start= time.time()
Am=linalg.inv(A.copy())
end= time.time()
print 'scipy.linalg.inv ', end-start, ' seconds'
print 'residual ',np.linalg.norm(A.dot(Am)-np.identity(n), np.inf)
进行编译,则对于1000x1000矩阵需要0.42s,而对于2000x2000矩阵则需要3s。
最后,如果Fortran代码和python代码未链接到相同的Blas / Lapack库,则可能会出现不同的性能。要对此进行调查,请键入DGETRF+DGETRI, 0.22541308403 seconds
residual 4.2155882951089296e-11
np.linalg.inv 0.29932808876 seconds
residual 4.371813154546711e-11
scipy.linalg.inv 0.298856973648 seconds
residual 9.110997546690758e-11
中所示的DGETRF+DGETRI, 1.64830899239 seconds
residual 8.541625644634121e-10
np.linalg.inv 2.02795410156 seconds
residual 7.448244269611659e-10
scipy.linalg.inv 1.61937093735 seconds
residual 1.6453560233026243e-09
之类的命令或LAPACK inversion routine strangely mixes up all variables。
[为了进一步利用分布式计算,PROGRAM solvelinear
implicit none
REAL(8), dimension(1000,1000) :: A,Ainv,M,LU
REAL(8),allocatable :: work(:)
REAL(8) :: wwork
INTEGER :: info,lwork
INTEGER,dimension(1000) :: ipiv
INTEGER :: i,j
real :: start, finish
! put code to test here
info=0
!work=0
ipiv=0
call RANDOM_NUMBER(A)
call cpu_time(start)
!-- LU factorisation
LU = A
CALL DGETRF(1000,1000,LU,1000,ipiv,info)
!-- Inversion of matrix A using the LU
Ainv=LU
lwork=-1
CALL DGETRI(1000,Ainv,1000,Ipiv,wwork,lwork,info)
lwork =INT( wwork+0.1)
allocate(work(lwork))
CALL DGETRI(1000,Ainv,1000,Ipiv,work,lwork,info)
deallocate(work)
call cpu_time(finish)
print '("Time = ",f6.3," seconds.")',finish-start
!-- computation of A^-1 * A to check the inverse
M = matmul(Ainv,A)
print*,"M = "
do i=1,3
do j=1,3
print*,M(i,j)
enddo
end do
END PROGRAM solvelinear
不建议对完全矩阵求逆,因为很少需要这样做。还可以写成gfortran main2.f90 -o main2 -llapack -lblas -lm -Wall
,其中np.__config__.show()
和Link ATLAS/MKL to an installed Numpy是密集矩阵。此外,此功能在python接口How to check BLAS/LAPACK linkage in NumPy and SciPy?中作为对象petsc的方法MatMatSolve(A,B,X)
提供。不再需要维护的B
被列为实现稠密矩阵的直接求解器。虽然Elemental库支持python接口,但其fork Hydrogen不再支持它。不过,“元素”页面列出了一些有关分布式密集线性代数的相关开源项目。 ScaLapack提供了使用LU分解对分布式密集矩阵进行求逆的例程X
。可能会留出一些空间进行更快的求逆。