☰
Bonmin 源码编译安装与 MINLP 求解实战指南
2026/9/26 7:25:20 网站建设 项目流程

简介:Bonmin-master 是开源混合整数非线性规划(MINLP)求解器 Bonmin 的主分支源码包,面向需要求解含整数变量与非线性函数优化问题的科研人员和工程师。压缩包共含 300 个文件,以 98 个 C++ 源文件与 91 个头文件为核心,另有 25 个.in 配置模板、19 个 TeX 文档、17 个 Automake 脚本及 configure、Makefile 等构建支撑,整体约 950KB,目录结构清晰,便于直接编译或定位相关实现。目前已有 610 人学习下载。通过这份代码,读者可以获取 Bonmin 的完整算法实现、依赖说明与编译配置模板,在自有环境中搭建基于 LP/NLP 的分支定界求解流程,并据此进行 MINLP 问题的建模、实验或二次开发。

1. 拿到 Bonmin-master 源码包以后:混合整数优化里的 MINLP 求解器到底能解决什么

Bonmin-master 是 Bonmin 求解器在 GitHub 上的开发主线源码包,Bonmin 解决的是混合整数非线性规划(MINLP):模型里同时存在 0-1 或整数变量和非线性函数。典型的例子是设备选型(离散)叠加操作参数(连续、非线性的能耗或产量方程),电力调度里的机组启停(离散)叠加交流潮流(非线性),这些都超出 Cplex、Gurobi 能直接处理的范畴。你需要知道的是:Bonmin 本身不是一个建模语言,它读 AMPL 的 .nl 格式,也提供 C++ 接口;最常见的用法是通过 Pyomo、AMPL 或 GAMS 建模后交给它求解。下面直接以源码包为中心,走一遍“编译安装、建第一个 MINLP、调参数、排坑、验证最优性”的完整路径,适合手里有 MINLP 模型想尽快跑通的人。

2. 源码编译 Bonmin:先理清依赖再 configure,一条可复现的安装路径

2.1 为什么第一反应 pip install 会落空:Bonmin 的依赖结构

搜索记录里和 Bonmin 绑在一起的高频词是 install。很多人的第一反应是pip install bonmin,或者像 Maven 里敲clean install一样,指望一个包管理器把所有事搞定。但 Bonmin 不是一个 Python 包,也不是一个能通吃所有 Linux 发行版的预编译二进制。它的形态是 COIN-OR 传统的源码包加 configure/make,编译前必须把依赖链理清楚,否则 configure 会在早期直接报错退场。

Bonmin 的依赖结构不算复杂,但每层都绕不开。核心依赖大致是这几块:

依赖库作用缺了会怎样
ASL读取 AMPL 的 .nl 模型文件,输出 .solconfigure 找不到 ASL 头文件,无法生成 bonmin 可执行文件
Cbc提供混合整数分支定界框架离散变量搜索部分没有着落
Clp作为 LP 子求解器,处理分支节点的松弛问题编译链断裂
CoinUtils参数解析、文件 IO 等公共工具几乎所有 COIN-OR 项目都依赖它
Osi求解器抽象接口层无法把 MIP 问题传给具体的 LP/MIP 求解器
Ipopt负责连续非线性子问题求解非线性约束和连续变量直接没处解

Bonmin 的工作方式可以理解为:外层离散搜索交给 Cbc 的分支框架,或者用外近似/割平面生成器(B-OA、B-Hyb),每一层的 NLP 子问题全部交给 Ipopt。它自己不直接做连续非线性求解,所以这些依赖缺一个就过不去。这也是为什么源码包下载后第一件事不是./configure,而是先检查 ThirdParty 目录。

master 分支还有另一个隐藏坑:源码包里的 ThirdParty 子模块(ASL、Mumps 等)在 GitHub 上是以 git submodule 形式挂进来的。如果你用的是页面上的 Download ZIP 下载,子模块目录会是空的。打开 ThirdParty 目录看一眼,里面只有 README 或者什么都没有,就说明没拉全。

2.2 最小可运行的编译步骤:clone、configure、make 三步

先说最简单的场景:本机已经有一个编译好的 Ipopt,并且安装前缀是 /opt/ipopt。这时候编译 Bonmin 只依赖 Cbc、Clp、CoinUtils、Osi 和 ASL,用源码树自带的 ThirdParty 就能补齐。命令如下:

git clone --recursive https://github.com/coin-or/Bonmin.git cd Bonmin ./configure --prefix=/opt/coinor --with-ipopt=/opt/ipopt make -j $(nproc) make install

--recursive是 clone 时就必须带的,它会顺带拉取 ThirdParty 下的 git 子模块,避免后面 configure 报“找不到 ASL”“找不到 CoinUtils”一类问题。--prefix=/opt/coinor把可执行文件、库和头文件装到独立目录,后续想卸载时直接删目录,不会污染系统路径。--with-ipopt=/opt/ipopt告诉 configure 去哪找 Ipopt 的头文件和库;如果你的 Ipopt 不是标准安装,configure 会找不到它,这时可以在选项后追加LDFLAGS=-L/opt/ipopt/lib CPPFLAGS=-I/opt/ipopt/include/coin来补路径。-j $(nproc)用满 CPU 核数是常规操作,Bonmin 编译不慢,但依赖多,单线程要等不少时间。

如果本机没有现成的 Ipopt,也不需要先去单独编译一个。Bonmin 源码包里自带 coinbrew 脚本,它会按需拉取并编译 Ipopt 和它的 ThirdParty 依赖,包括 Mumps。一套完整命令是这样:

git clone --recursive https://github.com/coin-or/Bonmin.git cd Bonmin ./coinbrew build Bonmin --prefix=/opt/coinor

coinbrew 做的事情比手动 configure 更省心:先处理 Ipopt,然后再回来构建 Bonmin,依赖顺序和版本匹配都自动处理好。第一次跑 coinbrew 会花不少时间,因为 Ipopt 本身的依赖链也不短,过程中需要联网下载 Mumps、ASL 等源码包。如果你习惯在 WSL 里装依赖,这时最容易碰到的不是编译错误,而是 apt 或者下载慢得像卡死,通常是源的问题,先把系统软件源换到国内镜像再继续,能省下半小时。

编译完成后,安装目录里会出现 bin/bonmin,这就是最终的求解器可执行文件。如果你看到的是Bonmin大写开头或者在 build 目录里找不到,多半是 configure 阶段出了岔子,回头看 2.1 的依赖项,把报错信息里的库名逐个对照。

2.3 编译完怎么确认能用:三个低成本的验证命令

装完之后先别急着跑模型,花一分钟确认安装真的可用:

export PATH=/opt/coinor/bin:$PATH export LD_LIBRARY_PATH=/opt/coinor/lib:$LD_LIBRARY_PATH which bonmin bonmin -?

which bonmin确保 shell 能找到可执行文件。LD_LIBRARY_PATH指向安装前缀下的 lib 目录,否则运行时可能报error while loading shared libraries: libbonmin.so: cannot open shared object file。bonmin -?会打印一长串参数说明,只要它能正常输出而不是报动态库错误,就说明可执行文件、共享库和 ASL 驱动都加载成功了。

这两个环境变量建议写进~/.bashrc,因为后面用 Pyomo 或者直接命令行调用时,如果出现“找不到 bonmin”或者动态库加载失败,十次里有八次是这一步漏了。下一步用一个小算例把求解链路完整跑通,比任何安装日志都可信。

3. 用 Pyomo 建一个真正的 MINLP 模型:从建模语言到 Bonmin 求解的完整链路

3.1 为什么建模层选 Pyomo:.nl 中间格式与 AMPL 驱动

Bonmin 的输入格式核心是 AMPL 的 .nl 文件,它不直接读取 Python 写的模型。Pyomo 在这条链路里扮演的是建模层和翻译层:你用 Python 定义变量、目标、约束,Pyomo 把它们编译成 .nl 格式,再交给 bonmin 可执行文件去解。这样做的实际好处是,模型可以用循环、函数、外部数据源来生成,而不是靠手写 AMPL 文本。解完之后,Pyomo 还会把 .sol 结果映射回 Python 对象,直接就能读取每个变量的值。

很多人会问:那我直接用 AMPL 写行不行?当然可以,但 AMPL 是商业软件,光是要跑一个几十行的 MINLP 模型,还没到求解就卡在许可证上。Pyomo 免费、开源,而且对 Bonmin 的支持成熟,SolverFactory('bonmin')一行就能接上。另一个常见选择是 GAMS,但 GAMS 同样需要商业许可。对于工程验证和论文复现,Pyomo 是最快的一条路。

3.2 一个工艺选择算例:二元变量、连续变量与非线性约束怎么落笔

用一个制造场景当例子:两条候选工艺路线,每条可以选择投资(0-1 变量)或放弃,选定后连续处理量 x 决定产出,两条路线共享一份资源,同时处理量之间存在非线性耦合约束。这类模型在很多领域都有对应物:选设备、选通道、选机组,本质都是“离散选择 + 连续非线性寻优”。

import pyomo.environ as pyo model = pyo.ConcreteModel() # 0-1 决策:是否选择工艺路线 1 / 2 model.y1 = pyo.Var(within=pyo.Binary) model.y2 = pyo.Var(within=pyo.Binary) # 连续操作变量:每条路线的处理量 model.x1 = pyo.Var(within=pyo.NonNegativeReals, bounds=(0, 8)) model.x2 = pyo.Var(within=pyo.NonNegativeReals, bounds=(0, 6)) # 目标:收益最大化 model.profit = pyo.Objective( expr=5 * model.x1 * model.y1 + 4 * model.x2 * model.y2 - 2 * model.y1 - 3 * model.y2, sense=pyo.maximize ) model.choose_route = pyo.Constraint(expr=model.y1 + model.y2 >= 1) # 大 M 链接约束:不选路线时,处理量必须为 0 model.link1 = pyo.Constraint(expr=model.x1 <= 8 * model.y1) model.link2 = pyo.Constraint(expr=model.x2 <= 6 * model.y2) # 共享资源约束 model.resource = pyo.Constraint(expr=model.x1 + model.x2 <= 10) # 非线性约束:处理量之间的工艺耦合 model.tech = pyo.Constraint( expr=model.x1 * (model.x2 + 1) ** 0.5 + model.x2 <= 8 )

变量声明的关键在于within=pyo.Binary和within=pyo.NonNegativeReals,这决定了 Bonmin 的变量类型。目标里的5 * model.x1 * model.y1是双线性项,在 MINLP 里很常见,但要注意这种项会让模型变成非凸,外近似算法不一定可靠,后面调参数那章会专门讲。link1和link2是典型的“大 M”链接约束,作用是把离散变量和连续变量绑定:y 取 0 时 x 被强制压到 0,y 取 1 时 x 放开到容量上限。如果不加这两条,模型会出现“不建这条路线却仍然产生处理量”的荒谬解,这是 MINLP 建模里最常见的错误之一。

model.tech里有根号运算,Pyomo 会把这类表达式翻译成 .nl 里的非线性算子,Bonmin 再交给 Ipopt 处理。这里特意留了一个值得注意的点:这个非线性约束在 x2 接近 0 时仍然光滑,不会出现除零问题。如果你的模型里有x/y这种除法,y 取 0 时整个约束在数学上无定义,Bonmin 和 Ipopt 会给你很难看的失败日志,务必先用大 M 约束或边界把分母约束到正数区间。

3.3 调用 bonmin 求解并判定结果:终止状态与目标值怎么读

模型写完,接下来就是调用求解器。这一步代码很短,但参数和状态判断值得仔细看:

opt = pyo.SolverFactory('bonmin', executable='/opt/coinor/bin/bonmin') opt.options['bonmin.algorithm'] = 'B-OA' opt.options['bonmin.time_limit'] = 120 results = opt.solve(model, tee=True) print(results.solver.termination_condition) print(pyo.value(model.profit)) print(pyo.value(model.y1), pyo.value(model.y2)) print(pyo.value(model.x1), pyo.value(model.x2))

SolverFactory('bonmin')是 Pyomo 里对接 Bonmin 的入口,executable参数直接指定刚编译好的可执行文件路径,这一步能绕开很多 PATH 配置问题。opt.options里的键值对会作为求解器参数传给 bonmin,bonmin.algorithm选外近似算法,bonmin.time_limit限制 120 秒。tee=True会把求解日志打到屏幕上,第一次跑建议开着,能看到分支节点数、NLP 子问题迭代次数和当前目标值。

判断结果时,会有一个最容易踩的误区:termination_condition显示optimal才代表收敛。如果显示feasible,说明在时间或节点限制内只找到了可行解,还没证明最优;显示stopped或maxTimeLimit,说明是时间到了被截断。后半句的取值代码,pyo.value(model.profit)是求当前目标函数值,pyo.value(model.y1)是取变量解出的数值。我一般会把 termination_condition、目标值、每个整数变量的值一起打印出来,存进日志文件,后面调参时对比才方便。

这个算例如果跑通,理论上你会看到最优解落在“只选路线 1,处理量拉满到 8”的位置,目标值 38。如果结果不是这样,先回去检查有没有漏了大 M 约束,或者求解时间被限得太死。这个模型小到可以心算验证,非常适合当 Bonmin 的冒烟测试用例。

4. Bonmin 的参数调优:四种算法、容忍度与求解规模的取舍

4.1 四种算法怎么选:B-BB、B-OA、B-Hyb、QP-Diving 的适用场景

第一次跑通模型后,紧接着就是调参。Bonmin 提供的求解算法有四种,选错算法会直接体现为“死活解不动”或者“解出来是错的”。这四种算法的核心差异在怎么处理离散变量和非线性子问题之间的关系。

算法参数值搜索策略适合场景
B-BB纯分支定界,节点松弛直接交给 Ipopt非凸严重的 MINLP,外近似容易切错区域时
B-OA外近似,用线性切平面逼近非线性凸 MINLP,约束和目标都相对光滑
B-Hyb外近似与分支定界混合默认选择,凸与非凸混合模型的折中方案
QP-Diving用 QP 松弛引导深度优先 diving时间紧张,只求一个高质量可行解时

从实际使用者的角度看,我的经验是这样的:如果你的模型里非线性项都是凸的,比如凸二次函数、指数和、Log 约束,B-OA 收敛快,节点少,是首选。如果你的模型里不可避免地出现了双线性项、sin/cos、或者求商,B-OA 的外近似切平面可能把可行域切掉一块,收敛到假最优解,这时候换 B-BB 更稳。B-Hyb 作为默认值大多数情况下表现均衡,但遇到强非凸问题同样会卡。QP-Diving 不是用来证明最优的,而是用来快速拿到一个可行解当预案,比如调度场景里先要一个能执行的方案,再慢慢优化。

切换算法在 Pyomo 里就是改一行参数。可以先用 B-OA 跑一遍,再用 B-BB 跑一遍,对比目标值。如果两个目标值差异超过容忍度,基本可以断定模型存在非凸性,需要回去检查建模,或者接受 B-BB 的结果。

4.2 收敛判据与 Ipopt 子问题参数:日志里哪些行值得盯

Bonmin 的求解日志会把每一层的 NLP 子问题状态刷出来。初学者常犯的错误是看最后一行“Optimal”就当万事大吉,但其实中间过程里藏着大量信息。日志里反复出现EXIT: Restoration Phase Failed或Too many infeasible NLP iterates,说明子问题求解器在收敛阶段翻了车。

这里要理解 Bonmin 的双层结构:外层是离散搜索,内层是 Ipopt 解 NLP。Ipopt 是连续非线性优化器,它需要目标函数和约束的一阶导数;当模型数值条件差(比如量级差十几个数量级)或者初值偏到奇异点,Ipopt 可能连一个可行子问题都解不出来。这时先别急着喊 Bonmin 不行,要看看是不是 Ipopt 收敛条件设置太苛刻。

两类参数值得调:一类是整数层面的判定容差,常见做法是设bonmin.integer_tolerance=1e-6,它控制变量值离整数多近时判定为整数,默认通常已经够用,但如果你的模型数值量级很大,可能需要放宽。另一类是 Ipopt 自身的收敛容差,通过ipopt.tol传入:

opt.options['bonmin.integer_tolerance'] = 1e-6 opt.options['ipopt.tol'] = 1e-7 opt.options['ipopt.max_iter'] = 500

ipopt.tol是 NLP 子问题的 KKT 收敛容忍度,默认在 1e-8 量级,如果你的模型本身精度要求不高,放到 1e-6 会让整个求解快很多,我经常对大型调度模型这么做。ipopt.max_iter限制子问题最大迭代次数,防止单个 NLP 子问题卡住不返回。这些参数的具体名称可以在命令行执行bonmin -?查看帮助输出,不同版本命名会略有差异,但前缀规则是一致的:带bonmin.的归外层,带ipopt.的透传给子求解器。

4.3 时间限制、可行解数量与容量扩展:把参数写进求解配置

工程场景里,很少有模型能无限时求解。Bonmin 支持直接限制求解时间和输出解的数量,这两个参数搭配起来很实用:

opt.options['bonmin.time_limit'] = 300 opt.options['bonmin.solution_limit'] = 5

time_limit的单位是秒,300 表示最多跑五分钟。solution_limit的意思是找到 5 个可行解就提前停止。这在工业场景里很有用:不追求全局最优的情况下,先拿 5 个可行解做对比,选一个工程上最容易实现的方案,比傻等最优解划算得多。

还有一组容易被忽略的容量参数是输出等级。调试时可以把日志打到最详细,看每个节点在做什么;生产环境则要压到只输出警告,否则几十万节点的日志文件能把你磁盘塞满。常见做法是设bonmin.print_level=2,数字越大输出越详细,默认值一般在中间档位。我的习惯是调试用详细日志,正式批量求解只保留 termination_condition 和目标值。

关于“什么情况别用 Bonmin”也值得在这里说清楚:如果模型里没有整数变量,直接用 Ipopt 就够了,Bonmin 的外层框架是纯开销;如果模型可以线性化,优先线性化后用 Cbc 或 Cplex;如果你的 MINLP 是二次约束二次目标(QCQP),先评估是否能用 Gurobi 或 Cplex 的二次求解能力。Bonmin 的适用范围是那些确实处理不了离散与非线性叠加的问题,用在对的地方,性价比很高。

5. Bonmin 安装与求解的五个常见坑:从编译失败到求解卡死的排查记录

5.1 坑一:configure 报找不到 ASL 或 ThirdParty 目录为空

现象:执行./configure,中途报错,关键词是Cannot find ASL或者Cannot find CoinUtils,看起来像系统缺库。

原因:源码包里的 ThirdParty 是 git submodule,直接 Download ZIP 下载时子模块目录是空的。configure 检查源码树里的 ASL、Blas、Mumps 时找不到,于是中止。

解决:如果已经用 git clone 拉过仓库但没有加--recursive,执行git submodule update --init --recursive补拉子模块,然后重新 configure。如果是从 ZIP 解压的,最简单的做法是删掉重来,用git clone --recursive https://github.com/coin-or/Bonmin.git重新拉一份。这个坑之所以高频,是因为 GitHub 页面默认提供的 ZIP 包太有迷惑性,让人以为所有文件都在里面。

5.2 坑二:运行时报找不到共享库 libbonmin.so

现象:编译和安装都成功了,which bonmin也能找到可执行文件,但一运行就报error while loading shared libraries: libbonmin.so: cannot open shared object file。

原因:Bonmin 安装到了非系统目录,比如 /opt/coinor/lib,而 Linux 的动态库加载器默认不搜索这个路径。

解决:在运行前导出LD_LIBRARY_PATH=/opt/coinor/lib:$LD_LIBRARY_PATH,并写入~/.bashrc。如果用了ldconfig方式,需要把 /opt/coinor/lib 写进 /etc/ld.so.conf.d/ 下的配置文件再执行ldconfig。对单用户使用场景,export 最简单可靠;对服务器部署,建议用 ldconfig 方式,避免每次开新 shell 都要重新设变量。

5.3 坑三:Pyomo 说找不到 bonmin 可执行文件

现象:Python 代码里SolverFactory('bonmin')直接抛出错误,提示找不到可执行文件,或者返回 code 127。

原因:Pyomo 默认在 PATH 里搜索 bonmin,但你的安装目录还没加进 PATH。Python 进程和 shell 不一样,不会自动读你~/.bashrc里 add 到 PATH 的那个目录。

解决:第一种做法是显式指定路径:SolverFactory('bonmin', executable='/opt/coinor/bin/bonmin'),一劳永逸,代码可复用性也好。第二种做法是确认当前 shell 的 PATH 真的包含了 Bonmin 的 bin 目录,执行echo $PATH验证。我个人的习惯是选择第一种,因为脚本换机器时只需要改一处路径配置,不依赖系统环境。

5.4 坑四:求解长时间卡死,节点数不增长

现象:跑大型模型时,日志里节点数长时间停在同一个数字,或者反复出现 NLP 子问题失败的提示,CPU 占用却很高。

原因:这是 MINLP 求解最典型的症状。外层分支定界每走一步都要解一个 NLP 子问题,如果子问题在指定初始点周围找不到可行解,节点就不会继续展开。常见的背后原因是非凸约束让外近似切错区域,或者连续变量的边界太宽,Ipopt 在数值上迷失方向。

解决:第一步换算法,把bonmin.algorithm设成 B-BB,绕过外近似切平面。第二步给连续变量收窄边界,比如原来写bounds=(0, 1000),根据物理含义缩到bounds=(0, 100),数值范围减小对 Ipopt 的收敛帮助极大。第三步给变量设置初始点,model.x1.set_value(50)这类赋值写在 solve 之前,让子问题从合理位置出发。这三板斧能解决大部分卡死问题。如果还不收敛,回到建模层面看约束是否真的可行。

5.5 坑五:Ipopt 报 MUMPS is not available 或线性求解器初始化失败

现象:Bonmin 运行时日志里出现MUMPS is not available,或者明确提示Linear solver失败,求解直接中止。

原因:Ipopt 求解 NLP 子问题需要线性代数求解器,MUMPS 是默认依赖,而它属于 Ipopt 的 ThirdParty 组件。如果你的 Ipopt 是手动单独编译的,编译时没拉 MUMPS 就会缺这个能力。

解决:如果 Ipopt 是用 coinbrew 跟着 Bonmin 一起拉起来编译的,通常不会有这个问题,因为 coinbrew 默认会编译 MUMPS。如果是手动编译 Ipopt,回到 Ipopt 源码目录,执行./coinbrew build Ipopt --prefix=/opt/ipopt,它会自动补充 MUMPS 并重新编译。做完之后把 Bonmin 重新 configure 一遍,指向新的 Ipopt 前缀。另外注意,如果机器上装了多个版本的 Ipopt,用ldd /opt/coinor/bin/bonmin检查它实际链接的是哪个 libipopt,避免链接到旧版本。

6. 用固定整数再求解验证 Bonmin 结果:一个成本最低的最优性检验技巧

6.1 为什么需要二次求解验证

Bonmin 给出optimal不代表结果可以闭眼信任,尤其是非凸问题。整数变量被分支定界确定为某个组合后,外近似算法也可能因为切平面偏差锁定在次优区域。与其依赖求解器一句话,不如自己做一个低成本交叉验证:把整数变量固定为已求出的解,整个模型退化为一个连续 NLP,再交给 Ipopt 精确求解一次。如果目标值和原来的 MINLP 结果基本一致,说明这个整数组合在连续空间里是稳定的;如果差异很大,说明最初搜索时某个环节出了问题。

6.2 脚本实现:读解、固定整数、二次求解

在第三章模型的基础上继续操作,核心代码只有十几行:

# 保存原始 MINLP 解 obj_minlp = pyo.value(model.profit) sol_y1 = int(round(pyo.value(model.y1))) sol_y2 = int(round(pyo.value(model.y2))) # 固定整数变量 model.y1.fix(sol_y1) model.y2.fix(sol_y2) # 退化为 NLP 后重新求解 opt2 = pyo.SolverFactory('bonmin', executable='/opt/coinor/bin/bonmin') obj_fixed = opt2.solve(model, tee=False) obj_fixed_nlp = pyo.value(model.profit) print(f"MINLP 解目标值: {obj_minlp:.6f}") print(f"固定整数后 NLP 目标值: {obj_fixed_nlp:.6f}") if abs(obj_fixed_nlp - obj_minlp) > 1e-3: print("差异过大,怀疑原解未收敛;建议改用 B-BB 并收窄边界重解。")

fix是 Pyomo 里把变量固定在当前值的标准方法,固定之后求解器不再对整数变量做分支决策,Bonmin 会直接把问题压缩成连续 NLP 交给 Ipopt。abs判断的阈值我习惯取 1e-3,但实际取决于模型数值量级:如果你的目标函数值本身只有 0.0001 量级,阈值要同步缩小;如果目标值以百万计,1e-3 的绝对误差可能反而过于苛刻,改用相对误差更合理。

6.3 记录求解日志与参数复现的习惯

做完验证,还有一件事值得养成习惯:把每一轮求解的算法选项、时间限制、termination_condition、两个目标值同步记录成文件。我见过太多“上次明明解出 42.7,重跑变成 42.6”的对账事故,最后发现是参数没记全,某个 tolerances 或初始点不一样。技术上这不算难题,但很耗人。

我的做法是在每次求解前把opt.options的字典全部打印出来存档,求解结束后把目标值、整数变量解、终止状态追加到同一行。回头对比时,先看参数差在哪,再看目标值差在哪。这套习惯帮我省了不少排查时间。如果你打算把 Bonmin 用在正经项目里,建议从第一天就建立这个记录习惯。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询