用python的话
- gurobi,不开源,有讲解,性能最好,建模最方便。
- ortools,建模变量设置方式灵活,但貌似只支持simplex。另外专门有一些特殊问题的包,如最大流问题,最小花费流问题。
- lpsolve貌似只支持simplex
- scipy.linprog 支持simplex(更准确)和interior point(本质上是近似),但建模固定矩阵式,不能灵活设置变量。
- cvxpy,建模变量更灵活些。
- CVXPY relies on the open source solvers ECOS, OSQP, and SCS
- cvxopt 也是固定矩阵式建模。
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 = None,
options = None, x0 = None
)
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)