混合整数非线性规划_SCIP混合整数规划框架简介

SCIP:优化求解器介绍

题主学习SCIP起因是CPLEX并不支持列生成算法,经过一番打听,发现SCIP(Solving Constraint Integer Programs)一款非常成熟的开源混合整数规划(MILP)框架,支持自定义搜索树中的各个模块,当然包括在分支限界(Branch and Bound)过程中添加变量的功能,如获至宝。SCIP核心库由C写成,有相应的C++封装函数,相比CPLEX,GUROBI等商业软件,上手难度比较高。现在运筹学应用中,基本以调库为主,但是要想对切平面(Cut),启发式算法(Heuristic)和分支算法(Branch)等问题进行研究,开源软件是必不可少的。开源项目COIN-OR也提供了不少代码供学术界研究,SCIP是相对比较完备的框架。SCIP在编译的时候可以选择使用商业线性规划求解器。

本文主要挑一些SCIP的重点介绍,具体细节可以参考官方网站。

SCIP起源:

SCIP的起源于ZIB(Zuse Institute Berlin),由博士生Tobias Achterberg奠定整个框架,经过进一步发展到2019年有了6.0.1版本。Tobias毕业后加入IBM CPLEX团队,之后跳槽到GUROBI,因此GUROBI还提高了软件的售价。整个SCIP的基础可以参见他的400多页的毕业论文https://opus4.kobv.de/opus4-zib/files/1112/Achterberg_Constraint_Integer_Programming.pdf。SCIP之所以命名成整数约束规划求解器,是因为他的技术结合了整数线性规划(Integer Linear Programming)和约束规划(Constraint Programming)的技术。整数线性规划理论根植于多面体理论(Polyedral Theory)和切平面(Cutting Plane)理论,以及算法中的搜索技术;约束规划起源于约束满足问题,这个视角下整数线性规划约束被视为一组合取范式(CNF),利用冲突分析(Conflict Analysis)和领域传播(Domain Propagation)迭代缩小决策变量的定义域,以减小搜索空间。

其实比较令人感兴趣的是前言中关于他的博士生涯的介绍,我们可以看到大神的成长之路由诸多因素构成,在此截取一些片段以飨读者。

Tobias在2000年的硕士论文做的是用神经网络学习混合整数规划中的分支(Branch)策略。他在此获得了ZIB某位成员的整数规划(MIP)求解器,分析并学习了其中原理。他的博士题目是利用混合整数规划设计大规模集成电路(VALSE),他意识到解决这个问题最好的方案是结合整数线性规划和约束规划。从2002年开始,在博士的头两年里,他一直进行着SCIP的开发,这期间他的导师给了他极大的自由和耐心,因为这两年间没有发表一篇文章,要知道德国博士一般也就3-4年(Tobias在2007年毕业)。在此期间他又从工业界的合作者中拿到了数据进行测试。在此期间他还结识了一位白俄罗斯从事整数规划的研究者Yakov Novikov,学习了冲突分析技术(其中有一段趣闻,此人刚到柏林被俄罗斯黑帮误认为他是携带大笔现金来德国买车的东欧人,这些人抢劫时,他出示了自己的一摞论文证明自己只是一个贫穷的研究者后,还是被释放了)。当他写了25万行C的代码后,发现自己离毕业时间不多,那也是是他30岁的生日,他才开始写作毕业论文。

到现在为止,SCIP已经由众多开发者共同维护,文档和功能也比较丰富。

SCIP框架:

SCIP库由C写成,SCIP的程序分模块编写,每个模块由一个.h和.c声明和定义,并对应于混合整数规划的算法的一个模块,分别有:

Constraint handlers: 约束处理模块,一般每一类需要特殊处理的约束会构建一个handler专门处理。一般来说,这个模块定义的约束都与问题本身有关,特别是这类约束的实例太多或者约束是非线性的时候,算法需要在根据松弛解判断是否满足此约束,然后再添加禁止这个解产生的约束。

Variable pricers: 变量定价模块,分支定价和列生成所需要的模块,用于在线性规划求松弛解之后添加变量,如果主问题有可行解,在PRICERREDCOST函数根据reduced cost添加列;如果主问题没有可行解,在PRICERFARKAS中由对偶单纯形根据Farkas Lemma添加列。注意,列是指搜索时候纳入线性规划中的部分变量。

Presolvers: 预处理器,最好你的问题定义的时候比较简约,不需要预处理。

Separators: 分割模块,根据当前松弛解生成切平面对应的约束,使得问题更紧。一般来说,这个模块定义的约束都是为了生成整数解,比如各种的切平面不等式。

Propagators: 传递模块,利用约束规划的冲突分析,收紧变量的范围。

Branching rules: 分支规则模块,略过。

Node selectors: 节点选择模块,略过。

Primal Heuristics: 主启发式模块,在线性规划求松弛解之前,通过启发式算法得到一个好的可行解。

File readers: 文件读取模块,略过。

Diving heuristics: 下搜启发式模块,控制深度优先搜索。

Event handler: 事件处理模块,以上模块都会在搜索树的节点的某个过程中触发,都属于一种特殊的事件处理模块。

以上模块,可以用C写或者用C++的封装类定义。一般来说,每个模块对应都需要初INIT,EXIT,INITSOL,EXITSOL函数,分别用于初始化和退出经过预处理的问题和没有经过预处理的问题。如果没有专门写并添加到SCIP类中,会执行默认模块或者不执行此模块。

根据这篇教程,各个模块对算法效率的影响:分支规则(219%), 切平面分割(117%), 预处理(48%), 主启发(34%, 对于找到第一个可行解319%), 节点选择(28%),领域传播(20%),冲突分析(14%)。

下图解释了各个模块在一个节点的调用顺序。

混合整数非线性规划_SCIP混合整数规划框架简介_第1张图片

以及在节点的循环步骤,十分复杂,可以学到很多

混合整数非线性规划_SCIP混合整数规划框架简介_第2张图片

混合整数非线性规划_SCIP混合整数规划框架简介_第3张图片

接下来是一些个人建议。

SCIP安装:

SCIP拥有独立的安装包,支持Windows,Linux还有Macos系统,不过这些安装包的默认编译方式采用SoPlex作为线性规划的求解器。如果想用其他求解器需要,自己在编译并指定,SCIP

支持Cmake和make系统。建议,在Linux或者Macos环境下编译,因为SCIP还需要一些其他几个包,windows系统安装这些包会非常麻烦。

SCIP数据结构和内存管理:

SCIP的样例代码,内存管理皆为调用SCIP的库函数,用于在堆区分配全局数据和在栈区分配搜索节点的数据。如果用C写得话,建议还是手动调用分配函数;如果用C++的话,对于全局数据,比如Probdata,直接new,delete操作即可,至于定义于节点的函数,采用a = A()的方式,交给编译器自动在栈上创建和释放。

SCIP学习路线:

配合官方文档,教程,Tobias的论文,以及Exemples和Applications里面的样例学习。

你可能感兴趣的:(混合整数非线性规划)