Lpsolve是一个跨平台的开源混合整数线性规划(MILP)求解器,提供了Python Api。
http://web.mit.edu/lpsolve/doc/Python.htm
lpsolve_wrapper是lp_solve的python包装类,提供了基于变量的Api。
https://github.com/hangvane/lpsolve_wrapper
动机
Lpsolve虽提供了Python Api,但Lpsolve的Python Api无法管理变量(特别是包含多个值的变量集,如 x i , j x_{i,j} xi,j),也无法对每个变量的系数矩阵自动压平为1维、自动拼接多个变量、得到的最优变量取值也不会自动按变量切割、反压平为单个变量的对应shape。目前使用Lpsolve的常见代码类似于:
from lp_maker import lp_maker, lpsolve
import numpy as np
x = np.ones((2, 2))
y = np.ones((3, 3))
f = np.concatenate([x.ravel(), y.ravel()]).tolist()
x.fill(0)
y.fill(0)
for i in range(2):
x[0][i] = 1
for n in range(3):
y[0][n] = 1
A = [np.concatenate([x.ravel(), y.ravel()]).tolist()]
b = 5
e = [-1, ]
x.fill(0)
y.fill(0)
vlb = np.concatenate([x.ravel(), y.ravel()]).tolist()
x.fill(1)
y.fill(2)
vub = np.concatenate([x.ravel(), y.ravel()]).tolist()
xint = []
scalemode = 1
setminimium = 0
lp = lp_maker(f, A, b, e, vlb, vub, xint, scalemode, setminimium)
lpsolve('solve', lp)
obj = lpsolve('get_objective', lp)
raw_notation = lpsolve('get_variables', lp)[0]
print('objective:', obj)
offset = 0
print('x:', raw_notation[0:2 * 2])
offset += 2 * 2
print('y:', raw_notation[offset + 1:offset + 3 * 3])
需要反复进行细节确认,目前只有2个小规模变量就乱成一团,大规模线性规划就是在写一坨屎山,而且最后得到的目标变量取值还没有反压平reshape。且在写以上demo时我出现了4个bug,全部和numpy结合lpsolve,以及lpsolve提供的接口细节相关,编码效率极低。
lpsolve_wrapper
lpsolve_wrapper是lp_solve的python包装类,提供了基于变量的Api。
特性
- 支持变量管理
- 自动偏移管理
- 自动压平/反压平
- 支持多种约束添加方式
- 支持Python 2.x/3.x
安装
强烈推荐通过 conda
安装 lpsolve
:
conda install -c rmg lpsolve55
如果你是windows用户,可以前往
https://www.lfd.uci.edu/~gohlke/pythonlibs/#lp_solve
下载编译好的 .whl
包,并通过pip离线安装:
pip install the_name_of_lpsolve55.whl
然后将 lpsolve_wrapper.py
添加到你的项目中。
教程
这里提供了几个例子,可以基于这些例子创建更复杂的线性规划,一个简单的例子如下:
- 引入
lpsolve_wrapper
:
import lpsolve_wrapper as lw
- 创建求解器实例,并添加两个变量:
model = lw.Model(
notations={
'x': lw.notation(
lower_bound=0,
),
'y': lw.notation(
lower_bound=0,
)
})
- 调用
lpsolve_wrapper
中的方法添加约束和求解:
model.add_constr(
coefs=[
lw.coef('x', 1),
lw.coef('y', 1),
],
right_value=75,
constr_type=lw.LEQ
)
objective, notation_list = model.lp_solve(
obj_func={
'x': 143,
'y': 60,
},
minimize=False
)
print('objective:', objective)
print('x:', notation_list['x'])
print('y:', notation_list['y'])
lpsolve_wrapper 提供了3个方法来添加约束:
- 通过给单个变量赋值的方式:
model.add_constr(
coefs=[
lw.coef(name='x', idx=[2, 3], value=1),
lw.coef(name='y', idx=[0], value=2),
],
right_value=75,
constr_type=lw.GEQ
)
- 通过输入变量系数矩阵的方式:
model.add_constr_mat(
coef_mats={
'x': [1, 1, 1],
'y': [[1, 2], [3, 4]]
},
right_value=1,
constr_type=lw.EQ
)
- 通过传入回调函数的方式:
def tmp_func(y):
y[2][3] = 1
model.add_constr_callback(
callbacks={
'x': lambda x: x[0].fill(1),
'y': tmp_func
},
right_value=1,
constr_type=lw.EQ,
)
还有若干用例和用例的公式表述可供参考。
- | add_constr() | add_constr_mat() | add_constr_callback() |
---|---|---|---|
用例 0 | ✔ | ||
用例 1 | ✔ | ||
用例 2 | ✔ | ||
用例 3 | ✔ | ✔ | |
用例 4 | ✔ | ||
用例 5 | ✔ |