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

Python环境下如何将Gurobi分支切割树日志转为CSV格式?

Gurobi日志转CSV的实现方法(Python)

一、Gurobi是否支持直接生成CSV格式日志?

Gurobi没有内置功能直接输出CSV格式的分支-切割树搜索日志,默认仅支持生成纯文本(TXT)格式日志文件。

二、Python下转CSV的简便实现方法

由于没有内置支持,需通过读取日志或实时收集数据的方式转换为CSV,以下是两种实用方案:

方法1:使用回调函数实时收集数据(推荐)

求解过程中直接捕获分支树关键数据,无需事后解析日志,准确性和灵活性更高。

import gurobipy as gp
from gurobipy import GRB
import csv

# 初始化模型(替换为你的模型定义)
model = gp.Model("mip_model")
# ... 添加变量、约束、目标函数 ...

# 存储日志数据的列表与表头
log_data = []
headers = [
    "Expl", "Unexpl", "Obj", "Depth", "IntInf",
    "Incumbent", "BestBd", "Gap", "It/Node", "Time", "Flag"
]

def mip_callback(model, where):
    if where == GRB.Callback.MIP:
        # 提取当前节点核心数据
        expl = model.cbGet(GRB.Callback.MIP_NODES)
        unexpl = model.cbGet(GRB.Callback.MIP_OPENNODES)
        obj = model.cbGet(GRB.Callback.MIP_OBJBND)
        depth = model.cbGet(GRB.Callback.MIP_NODEDEPTH)
        intinf = model.cbGet(GRB.Callback.MIP_INTCOUNT)
        incumbent = model.cbGet(GRB.Callback.MIP_OBJBST)
        bestbd = model.cbGet(GRB.Callback.MIP_OBJBND)
        gap = model.cbGet(GRB.Callback.MIP_GAP) if incumbent != GRB.INFINITY else ""
        it_per_node = model.cbGet(GRB.Callback.MIP_ITRCOUNT) / (expl + 1) if expl > 0 else ""
        time = round(model.cbGet(GRB.Callback.RUNTIME), 1)
        flag = ""

        # 标记可行解(*)
        if model.cbGet(GRB.Callback.MIP_SOLCNT) > model._last_sol:
            model._last_sol = model.cbGet(GRB.Callback.MIP_SOLCNT)
            flag = "*"

        # 整理行数据,处理无穷值与空值
        row = [
            expl, unexpl, obj, depth, intinf,
            incumbent if incumbent != GRB.INFINITY else "",
            bestbd, gap, it_per_node, time, flag
        ]
        log_data.append(row)

# 初始化可行解计数器
model._last_sol = 0

# 绑定回调并求解
model.optimize(mip_callback)

# 写入CSV文件
with open("gurobi_mip_log.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(headers)
    writer.writerows(log_data)

方法2:事后解析TXT日志文件

针对已生成的TXT日志,用正则表达式提取数据,适合回溯分析。

import re
import csv

log_path = "gurobi.log"
headers = [
    "Flag", "Expl", "Unexpl", "Obj", "Depth", "IntInf",
    "Incumbent", "BestBd", "Gap", "It/Node", "Time"
]
log_data = []

# 匹配日志行的正则(适配Gurobi标准分支树日志格式)
line_pattern = re.compile(
    r'^([H*]?)\s*(\d+)\s*(\d+)\s*([\d.-]*)\s*(\d*)\s*(\d*)\s*([\d.-]*)\s*([\d.-]*)\s*([\d.%]*)\s*([\d.]*)\s*(\d+s)$'
)

with open(log_path, "r") as f:
    # 跳过前两行表头
    for line in f.readlines()[2:]:
        line = line.strip()
        if not line:
            continue
        match = line_pattern.match(line)
        if match:
            row = [val if val else "" for val in match.groups()]
            log_data.append(row)

# 写入CSV
with open("gurobi_mip_log_parsed.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(headers)
    writer.writerows(log_data)

注意事项

  • 回调函数方案能避免日志格式变动带来的解析问题,更稳定
  • 正则表达式需根据你的Gurobi版本微调,确保匹配所有日志行
  • CSV中的Gap列含百分比符号,可在写入前通过字符串处理去除,方便后续数据分析

内容的提问来源于stack exchange,提问作者mjsl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:07:02