用Python求解线性整数方程组

时间:2017-03-07 00:05:52

标签: python numpy scipy linear-algebra

我正在寻找一种在Python中求解线性方程组的方法。 特别是,我正在寻找大于全零的最小整数向量并解决给定的方程。 例如,我有以下等式:

enter image description here

想要解决 enter image description here

在这种情况下,解决该等式的最小整数向量是 enter image description here

但是,如何自动确定此解决方案? 如果我使用scipy.optimize.nnls,例如

A = np.array([[1,-1,0],[0,2,-1],[2,0,-1]])
b = np.array([0,0,0])
nnls(A,b)

结果是(array([ 0., 0., 0.]), 0.0)。 这也是正确的,但不是理想的解决方案...

编辑:我为在某些方面不精确而道歉。 如果有人对细节感兴趣,问题来自论文 “用于数字信号处理的同步数据流程序的静态调度”,Edward A. Lee和David G. Messerschmitt, IEEE Transactions on Computers,Vol。 C-36,第1号,第24-35页,1987年1月。

定理2说

  

对于具有s个节点和拓扑矩阵A且秩(A)= s-2的连通SDF图,我们可以找到正整数向量b!= 0,使得Ab = 0,其中0是零向量。 / p>

直接在定理证明2之后他们说

  

可能需要求解零空间中最小的正整数向量。为此,减少u'中的每个有理条目,使其分子和分母相对为素数。欧几里德的算法将适用于此。

5 个答案:

答案 0 :(得分:3)

要找到您想要的确切解决方案,numpy和scipy可能不是最好的工具。他们的算法通常以浮点工作,并且不能保证给出完全答案。

您可以使用sympy来获得此问题的确切答案。 Matrix中的sympy类提供方法nullspace(),该方法返回nullspace的基础向量列表。这是一个例子:

In [20]: from sympy import Matrix, lcm

In [21]: A = Matrix([[1, -1, 0], [0, 2, -1], [2, 0, -1]])

在nullspace中获取向量。 nullspace()返回一个列表;此代码假定A的等级为2,因此列表的长度为1:

In [22]: v = A.nullspace()[0]

In [23]: v
Out[23]: 
Matrix([
[1/2],
[1/2],
[  1]])

v中找到值的分母的最小公倍数,以便我们可以将结果缩放到最小的整数:

In [24]: m = lcm([val.q for val in v])

In [25]: m
Out[25]: 2

x包含整数答案:

In [26]: x = m*v

In [27]: x
Out[27]: 
Matrix([
[1],
[1],
[2]])

要将该结果转换为numpy整数数组,您可以执行以下操作:

In [52]: np.array([int(val) for val in x])
Out[52]: array([1, 1, 2])

答案 1 :(得分:3)

实际上,它只是基本的线性代数。

>>> A = np.array([[1,-1,0], [0,2,-1],[2,0,-1]])    

让我们计算这个矩阵的特征值和特征向量。

>>> e = np.linalg.eig(A)
>>> e
(array([ -4.14213562e-01,  -1.05471187e-15,   2.41421356e+00]), array([[-0.26120387, -0.40824829,  0.54691816],
       [-0.36939806, -0.40824829, -0.77345908],
       [-0.89180581, -0.81649658,  0.32037724]]))    
>>>> np.round(e[0], 10)
array([-0.41421356, -0.        ,  2.41421356])

舍入后,我们看到0是矩阵A的特征值。因此,0-特征值的特征向量是方程组的一个很好的候选者。

>>> s = e[1][:,1]
>>> s
array([-0.40824829, -0.40824829, -0.81649658])    

将特征向量与相关矩阵相乘得到特征向量本身,由相关特征值缩放。因此,在上面的例子中我们看到:As = 0s = 0

>>> np.round(A.dot(s), 10)
array([ 0.,  0.,  0.])

由于我们对整数解决方案感兴趣,因此我们必须缩放解决方案向量。

>>> x = s / s[1]
>>> x
array([ 1.,  1.,  2.])

希望这个答案能解决你的问题。

答案 2 :(得分:0)

这或许是一个疯狂的想法,但听起来你正在寻找建立一个约束求解器。

minizinc是一个通用约束求解器。也许有可能以minizinc可以解决它的方式表达你的约束?

然后看来有一个Python库可以与它进行交互:https://pypi.python.org/pypi/pymzn/

答案 3 :(得分:0)

这个问题非常非正式,如评论中所示。如果不知道你对最小的定义(例子:l1-norm,l2-norm),很难回答你的具体问题。

一般问题与解决丢番图方程组有关,但这些问题很难解决(在一般情况下)并且软件不多。

一种自然的方法是使用整数编程,这是NP难的,但商业和一些开源解算器非常强大。

numpy / scipy中没有内置方法可以在没有大量修改的情况下解决您的问题(例如:基于numpy / scipy实现一些自己的算法)。可悲的是,在numpy / scipy中也没有IP解算器。

我们假设:

  • 最小是变量的总和(大多数时候这不是正确的方法; l2-norm更受欢迎,但是它更难以制定,它需要更强大的求解器)< / LI>
  • 你想禁止零向量
  • x is nonnegative

这是使用纸浆和numpy的一些简单的基于IP的实现。我不喜欢纸浆,但很容易在所有类型的流行系统上安装(pip install pulp)。

代码

from pulp import *
import numpy as np

EPS = 1e-3

""" Input """
A = np.array([[1,-1,0],[0,2,-1],[2,0,-1]])
b = np.array([0,0,0])

""" MIP """
# Variables
x = np.empty(b.shape[0], dtype=object)
for i in range(b.shape[0]):
    x[i] = LpVariable("x" + str(i), lowBound=0, upBound=None, cat='Integer')

# Problem
prob = LpProblem("prob", LpMinimize)

# Objective
prob += np.sum(x)

# Constraints
for row in range(A.shape[0]):
    prob += np.dot(A[row], x) == b[row]

prob += np.sum(x) >= EPS  # forbid zero-vector

# Solve
status = prob.solve()
print(LpStatus[status])
print([value(x_) for x_ in x])

输出

Optimal
[1.0, 1.0, 2.0]

答案 4 :(得分:-1)

pypi处有这种等式的求解器。它显然可以计算矩阵的Hermite normal form,而矩阵又可以用来解决你的问题。

该软件包的版本为0.1。

Sage似乎也是support Hermite正常形态。

同构系统的特殊情况,即b = 0稍微容易一些,这里是一个求解器,用于矩阵的最简单可能情况,其等级为n-1

import sympy
import numpy as np

def create_rd(n, defect=1, range=10):
    while True:
        res = sympy.Matrix((np.random.randint(-range+1,range,(n, n-defect))
                           @np.random.randint(0,2,(n-defect, n)))
                           .astype(object))
        if res.rank() == n-defect:
            break
    return res

def solve(M):
    ns = M.nullspace()
    ns = [n / sympy.gcd(list(n)) for n in ns]
    nsnp = np.array([[int(k) for k in n] for n in ns])
    if len(ns) == 1:
        return ns[0], nsnp[0]
    else:
        raise NotImplementedError

示例输出:

>>> M = create_rd(4)  # creates a rank-deficient matirx
>>> ns, nn = solve(M) # finds the 1d nullspace and a minimal integer basis vector
>>> M
Matrix([
[-7, -7, -7, -12],
[ 0,  6,  0,   6],
[ 4,  1,  4,  -3],
[-4, -7, -4,  -9]])
>>> ns
Matrix([
[-1],
[ 0],
[ 1],
[ 0]])
>>> M*ns
Matrix([
[0],
[0],
[0],
[0]])
>>> M = create_rd(40) # we can go to higher dimensions
>>> ns, nn = solve(M) # but solutions quickly become unwieldy
>>> ns
Matrix([
[  8620150337],
[-48574455644],
[ -6216916999],
[-14703127270],
 < - snip - >