我最近在研究一个问题的解决方案时,找到了一种基于最优化理论的方法。即通过「定义目标函数」—「给出约束条件」—「找到最优解」的流程找到问题的答案。由于目标函数中存在二次项,且变量从{0,1}中取值,因此,该问题属于目标函数中含有二次项的混合整数规划问题,简称MIQP(mixed integer programs with quadratic terms in the objective function)。

老实讲,我已经把原本就学得不是很好的运筹学知识全部还给老师了,现在能记住的,除了「分支定界」、「模拟退火」等少数几个算法的名称外,就只剩下课本黄色的封皮了。于是,趁着这个机会,我查了一些资料,首先解决「怎么用」的问题,然后再研究如何去优化。

1、利用Pyomo来进行建模

1.1 Pyomo简介

在学生时代,用的最多的工具非MATLAB莫属了,由于集成了各种工具箱,它的功能确实非常强大。不过,我所寻找的解决方案(如果成功验证后)是要集成到产品中去的,在软件许可方面可能会遇到问题。随着开源软件的崛起,一些优秀的项目在性能和易用性等方面已经不输商业软件了。因此,为了实现这个方案,我的第一想法就是搜索看是否有用python开发的解决方法。

接下来就发现了Pyomo这个软件包。从其主页描述可以知道,它可以用来定义、求解和分析各种优化问题的模型。包括:

  • 线性规划
  • 二次规划
  • 非线性规划
  • 混合整数线性规划
  • 混合整数二次规划(这个就是我的场景)
  • 混合整数非线性规划

更多支持问题的类型,可以从主页获取。

需要注意的是,上面说的定义模型,指的是Pyomo有一套自己的语言来将数学中用各种符号描述的优化模型程序化,从而使之符合python的语法要求;而求解实际上是利用了第三方的解算器。也就是说,Pyomo本身不具备求问题解的功能,而是定义了一套规范,并调用其他的解算器来求解。

我理解的这样做的好处在于,不同的解算器有着不同的使用方法,而Pyomo对这些接口进行了规范化,从而使用户更关注于模型本身;此外,python代码所具备的高可读性,也更容易让人将数学模型转化为代码脚本。

1.2 Pyomo安装

可以使用conda或者pip来进行安装,与安装其他的第三方python包并无区别。

强烈建议在安装前先创建一个干净的虚拟环境,这对于包的管理是非常方便的。特别是——在后面会看到——对于这种会利用其他第三方软件包(解算器)的软件包!

1.3 示例

直接给出官网的一个简单示例:

python 多目标 python多目标线性规划_pyomo

利用`Pyomo`对上述问题进行的代码如下,我对每一行进行了注释:

import pyomo.environ as pyo

# 创建一个模型实例
model = pyo.ConcreteModel()
# 向模型实例添加变量,x是变量组成的向量,维度是2,并且,每个变量都是非负实数
model.x = pyo.Var([1, 2], domain=pyo.NonNegativeReals)
# 向模型实例添加目标函数,目标函数是由向量中的各个变量与其系数组成
model.OBJ = pyo.Objective(expr=2 * model.x[1] + 3 * model.x[2])
# 添加约束,由于在添加变量的时候已经指定了非负实数,因此,这里的约束条件只剩下一个了
model.Constraint1 = pyo.Constraint(expr=3 * model.x[1] + 4 * model.x[2] >= 1)

模型构建好了以后,接下来就是调用解算器进行求解了。这一步只需要两行代码:

opt = pyo.SolverFactory('glpk')
opt.solve(model)

第一行,实例化了一个解算器,并指定了利用“glpk”这个解算器;第二行,调用解算器的solve方法,对模型进行求解。

前面提到,解算器是需要额外安装的。因此,如果不在环境中安装“glpk”这个解算器,那么上述代码是会报错的,所以,需要首先安装glpk。

glpk是一个开源软件,用于解决大规模的线性规划、混合整数规划等问题。可以直接利用conda进行安装:

conda install glpk

安装完成后,上述代码就可以成功运行。求解完成后,我们可以通过下述代码获取到最优解:

print('x1: ', pyo.value(model.x[1]))
print('x2: ', pyo.value(model.x[2]))

打印的内容:

x1:  0.333333333333333
x2:  0.0

1.4 总结

从上面的过程可以看出,利用Pyomo求解的基本过程就是:「建立模型」—「调用解算器」—「得到结果」。针对不同类的问题,建模的过程会有所不同。

2、解算器

上面提到的“glpk”是解算器的一种,但它仍有一定的限制:

  • 不能解决非线性规划问题
  • 与商业的解算器相比仍有一定的性能差距

为了解决我的问题,必须寻求其他的解算器,在参考的论文中,作者使用的是GAMS平台的CPLEX解算器。

CPLEX是IBM推出的一款用于构建和求解大规模、复杂的优化模型的产品。准确来说,产品的名称是:IBM ILOG CPLEX Optimization Studio,分为试用版和付费版两种。按照国外产品一贯的行为标准,试用版对问题规模有限制,付费版没有限制可价格很贵。这里我使用的是试用版。

实际上,完全可以把我的模型用IBM的产品写出来(即不用python),但这样的话发布可调用的API就成了一个问题。所以,我还是选择用Pyomo写模型,然后调用IBM的求解器。

不过我找了一圈,貌似没有直接的CPLEX求解器文件可供下载,这也是可以理解的,毕竟人家是一个完整的产品,不太可能将产品的零部件拆出来供人下载。所以,要想利用这个求解器,就得先安装整个试用版的软件,然后从软件安装目录里把求解器模型找到。

我用的是mac,安装好之后,求解器文件的位置是:/Applications/CPLEX_Studio_Community201/cplex/bin/x86-64_osx,其中有一个cplex的可执行文件,这就是我们要找的模型文件了,将其拷贝出来,然后在代码中指定好位置即可,像下面这样:

opt = pyo.SolverFactory('cplex', executable='/path/to/models/cplex')