使用svd解决欠定的scipy.sparse矩阵

时间:2018-06-13 17:24:43

标签: pandas sparse-matrix linear-algebra svd

问题

我有一组方程,变量用小写变量表示,常量用大写变量表示

A = a + b  
B = c + d  
C = a + b + c + d + e  

我在pandas DataFrame中提供了有关这些方程式结构的信息,其中包含两列:常量变量

E.g。

df = pd.DataFrame([['A','a'],['A','b'],['B','c'],['B','d'],['C','a'],['C','b'], 
['C','c'],['C','d'],['C','e']],columns=['Constants','Variables'])

然后我使用NetworkX

将其转换为稀疏CSC矩阵
table = nx.bipartite.biadjacency_matrix(nx.from_pandas_dataframe(df,'Constants','Variables')  
,df.Constants.unique(),df.Variables.unique(),format='csc')

转换为密集矩阵时, table 如下所示

矩阵([[1,1,0,0,0],[0,0,1,1,0],[1,1,1,1,1]],dtype = int64)

我想从这里得到的是找到哪些变量是可解的(在这个例子中,只有 e 是可解的),对于每个可解变量,它的值取决于哪些常数(在这种情况下,由于 e = C - B - A ,它依赖于 A B C

尝试解决方案

我首先尝试使用rref来解决可解决的变量。我使用了symbolics library sympy和函数sympy.Matrix.rref,它给了我我想要的东西,因为任何可解的变量都有自己的行,几乎全部为零和1,我可以逐行检查。 / p>

然而,这种解决方案并不稳定。它主要是非常慢,并没有利用我的数据集可能非常稀疏的事实。而且,rref在浮点数方面表现不佳。所以我决定转向另一种由Removing unsolvable equations from an underdetermined system推动的方法,建议使用svd

方便的是,scipy.sparse库中有一个svd函数,即scipy.sparse.linalg.svds。但是,由于我缺乏线性代数背景,我不明白在我的桌子上运行这个函数输出的结果,或者如何使用这些结果得到我想要的。

问题的进一步细节

  1. 我的问题中每个变量的系数是1.这就是数据在前面显示的两列pandas DataFrame中的表达方式
  2. 我实际示例中的绝大多数变量都无法解决。目标是找到几个可以解决的问题
  3. 如果它符合这个问题的限制,我非常愿意尝试另一种方法。
  4. 这是我第一次发帖提问,所以如果不完全遵循指导方针,我会道歉。请留下建设性的批评,但要温柔!

2 个答案:

答案 0 :(得分:1)

您正在解决的系统格式为

[ 1 1 0 0 0 ] [a]   [A]
[ 0 0 1 1 0 ] [b] = [B]
[ 1 1 1 1 1 ] [c]   [C]
              [d]
              [e]

,即五个变量a, b, c, d, e的三个方程式。正如您在问题中提到的答案所提到的那样,人们可以使用pseudoinverse解决这种欠定系统,Numpy直接根据pinv函数提供。

由于M具有线性独立的行,因此psudoinverse在这种情况下具有M.pinv(M) = I的属性,其中I表示单位矩阵(在这种情况下为3x3)。因此,正式地,我们可以将解决方案编写为:

v = pinv(M) . b

其中v是5分量解决方案向量,b表示右侧3分量向量[A, B, C]。但是,这个解决方案并不是唯一的,因为可以从所谓的内核或M矩阵null space中添加一个向量(即向量wM.w=0 })它仍然是一个解决方案:

M.(v + w) = M.v + M.w = b + 0 = b

因此,唯一存在唯一解的变量是来自M的零空间的所有可能矢量的相应分量为零的变量。换句话说,如果将零空间的基础组合成一个矩阵(每列一个基矢量),那么“可解变量”将对应于该矩阵的零行(列的任何线性组合的相应分量将然后也是零)。

让我们将其应用于您的特定示例:

import numpy as np
from numpy.linalg import pinv

M = [
    [1, 1, 0, 0, 0],
    [0, 0, 1, 1, 0],
    [1, 1, 1, 1, 1]
]

print(pinv(M))

[[ 5.00000000e-01 -2.01966890e-16  1.54302378e-16]
 [ 5.00000000e-01  1.48779676e-16 -2.10806254e-16]
 [-8.76351626e-17  5.00000000e-01  8.66819360e-17]
 [-2.60659800e-17  5.00000000e-01  3.43000417e-17]
 [-1.00000000e+00 -1.00000000e+00  1.00000000e+00]]

从这个伪逆,我们看到变量e(最后一行)确实可以表达为- A - B + C。但是,它也会“预测”a=A/2b=A/2。要消除这些非唯一解决方案(同样有效,例如a=Ab=0),让我们计算从SciPy Cookbook借用函数的零空间:

print(nullspace(M))

[[ 5.00000000e-01 -5.00000000e-01]
 [-5.00000000e-01  5.00000000e-01]
 [-5.00000000e-01 -5.00000000e-01]
 [ 5.00000000e-01  5.00000000e-01]
 [-1.77302319e-16  2.22044605e-16]]

这个函数已经返回组装成矩阵的空间的基础(每列一个向量),我们看到,在合理的精度范围内,唯一的零行确实只是对应于变量{{1的最后一行}}

编辑:

对于方程组

e

相应的矩阵A = a + b, B = b + c, C = a + c

M

在这里,我们看到矩阵实际上是方形的,并且是可逆的(行列式是[ 1 1 0 ] [ 0 1 1 ] [ 1 0 1 ] )。因此,伪逆与“正常”逆相符:

2

对应于解决方案[[ 0.5 -0.5 0.5] [ 0.5 0.5 -0.5] [-0.5 0.5 0.5]] 。由于a = (A - B + C)/2, ...是可逆的,因此其内核/ null空间为空,这就是cookbook函数仅返回M的原因。为了看到这一点,让我们使用内核的定义 - 它由所有非零向量[]组成,例如x。但是,由于存在M.x = 0,因此M^{-1}x,这是一个矛盾。形式上,这意味着找到的解决方案是唯一的(或者所有变量都是“可解决的”)。

答案 1 :(得分:0)

要基于ewcz的答案,可以使用numpy.linalg.svd计算零空间和伪逆。请参阅以下链接:

pseudo-inverse

nullspace