You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何绘制CPLEX代码的效率及最优解收敛曲线?

如何绘制CPLEX代码的效率及最优解收敛曲线?

嘿,这个需求太常见啦,很多做优化的伙伴都会需要用曲线直观展示CPLEX求解的收敛情况,我给你拆解成两步来做:先从CPLEX里捞到关键的收敛/效率数据,再用绘图工具把它可视化出来,下面给你具体说细节~

第一步:从CPLEX中获取收敛/效率数据

CPLEX提供了两种主要方式来拿这些数据,各有优劣,你可以按需选择:

方式1:用API自定义回调(精度高、灵活)

如果你用的是Python的docplex库(最方便的CPLEX Python接口),可以写一个自定义回调函数,在求解过程中实时抓取每一步的关键数据——比如当前时间、最优解上界(已经找到的最好可行解目标值)、松弛解下界(理论上的最优解下限)、求解间隙,甚至节点数、迭代次数这些效率指标。

给你贴个现成的示例代码,你可以直接套到自己的模型里:

from docplex.mp.callbacks import CplexCallback
import pandas as pd
import matplotlib.pyplot as plt

# 自定义回调类,专门记录收敛数据
class ConvergenceTracker(CplexCallback):
    def __init__(self):
        CplexCallback.__init__(self)
        self.convergence_data = []
        self.start_timestamp = self.get_time()  # 记录求解开始时间

    def __call__(self):
        # 计算当前耗时(秒)
        elapsed_time = self.get_time() - self.start_timestamp
        # 获取松弛解的最好下界
        lower_bound = self.get_best_objective_bound()
        # 获取当前找到的最好可行解(上界),如果还没找到可行解就设为None
        upper_bound = self.get_objective_value() if self.has_incumbent() else None

        # 只在有有效上界的时候记录(避免无效数据)
        if upper_bound is not None:
            # 计算求解间隙(百分比)
            optimality_gap = (upper_bound - lower_bound) / abs(upper_bound) * 100 if upper_bound != 0 else 0
            self.convergence_data.append({
                "耗时(秒)": elapsed_time,
                "下界": lower_bound,
                "上界": upper_bound,
                "求解间隙(%)": optimality_gap,
                "已探索节点数": self.get_num_nodes()
            })

# --- 把下面的部分换成你自己的模型代码 ---
# 假设你已经定义好了你的CPLEX模型model
# model = Model(name="your_model")
# ... 你的变量、约束、目标函数定义 ...

# 注册回调函数
tracker = ConvergenceTracker()
model.register_callback(tracker)

# 开始求解
model.solve()

# 把记录的数据转成DataFrame,方便后续处理
convergence_df = pd.DataFrame(tracker.convergence_data)

如果用的是C++/Java的CPLEX API,思路也是一样的:继承对应的回调类(比如C++里的IloCplex::MIPInfoCallback),在回调函数里调用CPLEX的接口获取数据,然后写到数组或者文件里就行。

方式2:解析CPLEX求解日志(快速上手,无需改代码)

如果你不想改模型代码,可以让CPLEX把求解日志输出到文件,然后手动解析日志里的关键数据。

比如在Python里可以这样让CPLEX输出日志到文件:

model.parameters.logfilename.set("cplex_solve_log.txt")
model.solve()

日志里会有类似这样的行:

Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 | - 0 0 | 1000.0000 500.0000 50.0% - 0s
5 12 | - 3 2 | 900.0000 600.0000 33.3% 2 1s

你可以用正则表达式或者字符串匹配,把每一行里的Incumbent(上界)、BestBd(下界)、Time(耗时)、Expl(已探索节点数)这些数据提取出来,整理成表格格式。

第二步:绘制收敛/效率曲线

拿到数据之后,用matplotlib或者seaborn就能轻松画图了,给你几个常用的可视化方向:

1. 最优解上下界收敛曲线(核心!直观展示逼近最优解的过程)

这个曲线能直接看到上界(可行解)和下界(理论下限)怎么逐渐靠拢,直到收敛到最优解:

plt.figure(figsize=(10, 6))
plt.plot(convergence_df["耗时(秒)"], convergence_df["下界"], label="松弛解下界", color="#1f77b4", linewidth=2)
plt.plot(convergence_df["耗时(秒)"], convergence_df["上界"], label="最优可行解上界", color="#ff7f0e", linewidth=2)
# 填充上下界之间的区域,更直观
plt.fill_between(convergence_df["耗时(秒)"], convergence_df["下界"], convergence_df["上界"], alpha=0.2, color="gray")
plt.xlabel("求解耗时(秒)")
plt.ylabel("目标函数值")
plt.title("最优解收敛曲线")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

2. 求解间隙随时间变化曲线

求解间隙是判断收敛速度的关键指标,能看到间隙怎么从大到小降到0(或者你设置的停止阈值):

plt.figure(figsize=(10, 6))
plt.plot(convergence_df["耗时(秒)"], convergence_df["求解间隙(%)"], label="最优性间隙", color="#2ca02c", linewidth=2)
plt.xlabel("求解耗时(秒)")
plt.ylabel("最优性间隙(%)")
plt.title("求解间隙收敛曲线")
plt.legend()
plt.grid(alpha=0.3)
plt.yscale("log")  # 用对数刻度更适合看间隙快速下降的过程
plt.show()

3. 效率相关曲线(展示求解速度)

如果想展示代码的求解效率,可以画已探索节点数随时间的变化,或者迭代次数随时间的变化:

plt.figure(figsize=(10, 6))
plt.plot(convergence_df["耗时(秒)"], convergence_df["已探索节点数"], label="已探索节点数", color="#d62728", linewidth=2)
plt.xlabel("求解耗时(秒)")
plt.ylabel("已探索节点数")
plt.title("节点探索效率曲线")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

一些小Tips

  • 如果求解时间很长,可以设置回调函数每隔N秒或者N次迭代才记录一次数据,避免数据量太大,也不会影响求解速度;
  • 对于连续线性规划(LP),收敛曲线看的是对偶间隙的变化,和MIP的上下界逻辑类似;
  • 如果需要更美观的图,可以用seaborn替换matplotlib,或者导出数据到Excel/Tableau做交互式可视化。

备注:内容来源于stack exchange,提问作者MO MO MA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 08:39:50