繁体   English   中英

使用自定义约束实现Python解算器

[英]Implementing a Python Solver with Customised Constraints

我有两个相互关联的变量,我想找到一个最佳解决方案,在这种情况下,它们是最小的总和。 现在,让我们称它们为XY ,并与预定义的常量一起,它们加起来一组“变量” s1s2 (后来提供约束):

105896649.59 + X = s1
    -6738.82 + Y = s2

在搜索SciPy文档时,我遇到了线性编程解决方案,其中我有最小化函数(在本例中为X + Y )以及我的变量绑定到的一组不等式和等式约束。 就我而言,它们如下:

  • X >= 0, Y >= 0
  • s1 >= 1, s2 >= 1
  • s2 / (s1 + s2) = 0.0001%

对于这种特定情况,代码很容易实现:

from scipy.optimize import linprog

lstConst = [105896649.59, -6738.82]

# function to minimise: X + Y
c= [1, 1]

# left-hand side of the equation for s2 / (s1 + s2) = 0.0001%
# i.e., -0.000001 * X + 0.999999 * Y
Aeq = [[-0.000001, 0.999999]]

# right-hand side of the equation
beq = [0.000001 * (lstConst[0] + lstConst[1]) - lstConst[1]]

# ensures minimum can't be a negative number
minX = max(1, max(1 -lstConst[0], 0))
minY = max(1, max(1 -lstConst[1], 0))

X_bounds = (minX, None)

Y_bounds = (minY, None)

res = linprog(c, A_eq=Aeq, b_eq=beq, bounds=[X_bounds, Y_bounds])

所以我们有XY的值来最小化x参数的函数:

In [1]: res.x
Out[1]: array([1.00000000e+00, 6.84471676e+03])

我想建立在这种方法上:

  1. 实际上,还有另一组限制: s1s2也必须是整数(注意XY没有浮点数问题)。
  2. 我没有为s1s2之间的比率定义单个值,而是提供了不同可能比率的列表。

本质上,我想在s1s2之间给出几个不同的比率,找到X + Y函数的最小值。 这可以通过迭代列表来定义每次迭代的Aeqbeq或定义其他限制(如果可能)来实现。

但是,我对整数限制毫无头绪,以及如何使线性规划算法将其考虑在内。

如果有人有另一个建议使用除SciPy和linprog之外的库/优化器,那也是受欢迎的。

首先,重申问题:

minimize x + y, subject to:

    k1 + x = s1
    k2 + y = s2
    x >= 0
    y >= 0
    s1 >= 1
    s2 >= 1
    s2 / (s1 + s2) = k3

Where:

    k1 = 105896649.59
    k2 = -6738.82
    k3 = 0.000001

注意,你不需要s1s2变量来编译linprog的问题。 没有s1s2辅助变量,问题是:

minimize x + y, subject to:

  x >= 0
  y >= 0
  x + k1 >= 1,
  y + k2 >= 1,
  (1-k3)y - k3x = (k1 + k2)k3 - k2

linprog更容易阅读和编码:

import numpy as np
from scipy.optimize import linprog
k1, k2, k3 = 105896649.59, -6738.82, 0.000001
A_ub = -np.eye(2)
b_ub = [k1-1, k2-1]
A_eq = [[-k3, (1-k3)]]
b_eq = (k1 + k2)*k3 -k2
res = linprog([1,1], A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=[[0,None], [0, None]])
print(res)

这给出了[0., 6844.71675549] ,其中你有x = 1,因为你实际上已经将x和y的下限设置为1(我认为这是一个错字...)但它并不重要被问到的问题的背景:


问题:

...我对整数限制毫无头绪,以及如何使线性规划算法将其考虑在内。

如果有人有另一个建议使用除SciPy和linprog之外的库/优化器,那也是受欢迎的。

您要求的是混合整数线性编程(MILP) MILP和线性编程(LP)通常使用不同的算法求解,并且MILP问题通常难以精确解决。 SciPy Optimize不支持MILP。 有许多开源工具可以使用OrToolsPySCIPOpt ,它们是SCIP上的Python包装器。


PySCIPOpt中的示例:

PySCIPOpt非常好,因为它有一个约束编程类型API。 在PySCIPOpt中,您的问题很容易以可读的形式陈述。 重新引入辅助变量,我们几乎可以逐字输入约束:

from pyscipopt import Model

k1, k2, k3 = 105896649.59, -6738.82, 0.000001
model = Model()
x = model.addVar(vtype="CONTINUOUS", name="x", lb=0)
y = model.addVar(vtype="CONTINUOUS", name="y", lb=0)
s1 = model.addVar(vtype="CONTINUOUS", name="s1", lb=None, ub=None)
s2 = model.addVar(vtype="CONTINUOUS" name="s2", lb=None, ub=None)
o = model.addVar(vtype="CONTINUOUS", name="Objective Value", lb=0, ub=None)
model.addCons(k1 + x == s1)
model.addCons(k2 + y == s2)
model.addCons(s1 >= 1)
model.addCons(s2 >= 1)
model.addCons(s2/(s1+s2) == k3)
model.addCons(x + y == o)
model.setObjective(o, "minimize")
model.optimize()
print('x + y = o -> (%.4f + %.4f = %.4f)' % (model.getVal(x), model.getVal(y), model.getVal(o)))

给出与linprog相同的答案,因为它只是一个线性程序。 但是,由于SCIP支持MILP,我们可以引入整数变量。 要处理你的情况#1,只需将s1和s2更改为整数:

...
s1 = model.addVar(vtype="INTEGER", name="s1", lb=None, ub=None)
s2 = model.addVar(vtype="INTEGER", name="s2", lb=None, ub=None)

得到:

...
SCIP Status        : problem is solved [optimal solution found]
Solving Time (sec) : 0.00
Solving Nodes      : 1
Primal Bound       : +1.10089229999989e+05 (1 solutions)
Dual Bound         : +1.10089229999989e+05
Gap                : 0.00 %
x + y = o -> (103244.4100 + 6844.8200 = 110089.2300)

这是一个完全不同的解决方案......但这就是为什么MILP不是LP。

从上面的例子中,通过阅读文档 ,您应该能够弄清楚如何编写#2案例 - 基本上像1/k3这样的东西成为模型中的另一个整数变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM