数学优化工具对比及封装scipy.linprog
用python的话
- gurobi,不开源,有讲解,性能最好,建模最方便。
- ortools,建模变量设置方式灵活,但貌似只支持simplex。另外专门有一些特殊问题的包,如最大流问题,最小花费流问题。
- lpsolve貌似只支持simplex
- scipy.linprog 支持simplex(更准确)和interior point(本质上是近似),但建模固定矩阵式,不能灵活设置变量。
- cvxpy,建模变量更灵活些。
- CVXPY relies on the open source solvers ECOS, OSQP, and SCS
- cvxopt 也是固定矩阵式建模。
- pulp
scipy.linprog
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linprog.html
min_{x_0,x_1} -x_0 + 4 x_1
s.b. -3x_0 + x_1 \leq 6 \\
-x_0 - 2x_1 \geq -4 \\
x_1 \geq -3
以下两种写法等价,说明bounds不用也行。
from scipy.optimize import linprog
c = [-1, 4]
A = [[-3, 1], [1, 2], [0,-1]]
b = [6, 4, 3]
x0_bounds = (None, None)
x1_bounds = (None, None)
res = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds])
print(res)
from scipy.optimize import linprog
c = [-1, 4]
A = [[-3, 1], [1, 2]]
b = [6, 4]
x0_bounds = (None, None)
x1_bounds = (-3, None)
res = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds])
print(res)
封装scipy.linprog
封装的作用是,更方便动态添加变量,应对变量数量庞大的情况。
'''
min_{x_0,x_1} -x_0 + 4 x_1
s.b. -3x_0 + x_1 \leq 6 \\
-x_0 - 2x_1 \geq -4 \\
x_1 \geq -3
from scipy.optimize import linprog
c = [-1, 4]
A = [[-3, 1], [1, 2]]
b = [6, 4]
x0_bounds = (None, None)
x1_bounds = (-3, None)
res = linprog(c, A_ub=A, b_ub=b, bounds=[x0_bounds, x1_bounds])
print(res)
'''
from scipy.optimize import linprog
class LinearProgramming():
"""
Desc:
"""
def __init__(self):
self._var_dict = {}
self._var_num = 0
self._c = []
self._A_ub = []
self._b_ub = []
self._A_eq = []
self._b_eq = []
self._bounds_list = []
def add_var(self, name : str, lb = None, ub = None):
self._var_dict[name] = self._var_num
self._var_num += 1
self._bounds_list.append((lb, ub))
def add_constraint(self, handwritten_str):
"""
Desc: 为方便解析,规定 handwritten_str 必须用 + 分割,+不可省略
且系数必须写出来如 -3*x0 (无须考虑*和-的优先级)
且含变量项全放左边
如: -3*x0+1*x1<=6
-1*x0 + -2*x1 >= -4
有无空格均可
"""
handwritten_str = handwritten_str.replace(' ', '')
if '<=' in handwritten_str:
left, right = handwritten_str.split('<=')
self._b_ub.append(float(right))
# 找到所有含变量的项,并将系数 list 添加到 A_ub
# 位置对应关系通过 _bounds_list 查询
items = left.split('+')
tmp_A_ub = [0 for _ in range(self._var_num)]
for it in items:
coe, var_name = it.split('*')
index = self._var_dict[var_name]
tmp_A_ub[index] = float(coe)
self._A_ub.append(tmp_A_ub)
elif '>=' in handwritten_str:
left, right = handwritten_str.split('>=')
self._b_ub.append(-float(right)) # 系数需取反
items = left.split('+')
tmp_A_ub = [0 for _ in range(self._var_num)]
for it in items:
coe, var_name = it.split('*')
index = self._var_dict[var_name]
tmp_A_ub[index] = -float(coe) # 系数需取反
self._A_ub.append(tmp_A_ub)
elif '==' in handwritten_str:
left, right = handwritten_str.split('==')
self._b_eq.append(float(right))
items = left.split('+')
tmp_A_eq = [0 for _ in range(self._var_num)]
for it in items:
coe, var_name = it.split('*')
index = self._var_dict[var_name]
tmp_A_eq[index] = float(coe)
self._A_eq.append(tmp_A_eq)
else:
print('handwritten_str error')
exit()
def add_objective(self, handwritten_str):
"""
Desc: minimize objective
为方便解析,规定 handwritten_str 必须用 + 分割,+不可省略
且系数必须写出来如 -3*x0 (无须考虑*和-的优先级)
"""
handwritten_str = handwritten_str.replace(' ', '')
items = handwritten_str.split('+')
tmp_c = [0 for _ in range(self._var_num)]
for it in items:
coe, var_name = it.split('*')
index = self._var_dict[var_name]
tmp_c[index] = float(coe)
self._c = tmp_c
def solve(self, method='interior-point', callback=None,
options=None, x0=None):
"""
Desc: If you need greater accuracy, try method = 'revised simplex'.
"""
self._A_ub = None if self._A_ub == [] else self._A_ub
self._b_ub = None if self._b_ub == [] else self._b_ub
self._A_eq = None if self._A_eq == [] else self._A_eq
self._b_eq = None if self._b_eq == [] else self._b_eq
res = linprog(
c = self._c, A_ub = self._A_ub, b_ub = self._b_ub,
A_eq = self._A_eq, b_eq = self._b_eq,
bounds = self._bounds_list,
method = method, callback = callback,
options = options, x0 = x0
)
return res
if __name__ == "__main__":
lp = LinearProgramming()
lp.add_var('x0', None, None)
lp.add_var('x1', -3, None)
# print(lp._var_dict)
# print(lp._var_num)
lp.add_constraint('-1*x0 + -2*x1 >= -4')
lp.add_constraint('-3*x0+1*x1<=6')
# print(lp._A_ub)
lp.add_objective('4*x1 + -1*x0')
res = lp.solve()
print(res)
pulp
https://www.cnblogs.com/shizhenqiang/p/8274806.html
from pulp import *
def get_re():
pass
def getresult(c, con):
# 设置对象
prob = LpProblem('myPro', LpMinimize)
# 设置三个变量,并设置变量最小取值
x11 = LpVariable("x11", lowBound=0)
x12 = LpVariable("x12", lowBound=0)
x13 = LpVariable("x13", lowBound=0)
x14 = LpVariable("x14", lowBound=0)
x21 = LpVariable("x21", lowBound=0)
x22 = LpVariable("x22", lowBound=0)
x23 = LpVariable("x23", lowBound=0)
x24 = LpVariable("x24", lowBound=0)
x31 = LpVariable("x31", lowBound=0)
x32 = LpVariable("x32", lowBound=0)
x33 = LpVariable("x33", lowBound=0)
X = [x11, x12, x13, x14, x21, x22, x23, x24, x31, x32, x33]
#c = [160, 130, 220, 170, 140, 130, 190, 150, 190, 200, 230]
# 目标函数
z = 0
for i in range(len(X)):
z += X[i]*c[i]
#print(z)
prob += z
# 载入约束变量
prob += x11+x12+x13+x14 == con[0]# 约束条件1
prob += x21+x22+x23+x24 == con[1]
prob += x31+x32+x33 == con[2]
prob += x11+x21+x31 <= con[3]
prob += x11+x21+x31 >= con[4]
prob += x12 + x22 + x32 <= con[5]
prob += x12 + x22 + x32 >= con[6]
prob += x13 + x23 + x33 <= con[7]
prob += x13 + x23 + x33 >= con[8]
prob += x14 + x24 <= con[9]
prob += x14 + x24 >= con[10]
# 求解
status = prob.solve()
print(status)
print(LpStatus[status])
print(value(prob.objective)) # 计算结果
# 显示结果
# for i in prob.variables():
# print(i.name + "=" + str(i.varValue))
for i in prob.variables():
print(i.varValue)
if __name__ == '__main__':
c = [160, 130, 220, 170, 140, 130, 190, 150, 190, 200, 230]
con = [50, 60, 50, 80, 30, 140, 70, 30,10, 50, 10]
getresult(c, con)