MIPGAP Graph
回答済みHello,
So recently I read this article "What is MIPGap?", which I found very helpful. Specially the graph that is given to elaborate on the relation between gap percentage and objective values. I wanted to ask, if anyone here knows how I can extract the values using callbacks to plot a similar graph while optimizing live, if I am using gurobipy.
I will be grateful for your help. Thank you.
-
Hi Mahnoor,
Usually we'd recommend using gurobi-logtools for plotting this sort of information but the requirement that the graph is updated live means it is probably easier to not use gurobi-logtools.
The following script works for me, when run from the command line (not a Jupyter notebook):
import gurobipy as gp
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
class GapPlotCallback:
def __init__(self):
_, self.ax = plt.subplots()
self.line, = self.ax.plot([],[])
self.ax.set_xlabel("Time (seconds)")
self.ax.set_ylabel("MIP Gap")
self.ax.set_title("MIP Gap vs Time")
self.ax.yaxis.set_major_formatter(PercentFormatter(1))
self.ax.legend()
self.time = []
self.gap = []
def __call__(self, model, where):
if where == gp.GRB.Callback.MIP:
runtime = model.cbGet(gp.GRB.Callback.RUNTIME)
objbst = model.cbGet(gp.GRB.Callback.MIP_OBJBST)
objbnd = model.cbGet(gp.GRB.Callback.MIP_OBJBND)
gap = abs(objbst - objbnd) / abs(objbst)
self.time.append(runtime)
self.gap.append(gap)
elif where == gp.GRB.Callback.MESSAGE:
self.line.set_data(self.time, self.gap)
self.ax.relim()
self.ax.autoscale_view()
plt.draw()
plt.pause(0.01)
m = gp.read("/path/to/a/model.mps")
cb = GapPlotCallback()
m.optimize(cb)You could adapt it for your own purposes. Note though that it will slow down the optimization - my rough estimate is by 10%. If you wanted to not impact the run time of the optimization then maybe running a gurobi-logtools approach in a separate thread would be the best approach.
- Riley
2 -
Dear Riley,
Thank you so much for your prompt response and guidance regarding the issue.
Previously I have been logging the data directly to a CSV file, during live optimization and then use a separate code to plot it.
The code I use for logging is as follows:-import os
import gurobipy as gp
from gurobipy import GRB
import csv
import time
def mycallback(model, where):
if where == GRB.Callback.MIP:
current_time = time.time() - model._start_time
objbnd = float(model.cbGet(GRB.Callback.MIP_OBJBND))
objbst = float(model.cbGet(GRB.Callback.MIP_OBJBST))
writer.writerow([f"{current_time:.6f}", f"{objbst:.6f}", f"{objbnd:.6f}"])
file.flush()
if model._bests != objbst or model._bounds != objbnd:
model._bests = objbst
model._bounds = objbnd
model._data.append([current_time, objbst, objbnd])
csv_file_path = '/path/to/a/model.csv'
if not os.path.exists(csv_file_path):
with open(csv_file_path, mode='w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Time (s)', 'Upper Bound (Obj Bst)', 'Lower Bound (Obj Bnd)'])
lp_file_path = '/path/to/a/model.lp'
model = gp.read(lp_file_path)
# Initialize attributes to store data
model._bounds = None
model._bests = None
model._data = []
model._start_time = time.time()
with open(csv_file_path, mode='a', newline='') as file:
writer = csv.writer(file)
model.optimize(callback=mycallback)
best_obj = model.objVal
best_bound = model.objBound
# Write the final best objective and best bound after optimization
runtime = model.Runtime
writer.writerow([f"{runtime:.6f}", f"{best_obj:.6f}", f"{best_bound:.6f}"])Here, since you mentioned about the direct graphing code slowing down the optimization by 10%, do you think the csv logging method I am already using may also slow it down?
Thanks again.
-Mahnoor
0 -
Hi Mahnoor,
Yes you would be seeing a slow down, as the solve is paused while the callback executes. This callback is going to be called many times, so a small amount of time in the callback can translate to a large time in total.
At the end of the log file you should see a line like this:
User-callback calls ..., time in user-callback ... sec
This will tell you exactly how much time was spent in the callback. If there is a lot of time being used then I think it would be better to just use gurobi-logtools and not worry about callbacks or calculating the gap yourself. You can loop through code using gurobi-logtools and updating a graph and it could be run completely separately to the optimization. You may wish to set DisplayInterval=1 so you get more regular logging.
- Riley
0
サインインしてコメントを残してください。
コメント
3件のコメント