我试图找出在python中找到稀疏对称矩阵和真实矩阵行列式的最快方法。使用scipy sparse
模块,但真的很惊讶没有行列式功能。我知道我可以使用LU分解来计算行列式,但是没有看到一个简单的方法来执行它,因为scipy.sparse.linalg.splu
的返回是一个对象并且实例化一个密集的L和U矩阵是不值得的 - 我可以好sp.linalg.det(A.todense())
A
是我的scipy稀疏矩阵。
我也有点惊讶为什么其他人没有面对scipy中有效决定因素计算的问题。如何使用splu
计算行列式?
我调查了pySparse
和scikits.sparse.chlmod
。后者对我来说现在不实用 - 需要软件包安装,也不确定在我遇到麻烦之前代码的速度有多快。
有解决方案吗提前致谢。
答案 0 :(得分:5)
解决这个问题的“标准”方法是使用cholesky分解,但是如果你不能使用任何新的编译代码,那么你就不走运了。最好的稀疏cholesky实现是蒂姆戴维斯的CHOLMOD,它是根据LGPL许可的,因此不适用于scipy本身(scipy是BSD)。
答案 1 :(得分:4)
以下是我作为答案here的一部分提供的一些参考资料。 我认为它们可以解决您要解决的实际问题:
来自幕府将军的笔记:
计算似然表达式中对数行列式项的常用技术依赖于矩阵的Cholesky分解,即Σ= LLT,(L是下三角Cholesky因子),然后使用因子的对角项来计算日志(DET(Σ))=2Σni= 1log(LII)。然而,对于稀疏矩阵,由于协方差矩阵通常是,Cholesky因子经常遭受填充现象 - 它们本身并不那么稀疏。因此,对于大尺寸,由于存储所有这些不相关的因子非对角系数的大量存储器需求,该技术变得不可行。虽然已经开发了订购技术以预先对行和列进行置换以便减少填充,例如,近似最小度(AMD)重新排序,这些技术在很大程度上取决于稀疏性模式,因此无法保证提供更好的结果。
最近的研究表明,使用复杂分析,数值线性代数和贪婪图着色等多种技术,我们可以将对数行列式逼近任意精度[Aune et。 al。,2012]。主要技巧在于我们可以将log(det(Σ))写为trace(log(Σ)),其中log(Σ)是矩阵对数。
答案 2 :(得分:3)
您可以使用scipy.sparse.linalg.splu
来获得L
分解的较低(U
)和较高(M=LU
)三角矩阵的稀疏矩阵:
from scipy.sparse.linalg import splu
lu = splu(M)
行列式det(M)
可以表示为:
det(M) = det(LU) = det(L)det(U)
三角矩阵的行列式只是对角线项的乘积:
diagL = lu.L.diagonal()
diagU = lu.U.diagonal()
d = diagL.prod()*diagU.prod()
但是,对于large matrices underflow or overflow commonly occurs,可以通过使用对数来避免。
diagL = diagL.astype(np.complex128)
diagU = diagU.astype(np.complex128)
logdet = np.log(diagL).sum() + np.log(diagU).sum()
请注意,我调用复杂的算术来解释可能出现在对角线中的负数。现在,您可以从logdet
恢复行列式:
det = np.exp(logdet) # usually underflows/overflows for large matrices
可直接从diagL
和diagU
计算行列式的符号(例如,在实施Crisfield的弧长方法时很重要):
sign = swap_sign*np.sign(diagL).prod()*np.sign(diagU).prod()
其中swap_sign
是考虑LU分解中的置换数量的术语。感谢@Luiz Felippe Rodrigues,可以计算出它:
swap_sign = minimumSwaps(lu.perm_r)
def minimumSwaps(arr):
"""
Minimum number of swaps needed to order a
permutation array
"""
# from https://www.thepoorcoder.com/hackerrank-minimum-swaps-2-solution/
a = dict(enumerate(arr))
b = {v:k for k,v in a.items()}
count = 0
for i in a:
x = a[i]
if x!=i:
y = b[i]
a[y] = x
b[x] = y
count+=1
return count
答案 3 :(得分:1)
同时使用 SuperLU 和 CHOLMOD 的 N=1e6 附近的稀疏三对角线 (-1 2 -1) 的行列式开始出现问题...
行列式应该是 N+1。
在计算 U 对角线的乘积时可能是误差的传播:
from scipy.sparse import diags
from scipy.sparse.linalg import splu
from sksparse.cholmod import cholesky
from math import exp
n=int(5e6)
K = diags([-1.],-1,shape=(n,n)) + diags([2.],shape=(n,n)) + diags([-1.],1,shape=(n,n))
lu = splu(K.tocsc())
diagL = lu.L.diagonal()
diagU = lu.U.diagonal()
det=diagL.prod()*diagU.prod()
print(det)
factor = cholesky(K.tocsc())
ld = factor.logdet()
print(exp(ld))
输出:
4999993.625461911
4999993.625461119
即使 U 的准确度为 10-13 位,这也是意料之中的:
n=int(5e6)
print(n*diags([1-0.00000000000025],0,shape=(n,n)).diagonal().prod())
4999993.749444371