【发布时间】:2019-04-24 09:33:26
【问题描述】:
我在 Python 中使用 lpsolve 已经很长时间了,总体来说效果很好。我什至为它编写了自己的 Cython 包装器,以克服原始 Python 包装器所带来的混乱。
我的 Cython 包装器在设置问题时要快得多,但当然解决时间只取决于 lpsolve C 代码,与 Python 无关。
我只解决实值线性问题,没有 MIP。我必须解决的最新(也是最大的)LP 有一个大小约为 5,000 x 3,000 的约束矩阵,设置加求解大约需要 150 毫秒。问题是,我必须在我的模拟中多次解决相同问题的略微修改版本(约束、RHS、边界等对于具有许多时间步长的模拟来说是时间相关的)。约束矩阵通常非常稀疏,大约为 NNZ 的 0.1% - 0.5% 或更少。
使用 lpsolve,问题的设置和解决如下所示:
import numpy
from lp_solve.lp_solve import PRESOLVE_COLS, PRESOLVE_ROWS, PRESOLVE_LINDEP, PRESOLVE_NONE, SCALE_DYNUPDATE, lpsolve
# The constraint_matrix id a 2D NumPy array and it contains
# both equality and inequality constraints
# And I need it to be single precision floating point, like all
# other NumPy arrays from here onwards
m, n = constraint_matrix.shape
n_le = len(inequality_constraints)
n_e = len(equality_constraints)
# Setup RHS vector
b = numpy.zeros((m, ), dtype=numpy.float32)
# Assign equality and inequality constraints (= and <=)
b[0:n_e] = equality_constraints
b[-n_le:] = inequality_constraints
# Tell lpsolve which rows are equalities and which are inequalities
e = numpy.asarray(['LE']*m)
e[0:-n_le] = 'EQ'
# Make the LP
lp = lpsolve('make_lp', m, n)
# Some options for scaling the problem
lpsolve('set_scaling', lp, SCALE_DYNUPDATE)
lpsolve('set_verbose', lp, 'IMPORTANT')
# Use presolve as it is much faster
lpsolve('set_presolve', lp, PRESOLVE_COLS | PRESOLVE_ROWS | PRESOLVE_LINDEP)
# I only care about maximization
lpsolve('set_sense', lp, True)
# Set the objective function of the problem
lpsolve('set_obj_fn', lp, objective_function)
lpsolve('set_mat', lp, constraint_matrix)
# Tell lpsolve about the RHS
lpsolve('set_rh_vec', lp, b)
# Set the constraint type (equality or inequality)
lpsolve('set_constr_type', lp, e)
# Set upper bounds for variables - lower bounds are automatically 0
lpsolve('set_upbo', lp, ub_values)
# Solve the problem
out = lpsolve('solve', lp)
# Retrieve the solution for all the variables
vars_sol = numpy.asarray([lpsolve('get_var_primalresult', lp, i) for i in xrange(m + 1, m + n + 1)], dtype=numpy.float32)
# Delete the problem, timestep done
lpsolve('delete_lp', lp)
由于太长无法解释的原因,我的 NumPy 数组是所有单精度浮点数组,我希望它们保持这种状态。
现在,在所有这些痛苦的介绍之后,我想问一下:有没有人知道另一个库(具有合理的 Python 包装器),它允许我设置和解决这种规模的问题,速度比 lpsolve 快(或可能更快) ?我看过的大多数库(PuLP、CyLP、PyGLPK 等)似乎都没有一种直截了当的方式来表示“这是我的整个约束矩阵,一次性设置它”。它们似乎大多倾向于成为允许像这样花哨的东西的“建模语言”(CyLP 示例):
# Add variables
x = s.addVariable('x', 3)
y = s.addVariable('y', 2)
# Create coefficients and bounds
A = np.matrix([[1., 2., 0],[1., 0, 1.]])
B = np.matrix([[1., 0, 0], [0, 0, 1.]])
D = np.matrix([[1., 2.],[0, 1]])
a = CyLPArray([5, 2.5])
b = CyLPArray([4.2, 3])
x_u= CyLPArray([2., 3.5])
# Add constraints
s += A * x <= a
s += 2 <= B * x + D * y <= b
s += y >= 0
s += 1.1 <= x[1:3] <= x_u
老实说,我并不关心灵活性,我只需要在问题设置和解决过程中的原始速度。创建 NumPy 矩阵以及执行上述所有花哨的操作肯定会成为性能杀手。
如果可能,我宁愿继续使用开源求解器,但欢迎提出任何建议。对于这篇长篇文章,我深表歉意。
安德烈亚。
【问题讨论】:
-
CyLP 可能是候选人。在将稀疏矩阵发布到 clpy(extract:
model += lhs * x == rhs)之前,我确实使用了很多更底层的 numpy/scipy 建模 here。当然,这种(scipy 的)稀疏格式需要为 Cbc/Clp 复制/转换,但这在非零的数量上应该是线性的。 -
@sasha:感谢您的评论和链接。看起来很有趣,尽管在我的情况下约束矩阵的生成非常复杂,并且通过转换到稀疏矩阵需要与设置线性问题大约相同的时间(即,2D NumPy 数组到 scipy 稀疏大约需要 70-80 毫秒)。我会以更全面的方式查看您的代码,尽管我不确定它是否能轻松适应我手头的问题......
-
在 scipy 1.6 中尝试
linprog( method="highs-ipm" ... ),使用稀疏的 csR 数组? (我猜稀疏的 float32 将被转换为 float64——doc 没有说。)
标签: python numpy linear-programming lpsolve