如何通过CPO Optimizer Python API保存日志与中间解以生成优化演化图表?
解决CPO Optimizer Python API记录中间解与时间戳的方案
当然有办法,下面是两种实用的方案,都能帮你记录中间解和时间戳,进而构建优化过程演化图表:
方案一:自定义回调函数(推荐,灵活可控)
CPO Optimizer的Python API支持通过自定义回调函数,在每次找到新的可行解或改进解时触发逻辑,直接记录时间戳、目标值甚至完整变量解。
实现步骤:
- 继承
CpoCallback类,重写notify_solution方法,在该方法中捕获解信息与时间; - 实例化自定义回调类,在求解时传入
callbacks参数; - 求解完成后,从回调对象中提取数据,保存为文件或直接用于绘图。
代码示例:
from docplex.cp.model import CpoModel from docplex.cp.callback import CpoCallback import time import csv import matplotlib.pyplot as plt # 自定义回调类,用于记录中间解 class SolutionLogger(CpoCallback): def __init__(self): super().__init__() self.solution_history = [] # 存储格式:(耗时秒数, 目标值, 变量值字典) def notify_solution(self, solution): # 获取优化启动以来的耗时 elapsed_time = self.get_cpo_info().get_elapsed_time() # 获取当前解的目标值 objective_value = solution.get_objective_value() # 提取所有变量的取值 var_values = {var.get_name(): solution.get_value(var) for var in self.model.get_all_variables()} # 存入历史记录 self.solution_history.append((elapsed_time, objective_value, var_values)) # 构建你的CPO模型(这里以简单示例为例) model = CpoModel() x = model.integer_var(0, 20, "x") y = model.integer_var(0, 20, "y") model.add(x + y >= 10) model.minimize(3*x + 2*y) # 实例化回调并求解 solution_logger = SolutionLogger() solve_result = model.solve(agent='local', callbacks=[solution_logger]) # 将记录保存为CSV文件 with open('cpo_optimization_history.csv', 'w', newline='') as csv_file: writer = csv.writer(csv_file) # 写入表头 writer.writerow(["Elapsed Time (s)", "Objective Value", "x", "y"]) # 写入每一条记录 for t, obj, vars in solution_logger.solution_history: writer.writerow([round(t, 2), obj, vars["x"], vars["y"]]) # 绘制优化过程演化图 times = [entry[0] for entry in solution_logger.solution_history] objectives = [entry[1] for entry in solution_logger.solution_history] plt.figure(figsize=(10, 6)) plt.plot(times, objectives, marker='o', linestyle='-', color='b') plt.xlabel('Elapsed Time (s)') plt.ylabel('Objective Value (Minimization)') plt.title('CPO Optimizer Progress') plt.grid(True) plt.show()
方案二:解析CPO日志文件(轻量快速)
如果只需要记录目标值和时间戳,可以直接将CPO的求解日志定向到文件,再通过正则表达式解析日志中的关键信息。
实现步骤:
- 求解时将
log_output参数设置为文件对象,把日志写入文件; - 读取日志文件,用正则匹配提取中间解的时间和目标值;
- 用提取的数据绘图。
代码示例:
from docplex.cp.model import CpoModel import re import matplotlib.pyplot as plt # 构建模型(同方案一示例) model = CpoModel() x = model.integer_var(0, 20, "x") y = model.integer_var(0, 20, "y") model.add(x + y >= 10) model.minimize(3*x + 2*y) # 将日志写入文件 with open('cpo_solve_log.txt', 'w') as log_file: solve_result = model.solve(agent='local', log_output=log_file) # 解析日志文件 log_entries = [] pattern = re.compile(r'! New solution, objective (\d+\.?\d*) \(time (\d+\.?\d*) sec') with open('cpo_solve_log.txt', 'r') as f: for line in f: match = pattern.search(line) if match: obj_val = float(match.group(1)) elapsed_time = float(match.group(2)) log_entries.append((elapsed_time, obj_val)) # 绘制演化图 plt.figure(figsize=(10, 6)) plt.plot([t for t, obj in log_entries], [obj for t, obj in log_entries], marker='x', linestyle='--', color='r') plt.xlabel('Elapsed Time (s)') plt.ylabel('Objective Value (Minimization)') plt.title('CPO Optimizer Progress from Log') plt.grid(True) plt.show()
注意事项:
- 回调方案可以获取完整的变量解,适合需要分析变量取值变化的场景;
- 日志解析方案更轻量,但只能提取日志中显示的信息(默认日志会输出目标值和时间,变量细节需要开启更详细的日志级别);
- 如果需要绝对时间戳(而非优化耗时),可以在回调的
notify_solution方法中用time.time()替代get_elapsed_time()。
内容的提问来源于stack exchange,提问作者Javi Pernas
相关产品推荐
相关产品推荐

