使用自定义约束实现Python解算器
我有两个相互关联的变量,我想找到一个最优解,在这种情况下是它们之和的最小值。现在,让我们将它们称为使用自定义约束实现Python解算器,python,scipy,linear-programming,Python,Scipy,Linear Programming,我有两个相互关联的变量,我想找到一个最优解,在这种情况下是它们之和的最小值。现在,让我们将它们称为X和Y,它们与预定义的常量一起,加起来就是一组“变量”s1和s2(这些变量稍后会提供约束): 在搜索SciPy文档时,我遇到了一个解决方案,其中我有最小化函数(在本例中,X+Y)和一组不等式和等式约束,我的变量绑定到这些约束。就我而言,它们如下: X>=0,Y>=0 s1>=1,s2>=1 s2/(s1+s2)=0.0001% 对于这种特定情况,代码很容易实现: 来自scipy.optimize
X
和Y
,它们与预定义的常量一起,加起来就是一组“变量”s1
和s2
(这些变量稍后会提供约束):
在搜索SciPy文档时,我遇到了一个解决方案,其中我有最小化函数(在本例中,X+Y
)和一组不等式和等式约束,我的变量绑定到这些约束。就我而言,它们如下:
X>=0,Y>=0
s1>=1,s2>=1
s2/(s1+s2)=0.0001%
来自scipy.optimize import linprog
lstConst=[105896649.59,-6738.82]
#要最小化的功能:X+Y
c=[1,1]
#s2/(s1+s2)=0.0001%方程的左侧
#即-0.000001*X+0.999999*Y
Aeq=[-0.000001,0.999999]]
#等式的右侧
beq=[0.000001*(lstConst[0]+lstConst[1])-lstConst[1]]
#确保最小值不能为负数
minX=max(1,max(1-lstcont[0],0))
minY=max(1,max(1-lstcont[1],0))
X_界限=(最小值,无)
Y_界限=(最小,无)
res=linprog(c,A_eq=Aeq,b_eq=beq,bounds=[X_bounds,Y_bounds])
因此我们有X
和Y
的值,以最小化X
参数上的函数:
In [1]: res.x
Out[1]: array([1.00000000e+00, 6.84471676e+03])
我想在此基础上:
s1
和s2
也必须是整数(注意X
和Y
作为浮点数没有问题)s1
和s2
之间的比率定义单个值,我将提供一个不同可能比率的列表X+Y
函数的最小值,给定s1
和s2
之间的几个不同比率。这可以通过迭代列表来实现,在每次迭代中定义Aeq
和beq
,或者定义其他限制(如果可能)
然而,对于整数限制以及如何使线性规划算法考虑到它,我一无所知
如果有人建议使用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
注意,您不需要使用s1
和s2
变量在linprog
中对问题进行编码。如果没有辅助变量s1
和s2
,问题在于:
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和线性规划(LP)通常使用不同的算法求解,而MILP问题通常更难精确求解。SciPy Optimize不支持MILP。有许多开源工具可以做到这一点,比如和,它是一个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
成为模型中的另一个整数变量。FYI您的代码中有另一个关于max(1,max(1-lstcont[0],0)]的输入错误。
这相当于max(1-lstcont[0],1)
但是评论说这只是为了确保数字不是负数。我没有意识到没有必要更详细,我认为如果我没有这样设置,lstcont
的符号会影响边界,谢谢你的实现你确实需要它来编码不平等,只是它应该是minX=max(1-lstcont[0],0)
而不是max(1,max(1-lstcont[0],0))
AFAICT。我使用了linprog不等式约束来解决这些问题。在安装pyscipot
时遇到了一些问题,但最终成功地使其运行。MILP的复杂性是否使其运行相对较慢?我跑了15分钟,原始边界和双重边界之间仍然有1.5%的差距。确切的代码是??一般来说,MILP是NP难的(参见维基百科文章链接ni答案),是的,比LP难。但在这种情况下,只有两个变量,应该很容易解决。在我的系统上花了0.00秒。我更新了答案以显示更多的输出。这也可能是因为我安装的解算器的可用性,而您没有。我不确定。是的,我实际上复制了你的代码来检查它的性能。可能是因为我使用的是特定的发行版吗?在我的例子中,因为我有蟒蛇,我必须通过pi安装pyscipot
...
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)