【问题标题】:Is it possible to compute an inverse of sparse matrix in Python as fast as in Matlab?是否可以像在 Matlab 中一样快地在 Python 中计算稀疏矩阵的逆?
【发布时间】:2014-03-08 18:03:54
【问题描述】:

Matlab 使用 sparse 命令计算对角矩阵的逆矩阵需要 0.02 秒。

P = diag(1:10000);
P = sparse(P);
tic;
A = inv(P);
toc

但是,对于 Python 代码,这需要很长时间 - 几分钟。

import numpy as np
import time

startTime = time.time()
P = np.diag(range(1,10000))
A = np.linalg.inv(P)
runningTime = (time.time()-startTime)/60
print "The script was running for %f minutes" % runningTime

我尝试使用 Scipy.sparse 模块,但没有帮助。运行时间减少了,但只有 40 秒。

import numpy as np
import time
import scipy.sparse as sps
import scipy.sparse.linalg as spsl

startTime = time.time()
P = np.diag(range(1,10000))
P_sps = sps.coo_matrix(P)
A = spsl.inv(P_sps)
runningTime = (time.time()-startTime)/60
print "The script was running for %f minutes" % runningTime

是否可以像在 Matlab 中一样快地运行代码?

【问题讨论】:

  • 将矩阵存储为 P_sps = sps.dia_matrix(P) 会有所改进,但 Matlab 代码仍然快 3 个数量级
  • 真正的问题是为什么你觉得需要反转矩阵。
  • 正如赫弗南所说,你真的需要反转矩阵吗?请参阅 thisthis 了解一些帖子
  • 在 matlab 代码中,您开始计算创建对角线并将矩阵转换为稀疏矩阵后的时间。在你之前做的python代码中。你没有公平地比较。
  • 我很确定,创建大小为 10000x10000 的二维数组、存储对角线并将其转换为 csr 所花费的时间比反转对角线所花费的时间要大得多。

标签: python performance matlab numpy sparse-matrix


【解决方案1】:

这就是答案。当您在 matlab 中对稀疏矩阵运行 inv 时,matlab 会检查矩阵的不同属性以优化计算。对于稀疏的对角矩阵,可以运行下面的代码看看matlab在做什么

n = 10000;
a = diag(1:n);
a = sparse(a);
I = speye(n,n);
spparms('spumoni',1);
ainv = inv(a);
spparms('spumoni',0);

Matlab 将打印以下内容:

sp\: bandwidth = 0+1+0.
sp\: is A diagonal? yes.
sp\: do a diagonal solve.

所以 matlab 只反转对角线。

Scipy 如何反转矩阵? 这里我们有code

...
from scipy.sparse.linalg import spsolve
...

def inv(A):
    """
    Some comments...
    """
    I = speye(A.shape[0], A.shape[1], dtype=A.dtype, format=A.format)
    Ainv = spsolve(A, I)
    return Ainv

spsolve

    # Cover the case where b is also a matrix
    Afactsolve = factorized(A)
    tempj = empty(M, dtype=int)
    x = A.__class__(b.shape)
    for j in range(b.shape[1]):
        xj = Afactsolve(squeeze(b[:, j].toarray()))
        w = where(xj != 0.0)[0]
        tempj.fill(j)
        x = x + A.__class__((xj[w], (w, tempj[:len(w)])),
                            shape=b.shape, dtype=A.dtype)

即,scipy 分解 A 然后求解一组线性系统,其中右侧是坐标向量(形成单位矩阵)。对矩阵中的所有解进行排序,我们得到初始矩阵的逆矩阵。

如果matlab利用了矩阵的对角结构,而scipy没有(当然scipy也是利用矩阵的结构,但是效率较低,至少对于例子来说),matlab应该是更快。

编辑 可以肯定的是,正如@P.Escondido 所建议的那样,我们将尝试对矩阵 A 进行小修改,以在矩阵不是对角线时跟踪 matlab 过程:

n = 10000; a = diag(1:n); a = sparse(a); ainv = sparse(n,n);
spparms('spumoni',1);
a(100,10) = 500; a(10,1000) = 200; 
ainv = inv(a);
spparms('spumoni',0);

它打印出以下内容:

sp\: bandwidth = 90+1+990.
sp\: is A diagonal? no.
sp\: is band density (0.00) > bandden (0.50) to try banded solver? no.
sp\: is A triangular? no.
sp\: is A morally triangular? yes.
sp\: permute and solve.
sp\: sprealloc in sptsolve: 10000 10000 10000 15001

【讨论】:

  • 谢谢!请加a(1000,10)=500; a(10,1000)=200;解和相应的输出。它让我们更好地了解 Matlab 的实际工作原理。如果你这样做了,我会将问题标记为已解决。
【解决方案2】:

splu() 怎么样,它更快但需要密集数组并返回密集数组:

创建一个随机矩阵:

import numpy as np
import time
import scipy.sparse as sps
import scipy.sparse.linalg as spsl
from numpy.random import randint
N = 1000
i = np.arange(N)
j = np.arange(N)
v = np.ones(N)

i2 = randint(0, N, N)
j2 = randint(0, N, N)
v2 = np.random.rand(N)

i = np.concatenate((i, i2))
j = np.concatenate((j, j2))
v = np.concatenate((v, v2))

A = sps.coo_matrix((v, (i, j)))
A = A.tocsc()

%time B = spsl.inv(A)

通过splu()计算逆矩阵:

%%time
lu = spsl.splu(A)
eye = np.eye(N)
B2 = lu.solve(eye)

检查结果:

np.allclose(B.todense(), B2.T)

这是 %time 输出:

inv: 2.39 s
splv: 193 ms

【讨论】:

  • 代码在 Python 2.7 中无法编译;问题出在“%time B = spsl.inv(A)”中。我应该使用什么编译器?
  • %time 是 IPython 命令,如果你不使用 IPython,只需删除 %time 和 %%time。
【解决方案3】:

您正在从您的软件中提取关键信息:矩阵是对角线这一事实使其非常容易反转:您只需反转其对角线的每个元素:

P = np.diag(range(1,10000))
A = np.diag(1.0/np.arange(1,10000))

当然,这仅对对角矩阵有效...

【讨论】:

  • 没错,这仅对对角矩阵有效,因此答案并不真正有效。另请注意,我不必向 Matlab 指定我对结构的了解。
  • @P.Escondido 我花了一点时间试图理解和逆向分析 Matlab 在反转矩阵时所做的事情。我必须说 mathworks 的人员在这个问题上做得非常好。在幕后,有很多数值库在利用矩阵的特定特性。如果您可以为您的程序提供这些额外信息 - 让自己过上轻松的生活并使用它,如果您不需要,请不要尝试解决过于复杂的问题...
【解决方案4】:

如果你尝试这样做,结果会更好:

import numpy as np
import time
import scipy.sparse as sps
import scipy.sparse.linalg as spsl

P = np.diag(range(1,10000))
P_sps = sps.coo_matrix(P)
startTime = time.time()
A = spsl.inv(P_sps)
runningTime = (time.time()-startTime)/60
print "The script was running for %f minutes" % runningTime

现在您可以与您的 matlab 脚本进行比较。

【讨论】:

  • 谢谢,我同意定时器应该在计算之前就启动了,但是差别可以忽略不计
  • 我认为差别不大。
  • 0.44 对 0.64 分钟。 Matlab 的速度提高了 3 个订单。从这个意义上说,它可以忽略不计。
猜你喜欢
  • 2015-09-23
  • 2014-11-13
  • 1970-01-01
  • 2011-08-18
  • 1970-01-01
  • 2012-10-22
  • 2020-09-04
  • 2018-01-12
  • 1970-01-01
相关资源
最近更新 更多