为什么scipy的稀疏求解器给出了错误的答案?

时间:2016-07-07 20:47:38

标签: python numpy scipy sparse-matrix

注意:我在最初发布的代码中犯了一个愚蠢的错误,正如Warren Weckesser所说。纠正后,一些求解器给出了正确的答案,但其他求解者给出了NaN或不正确的答案。我也忘了在输出中包含运行时警告;他们现在在那里。我已相应地修改了这个问题。我或许可以使用有效的求解器,但如果我理解为什么其他人失败了,我会更开心。

我正在尝试使用scipy.sparse.linalg中的一个或多个求解器来求解稀疏的线性方程组。在测试用例中,系统小到可以直接求解,一些稀疏求解器给出了错误的答案,如下例所示:

import numpy as np
import scipy.sparse as ss


A = np.matrix([[ 0.,  0.,  0.,  0.,  0.,  1., -1., -0., -0., -0., -0.],
       [ 0.,  0.,  0.,  0.,  0.,  2., -0., -1., -0., -0., -0.],
       [ 0.,  0.,  0.,  0.,  0.,  2., -0., -0., -1., -0., -0.],
       [ 0.,  0.,  0.,  0.,  0.,  2., -0., -0., -0., -1., -0.],
       [ 0.,  0.,  0.,  0.,  0.,  1., -0., -0., -0., -0., -1.],
       [ 1.,  2.,  2.,  2.,  1.,  0., -0., -0., -0., -0., -0.],
       [-1.,  0.,  0.,  0.,  0.,  0., -1., -0., -0., -0., -0.],
       [ 0., -1.,  0.,  0.,  0.,  0., -0., -1., -0., -0., -0.],
       [ 0.,  0., -1.,  0.,  0.,  0., -0., -0., -1., -0., -0.],
       [ 0.,  0.,  0., -1.,  0.,  0., -0., -0., -0., -1., -0.],
       [ 0.,  0.,  0.,  0., -1.,  0., -0., -0., -0., -0., -1.]])
b = np.matrix([0.,0.,0.,0.,0.,1.,0.,0.,0.,0.,0.]).T
As = ss.coo_matrix(A)

# The linear system Ax = b has a solution:
x1 = np.linalg.solve(A,b)
print("Solution to Ax = b:",x1)
print("Ax - b = ",A*x1-b)

print("Info and maximum error in solutions found by various other methods: ")
x2,info = ss.linalg.bicg(As,b)
print("bicg:",info,np.max(np.abs(x2-x1.ravel())))
x2,info = ss.linalg.bicgstab(As,b)
print("bicgstab:",info,np.max(np.abs(x2-x1.ravel())))
x2,info = ss.linalg.cgs(As,b)
print("cgs:",info,np.max(np.abs(x2-x1.ravel())))
x2,info = ss.linalg.gmres(As,b)
print("gmres:",info,np.max(np.abs(x2-x1.ravel())))
x2,info = ss.linalg.lgmres(As,b)
print("lgmres:",info,np.max(np.abs(x2-x1.ravel())))
x2,info = ss.linalg.minres(As,b)
print("minres:",info,np.max(np.abs(x2-x1.ravel())))
x2,info = ss.linalg.qmr(As,b)
print("qmr:",info,np.max(np.abs(x2-x1.ravel())))

当我运行它时,我得到以下输出:

Solution to Ax = b: [[ 0.07142857]
 [ 0.14285714]
 [ 0.14285714]
 [ 0.14285714]
 [ 0.07142857]
 [-0.07142857]
 [-0.07142857]
 [-0.14285714]
 [-0.14285714]
 [-0.14285714]
 [-0.07142857]]
Ax - b =  [[  0.00000000e+00]
 [  0.00000000e+00]
 [  2.77555756e-17]
 [  0.00000000e+00]
 [  1.38777878e-17]
 [  0.00000000e+00]
 [  2.77555756e-17]
 [ -2.77555756e-17]
 [ -5.55111512e-17]
 [  2.77555756e-17]
 [  0.00000000e+00]]
Info and maximum error in solutions found by various other methods: 
bicg: 1 nan
bicgstab: 1 nan
cgs: 1 nan
gmres: 0 5.55111512313e-17
lgmres: 0 1.38777878078e-16
minres: 0 0.142857142857
qmr: -11 0.142857142857
/Users/ebunn/anaconda/lib/python3.5/site-packages/scipy/sparse/linalg/isolve/iterative.py:197: RuntimeWarning: invalid value encountered in multiply
  work[slice2] *= sclr2
/Users/ebunn/anaconda/lib/python3.5/site-packages/scipy/sparse/linalg/isolve/iterative.py:318: RuntimeWarning: invalid value encountered in multiply
  work[slice2] *= sclr2
/Users/ebunn/anaconda/lib/python3.5/site-packages/scipy/sparse/linalg/isolve/minres.py:244: RuntimeWarning: divide by zero encountered in double_scalars
  Acond = gmax/gmin

x2的每个计算都应该是与x1相同的线性系统的解,因此最后七行中的所有误差都应为零(或至少很小)。

gmres和lgmres工作,但其他人没有。在大多数情况下,信息正确指示失败,但minres表示成功(info = 0),同时返回全零(不正确)的解决方案。

以下是一些可能相关的其他信息:

  • 矩阵A是对称的,但不是正定的。
  • 矩阵A条件很好。
  • 如果我在所有稀疏解算器中用原始矩阵A替换稀疏表示As,那么结果就不会改变 - 也就是说,如果我说 ss.linalg.bicg(A,b)而不是 ss.linalg.bicg(As,b)等。
  • 包含bicgstab实际上是不公平的,因为文档说这仅适用于正定矩阵。我把它包括在内,希望它可以工作,因为这种方法原则上适用于不定矩阵。
  • 我正在运行Python 3.5.1,scipy版本0.17.0。

当然,对于这个系统来说,它并不重要,因为直接解决它是微不足道的。对于较大的问题,这是一个热身问题,稀疏性是必不可少的。

可能gmres和/或lgmres会满足我的需求,但我仍然想了解其他人出了什么问题,部分原因是为了我自己的理解而且我也许有更广泛的方法可供选择。特别是,这两种方法都没有利用A的对称性,并且有一种方法可能会很好。

1 个答案:

答案 0 :(得分:1)

当我运行你的代码(py3)

时,我收到了一堆警告
Maximum error in solutions found by various other methods: 
bicg: nan
/usr/lib/python3/dist-packages/scipy/sparse/linalg/isolve/iterative.py:197: RuntimeWarning: invalid value encountered in multiply
  work[slice2] *= sclr2
bicgstab: nan
/usr/lib/python3/dist-packages/scipy/sparse/linalg/isolve/iterative.py:318: RuntimeWarning: invalid value encountered in multiply
  work[slice2] *= sclr2
cgs: nan
gmres: 0.285714285714
lgmres: 0.285714285714
/usr/lib/python3/dist-packages/scipy/sparse/linalg/isolve/minres.py:244: RuntimeWarning: divide by zero encountered in double_scalars
  Acond = gmax/gmin
minres: 0.142857142857
qmr: 0.142857142857

正如沃伦警告的那样,对于gmres案例,阵列形状可以咬你。非nan个案例中的错误均为0.1428或2倍。

添加:

print(x2)
print(x2-x1.ravel())

产生

[ 0.07142857  0.14285714  0.14285714  0.14285714  0.07142857 -0.07142857
 -0.07142857 -0.14285714 -0.14285714 -0.14285714 -0.07142857] 0
[[  4.16333634e-17   0.00000000e+00  -5.55111512e-17   5.55111512e-17
    0.00000000e+00  -1.38777878e-17  -1.38777878e-17  -2.77555756e-17
    0.00000000e+00  -2.77555756e-17   0.00000000e+00]]

正确比较2种解决方案时没有错误。