优化求解器的用户手册,介绍求解器的调用方式、如何输入问题和求解器的APIs清单。
由于本软件APIs比较多,且还在高频增加功能中,因此APIs的介绍会较多地引导至另一个 《MindOpt用户使用手册——完整版》 上查看细节,给您带来的不连贯阅读体验敬请谅解。该文库可以在页面切换文档语言和文档版本。
优化求解器调用方式
在使用之前,请先下载和安装求解器SDK,并获取使用权限,见 快速入门(开通和使用) 和 求解器SDK下载和安装 。
下面列出简单的示例,求解器细节的调用方式和完整案例可查看 更多 。
命令行调用示例:
Linux以及macOS下,假设用户根据安装文档将MindOpt安装到环境变量$MINDOPT_HOME指定目录:
mindopt $MINDOPT_HOME/examples/data/afiro.mps
Windows下
mindopt %MINDOPT_HOME%\examples\data\afiro.mps
C/C++/C#/Java/Python语言调用示例:
V1.x的API与V0.x的API大部分都不相同。请关注自己使用的版本。
V1.x.x
env = mindoptpy.Env()
env.start()
model = mindoptpy.read(filename, env)
model.optimize()
print(f"Optimal objective value is: {model.objval}")
for v in x:
print(f"{v.VarName} = {v.X}")
MDOEnv env = new MDOEnv();
MDOModel model = new MDOModel(env, filename);
model.optimize();
System.out.println("Optimal objective value is: " + model.get(MDO.DoubleAttr.ObjVal));
MDOVar[] x = model.getVars();
for (int i = 0; i < x.length; ++i) {
System.out.println(x[i].get(MDO.StringAttr.VarName) + " = " + x[i].get(MDO.DoubleAttr.X));
}
MDOEnv env = MDOEnv();
MDOModel model = MDOModel(env, filename);
model.optimize();
std::cout << "Optimal objective value is: " << model.get(MDO_DoubleAttr_ObjVal) << std::endl;
std::vector<MDOVar> vars = model.getVars();
for (auto v : vars) {
std::cout << v.get(MDO_StringAttr_VarName) << " = " << v.get(MDO_DoubleAttr_X) << std::endl;
}
MDOenv* env;
MDOemptyenv(&env);
MDOstartenv(env);
MDOmodel* model;
double obj;
MDOreadmodel(env, argv[1], filename);
MDOoptimize(model);
MDOgetdblattr(model, MDO_DBL_ATTR_OBJ_VAL, &obj);
printf("Optimal objective value is: %f\n", obj);
/* Free the environment. */
MDOfreemodel(model);
MDOfreeenv(env);
MDOEnv env = new MDOEnv();
MDOModel model =new MDOModel(env, filename);
model.optimize();
Console.WriteLine($"Optimal objective value is: {model.Get(MDO.DoubleAttr.ObjVal)}");
MDOVar[] vars = model.GetVars();
for (int i = 0; i < vars.Length; i++)
Console.WriteLine($"{vars[i].Get(MDO.StringAttr.VarName)} = {vars[i].Get(MDO.DoubleAttr.X)}");
}
完整例子的代码请查看 更多 。
V0.x.x
# 方法1:从0.19.0版本开始引入新式的创建模型方式,云鉴权更快,消耗的并发度少
env = mindoptpy.MdoEnv()
model = mindoptpy.MdoModel(env)
model.read_prob(filename)
model.solve_prob()
model.display_results()
# 方法2:旧式的创建模型方式仍然支持
# model = mindoptpy.MdoModel()
# model.read_prob(filename)
# model.solve_prob()
# model.display_results()
//<dependency>
// <groupId>com.alibaba.damo</groupId>
// <artifactId>mindoptj</artifactId>
// <version>[0.20.0,)</version>
//</dependency>
// load动态链接库,如下:
Mdo.load("c:\\mindopt\\0.20.0\\win64_x86\\lib\\mindopt_0_20_0.dll");
// 方法1:从0.19.0版本开始引入新式的创建模型方式,云鉴权更快,消耗的并发度少
//Set up environment,放在程序初始化的时候进行,例如MapReduce里的setup阶段
MdoEnv env = new MdoEnv();
//create a model
MdoModel model = env.createModel();
model.readProb(filename)
model.solveProb();
model.displayResult();
model.free();
//JAVA SDK 需要手动释放 env,放在程序结束的时候进行,例如MapReduce里的cleanup阶段
env.free();
// 方法2:旧式的创建模型方式仍然支持,但被标记为过时,将在以后的版本中移除
MdoModel model = new MdoModel();
model.readProb(filename)
model.solveProb();
model.displayResult();
*/
// 方法1:从0.19.0版本开始引入新式的创建模型方式,云鉴权更快,消耗的并发度少
using mindopt::MdoEnv;
MdoEnv env;
MdoModel model(env);
model.readProb(filename);
model.solveProb();
model.displayResults();
// 方法2:旧式的创建模型方式仍然支持
MdoModel model;
model.readProb(filename);
model.solveProb();
model.displayResults();
*/
// 方法1:从0.19.0版本开始引入新式的创建模型方式,云鉴权更快,消耗的并发度少
MdoEnvPtr env;
MdoMdlPtr model;
Mdo_createEnv(&env);
Mdo_createMdlWithEnv(&model, env);
Mdo_readProb(model, filename);
Mdo_solveProb(model);
Mdo_displayResults(model);
Mdo_freeMdl(&model);
// C SDK 需要手动释放 env
Mdo_freeEnv(&env);
// 方法2:旧式的创建模型方式仍然支持
MdoMdlPtr model;
Mdo_createMdl(&model);
Mdo_readProb(model, filename);
Mdo_solveProb(model);
Mdo_displayResults(model);
Mdo_freeMdl(&model);
*/
完整例子的代码请查看 V0.x版本文档 。
支持求解的优化问题
当前版本的求解器支持:
-
线性规划:求解 线性规划(LP) 的能力已完整。支持原始/对偶单纯形法 (simplex) 和内点法 (interior point)、支持求解大规模网络流优化问题。
-
整数规划:支持求解 混合整数线性规划(MILP) 问题的分支定界算法 (branch-and-cut solver)。
优化问题的输入方式
优化问题支持3种输入方式:文件输入、数据建模APIs输入、外部建模工具调用。
方式1:文件输入
-
支持 MPS 格式 和 LP 格式 ,如
.mps
和.lp
,以及对应文件的压缩文件如:.mps.gz
和.mps.bz2
。 -
支持
.dat-s
格式, 如SDP问题示例。 -
支持
.qps
格式,如QP问题数据。 -
mindoptampl模块支持
.nl
格式文件输入,如在命令行中输入mindoptampl filename.nl
。
更详细的介绍请查看 更多 。
方式2:建模APIs输入
V1.x的API与V0.x的API大部分都不相同。请关注自己使用的版本。
V1.x.x
相关联的APIs有多种,同一个数据也可以有多种输入方式。
-
按行输入Python示例简述:
-
用
mindoptpy.Model.modelsense = MDO.MINIMIZE
将目标函数设置为最小化; -
用
mindoptpy.Model.addVar()
来添加优化变量,并分别定义其下界、上界、目标系数、名称和类型; -
用
mindoptpy.Model.addConstrs()
来添加约束。
-
-
按列输入Python示例简述:
-
用
mindoptpy.Model.modelsense = MDO.MAXIMIZE
将目标函数设置为最小化; -
开始时调用
mindoptpy.Model.addConstrs()
来创建带有指定的左侧和右侧值的约束(无非零元素); -
创建临时的列对象
mindoptpy.Model.Column()
来按顺序地保存约束和非零元素的值; -
最后调用
mindoptpy.Model.addVar()
来创建新的变量,及其相应的目标函数系数、列向量中的非零元、下界和上界、变量名以及变量类型。
-
更详细的介绍请查看 更多 。其中,属性描述(Attributes)列表见 更多 ,且API章节有对应语言的完整例子。
V0.x.x
相关联的APIs有多种,同一个数据也可以有多种输入方式。
-
按行输入Python示例简述:
-
用
mindoptpy.MdoModel.set_int_attr()
将目标函数设置为最小化; -
用
mindoptpy.MdoModel.add_var()
来添加四个优化变量,并分别定义其下界、上界、名称和类型; -
用
mindoptpy.MdoModel.add_cons()
来添加约束。
-
-
按列输入Python示例简述:
-
用
mindoptpy.MdoModel.set_int_attr()
将目标函数设置为最小化; -
开始时调用
mindoptpy.MdoModel.add_cons()
来创建带有指定的左侧和右侧值的约束(无非零元素); -
创建临时的列对象
mindoptpy.MdoCol()
来按顺序地保存约束和非零元素的值; -
最后调用
mindoptpy.MdoModel.add_var()
来创建新的变量,及其相应的目标函数系数、列向量中的非零元、下界和上界、变量名以及变量类型。
-
更详细的介绍请查看 更多V0版文档 。其中,模型的属性描述(Model Attributes)列表见 更多V0版文档 。
方式3:建模语言 MindOpt APL、 AMPL、Pyomo、PuLP、JuMP
采用建模语言的好处是用建模语言的API来建模,可以方便切换不同版本求解器。
MindOpt支持一些常见的建模工具,当前支持以下几种:
1. MindOpt APL
2022年开始支持MindOpt APL建模语言,是MindOpt团队自研的一款建模语言。
2. AMPL
使用 AMPL 调用 MindOpt 之前需要先安装 MindOpt 和 AMPL 。mindoptampl 应用位于安装包的
\bin\mindoptampl
。mindoptampl 提供了一些可配置的参数,用户可以通过 AMPL 的
option
命令设置
mindoptampl_options
参数,如:
ampl: option mindoptampl_options 'numthreads=4 maxtime=1e+4';
更详细的介绍和案例请查看 更多 。
3. Pyomo
使用 Pyomo 调用 MindOpt 之前需要先安装 MindOpt 和 Pyomo。调用 MindOpt 求解器的 Pyomo API 需要使用接口文件
mindopt_pyomo.py
。 MindOpt 的 Pyomo 接口是继承自 Pyomo 的
DirectSolver
类,实现代码在安装包的
\lib\pyomo\mindopt_pyomo.py
。在 Python 代码中导入该文件:
from mindopt_pyomo import MindoDirect
更详细的介绍和案例请查看 更多 。
4. PuLP
使用 PuLP 调用 MindOpt 之前需要先安装 MindOpt 和 PuLP。调用 MindOpt 求解器的 PuLP API 需要使用接口文件
mindopt_pulp.py
。 MindOpt 的 PuLP 接口继承自 PuLP 的
LpSolver
类,实现代码在安装包的
\lib\pulp\mindopt_pulp.py
。在 Python 代码中导入该文件:
from mindopt_pulp import MINDOPT
更详细的介绍和案例请查看 更多 。
5. JuMP
JuMP 是一个基于 Julia 编程语言的开源建模工具。使用 JuMP 调用 MindOpt 之前需要先安装 MindOpt、Julia、JuMP、AmplNLWriter。调用JuMP API来创建建模对象,并指定使用mindoptampl作为求解器。
model = Model(() -> AmplNLWriter.Optimizer(mindoptampl))
更详细的介绍和案例请查看 更多 。
求解时的参数设置
运行求解器可以设置输入参数,V1版文档请参考
参数
章节。如设置求解器的最大求解时间
"MaxTime"
。V0版本文档请参考
可选输入参数
章节。
计算设备配置参考:LP求解时不同算法特性
配置机器资源时候,不同问题结构、算法选择,耗费的机器资源会有差异,请根据需要测试选择。
关于LP求解,当前我们线性规划(LP)的求解提供了Simplex(单纯形法)、IPM(Interior Point Method,内点法)、Concurrent(并发优化)算法。在求解时候,执行流程如下文的“执行流程”图片所示,会默认选Concurrent,您可以通过设置“Method”参数来选择算法。
这3种算法的区别如下:
|
Simplex(单纯形法) |
IPM(Interior Point Method,内点法) |
Concurrent(同时优化) |
特性 |
- 通常情况下对数值敏感低 - 耗费内存更少 - 支持Warm-start |
- 对数值更敏感 - 比Simplex方法要多2~10倍的内存 - 不支持Warm-start - 对大规模的问题可能更适用 |
- 同时进行两个方法的优化,耗费的内存更多 - 更鲁棒 - 在求解新类别问题的时候,建议先用本方法来尝试求解,帮助分辨Simplex或IPM方法哪种更合适,辅助后续算法选取 |
计算设备需求 不同问题可能有明显差异,请以实测为准。以下实验室的测试值供参考: |
|||
当问题约束量为43200,非零元素为1038761时 |
测试最大内存占用为350 MB |
测试最大内存占用为620 MB |
测试最大内存占用为920 MB |
当问题约束量为986069,非零元素为4280320时 |
测试最大内存占用为1250 MB |
测试最大内存占用为1500 MB |
测试最大内存占用为1650 MB |
当问题约束量为4284,非零元素为11279748时 |
测试最大内存占用为2050 MB |
测试最大内存占用为5200 MB |
测试最大内存占用为5200 MB |
当问题约束量为22117,非零元素为20078717时 |
测试最大内存占用为3400 MB |
测试最大内存占用为5600 MB |
测试最大内存占用为8300 MB |
- 内存的消耗取决于问题的形式、规模以及稀疏程度,如需提前预估内存资源时,建议先通过问题的规模和稀疏程度来推算内存消耗,再乘以一定的倍数作为内存资源预留的预估值。
求解结果获取
命令行运行
命令行运行求解时,会打印输出求解过程输出和结果的summary,同时会在求解的文件处生成同名结果文档
.bas
和
.sol
。
如下图示意运行
mindopt afiro.mps
后,文件夹中生成了
afiro.bas
和
afiro.sol
文件,
.bas
文件保存的是基,
.sol
文件保存的是求解的最优目标值和变量值的解。
API求解和获取结果
V1.x的API与V0.x的API大部分都不相同。请关注自己使用的版本。
V1.x.x
待求解的问题输入后,可以调用
optimize()
的API来求解,如python的求解指令
model.optimize()
。
求解完后,可以调用API来获取结果,以Python为例:
-
可以调用
model.status
获取求解的状态。有UNKNOWN、OPTIMAL、INFEASIBLE、UNBOUNDED、INF_OR_UBD、SUB_OPTIMAL。如Python里的MDO.OPTIMAL
-
可以用
model.objval
获取目标值。-
还可以调用
model.getAttr(MDO.Attr.PrimalObjVal)
来获取其他的模型属性值,类似地:-
SolutionTime
对应:总执行时间(秒) -
DualObjVal
对应:对偶目标值 -
ColBasis
对应:原始解的基
-
-
-
可以调用
var.X
来变量的解。 -
更多解的属性描述(Attributes)列表见 更多 。
完整的代码示例可参考安装包的example文件夹,或文档 更多 ,和各个语言API文档最后一章,如Python的 examples 。
V0.x.x
待求解的问题输入后,可以调用
solveProb
的API来求解,如python的求解指令
model.solve_prob()
。
求解完后,可以调用API来获取结果,以Python为例:
-
可以调用
model.display_results()
来打印结果。 -
可以调用
model.get_status()
获取求解的状态。 -
可以调用
model.get_real_attr("PrimalObjVal")
来获取原始目标值,类似地:-
"DualObjVal"
对应:对偶目标值 -
"PrimalSoln"
对应:原始解 -
"ColBasis"
对应:原始解的基 -
更多解的属性描述(Solution Attributes)列表在 更多V0版本文档
-
完整的代码示例可参考安装包的example文件夹或文档。
求解辅助分析工具、高级建模技巧
约束不可行性分析
在建模求解过程中,会遇到由于某些约束互相冲突导致问题不可行(infeasible)的情况,分析不可行问题并识别出导致约束冲突的关键约束能有效帮助建模。这类导致问题不可行的最小约束子集被称为不可约不可行系统(IIS, irreduciable infeasible system)。
MindOpt 设计了用来计算IIS的API,当问题
"INFEASIBLE"
时,用户可以通过 compute IIS 的API来对不可行问题进行分析,如可依据此对IIS中的约束进行修正或移除,使得优化问题变得可行。
IIS的Python接口调用示例如下:
V1.x.x
if model.status == MDO.INFEASIBLE or model.status == MDO.INF_OR_UBD:
idx_rows, idx_cols = model.compute_iis()
完整的 compute IIS 的说明请参阅 更多 。
V0.x.x
status_code, status_msg = model.get_status()
if status_msg == "INFEASIBLE":
idx_rows, idx_cols = model.compute_iis()
完整的 compute IIS 的说明请参阅 更多V0版文档 。
回调功能 (Callback)
MindOpt使用经典的分支定界法来求解MIP问题。MindOpt为用户提供回调功能,帮助用户对MIP问题的求解过程进行跟踪与修改。利用回调功能,用户可以提供一些决策以修改求解过程,包括:
-
添加割平面,裁剪不会出现最优解的分支;
-
干预MindOpt的分支选择策略,控制节点二分方法及遍历顺序;
-
添加自定义可行解(比如通过某种启发式算法得到的解),一个较好的可行解可以提高MindOpt的求解效率。
可以如下方式建立回调函数:
def callback(model, where):
完整的 Callback 说明请参阅 更多 。
求解器调参
MindOpt Tuner也可以对求解器超参调优,提供业务场景的数据,采用MindOpt Tuner,经过调参后,可以得到一个业务场景定制的更快的求解器。目前部分功能在 MindOpt云上平台 中可以使用。
AI工程师辅助建模
AI工程师是在LLM大模型的基础上,结合特定领域软件工具的知识,用于该领域基础技术咨询的一个AI机器人。 MindOpt Copilot 是一款帮助用户使用MindOpt技术解决“数学优化”问题的AI工程师。可用于“数学优化”领域的技术咨询。
当前,MindOpt Copilot AI工程师支持使用MindOpt Solver优化求解器、MindOpt APL建模语言等工具,通过自然语言和表格数据沟通,自动根据用户的业务问题进行数学建模、列数学公式、码代码、调用MindOpt软件来求解等工序。并且可以将机器人生成的文字、数学公式、代码等,生成项目,快捷地导入开发项目进行开发。
2023年9月首次上线,可在 MindOpt云上平台 中可以使用。
其他功能
数据脱敏
MindOpt的命令行功能里有
--sanitize
可进行数据脱敏,方便外发数据用于技术交流或求解器调参改进。其脱敏原理可以参考
博客
。
求解器的执行流程
MindOpt的执行流程:
不同的优化问题会有不同的求解方法,整体流程大致相同。以下是线性规划LP问题的调用示例,过程中有参数可以由用户选择设定。
完整APIs和调用方式
完整的说明请查阅 《MindOpt用户使用手册——完整版》
目录示意如下: