Skip to main content

Working with the optimization log

Answered

Comments

5 comments

  • Eli Towle
    Gurobi Staff Gurobi Staff

    Hi Felix,

    You could do this with a callback. For example, this callback creates a list of times, objective values, and objective bounds, then writes the data to a CSV file:

    import gurobipy as gp
    import csv
    import time


    def data_cb(model, where):
        if where == gp.GRB.Callback.MIP:
            cur_obj = model.cbGet(gp.GRB.Callback.MIP_OBJBST)
            cur_bd = model.cbGet(gp.GRB.Callback.MIP_OBJBND)

    # Did objective value or best bound change?
            if model._obj != cur_obj or model._bd != cur_bd:
                model._obj = cur_obj
                model._bd = cur_bd
                model._data.append([time.time() - m._start, cur_obj, cur_bd])

    # Build model m here

    m._obj = None
    m._bd = None
    m._data = []
    m._start = time.time()
    m.optimize(callback=data_cb)

    with open('data.csv', 'w') as f:
        writer = csv.writer(f)
        writer.writerows(m._data)

    Does this help?

    Eli

    2
  • Felix Graßl
    Gurobi-versary
    First Comment
    First Question

    Thank you, yes, this allowed me to do exactly what I had in mind. Special thanks for the working code!

    0
  • Phillip Meng
    Gurobi-versary
    Conversationalist
    First Question

    Following this one up, would this be the right kind of approach for getting the log information to integrate with our python logger (logging package). I'm thinking we create a callback that if gurobi logs something, we collect that information and parse into our logging file. Any advice on this is welcome!

    1
  • Shadi Beheshti
    Gurobi-versary
    First Comment
    First Question

    Good day,

    I would like to save the Gaps (16.1, 12.5, 2.86)that I get in the output log:

      Nodes    |    Current Node    |     Objective Bounds      |     Work
     Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    
         0     0   36.00000    0   11   31.00000   36.00000  16.1%     -    0s
    H    0     0                      32.0000000   36.00000  12.5%     -    0s
    H    0     0                      35.0000000   36.00000  2.86%     -    0s

    I used
    def data_cb(model, where):
        if where == gp.GRB.Callback.MIP:
            cur_obj = model.cbGet(gp.GRB.Callback.MIP_OBJBST)
            cur_bd = model.cbGet(gp.GRB.Callback.MIP_OBJBND)
            model._data.append([cur_obj, cur_bd])

    I returns
    31.0,50.0
    31.0,50.0
    31.0,36.0
    while the correct MIP_OBJBST (Incumbent in the outputlog) are [31,32,35]
    and the correct MIP_OBJBND (BestBd in in the outputlog) are [36,36,36].
    Can anyone help with this?
    0
  • Riley Clement
    Gurobi Staff Gurobi Staff

    Hi Shadi,

    One approach you could use is via the MESSAGE callback to get each line that is logged and extract the gap from that using regex.

    import re
    import gurobipy as gp

    m = # define model 

    def mycallback(model, where):
        if where == gp.GRB.Callback.MESSAGE:
            # Message callback
            msg = model.cbGet(gp.GRB.Callback.MSG_STRING)
            match = re.findall("(\d+\.\d+%).*s", msg)
            gap = None if not match else match[0]
            if gap:
                print("gap", gap)
                # do something with your gap string, eg strip % and convert to float

    m.optimize(mycallback)

    Note that there may be changes in the gap which are not logged.

    In your attempt, the gap is changing due to the incumbent improving, so you may be able to include the MIPSOL callback to make it work the way you would like.

    - Riley

    0

Please sign in to leave a comment.