Callbacks#

A callback is a user function that is called periodically by the Gurobi Optimizer in order to allow the user to query or modify the state of the optimization. More precisely, if you pass a function that takes two arguments (model and where) as the argument to Model.optimize or Model.computeIIS, your function will be called during the optimization. Your callback function can then call Model.cbGet to query the optimizer for details on the state of the optimization.

Gurobi callbacks can be used both to monitor the progress of the optimization and to modify the behavior of the Gurobi Optimizer. A simple user callback function might call Model.cbGet to produce a custom display, or perhaps to terminate optimization early (using Model.terminate) or to proceed to the next phase of the computation (using Model.cbProceed). More sophisticated MIP callbacks might use Model.cbGetNodeRel or Model.cbGetSolution to retrieve values from the solution to the current node, and then use Model.cbCut or Model.cbLazy to add a constraint to cut off that solution, or Model.cbSetSolution to import a heuristic solution built from that solution. For multi-objective problems, you might use Model.cbStopOneMultiObj to interrupt the optimization process of one of the optimization steps in a multi-objective MIP problem without stopping the hierarchical optimization process.

GRB.Callback provides a set of constants that are used within the user callback function. The first set of constants provide the options for the where argument to the user callback function. The where argument indicates from where in the optimization process the user callback is being called. Options are listed in the Callback Codes section of this document.

The other set of constants provide the options for the what argument to Model.cbGet. The what argument is used by the user callback to indicate what piece of status information it would like to retrieve. The full list of options can be found in the Callback Codes section. As with the where argument, you refer to a what constant through GRB.Callback. For example, the simplex objective value would be requested using GRB.Callback.SPX_OBJVAL.

When solving a model using multiple threads, the user callback is only ever called from a single thread, so you don’t need to worry about the thread-safety of your callback. However, if the solve was started asynchronously using Model.optimizeAsync then the callback is called from the background thread running the optimization. You must make sure that your callback code and foreground code do not interact in a thread-unsafe manner.

A few parameters are callback settable. These parameters can be modified from within a callback invocation by using the Model.cbSetParam method.

The following sections provide brief examples of usage of Python functions and class instances as callbacks. You can also look at callback.py and tsp.py for details of how to use Gurobi callbacks from Python.

Python Functions as Callbacks#

The simplest form of a callback is a Python function that takes the model and where arguments. This allows you to access the callback functions mentioned in the previous section via the model argument:

def callback(model, where):
    if where == GRB.Callback.MIP:
        best_objective = model.cbGet(GRB.Callback.MIP_OBJBST)
        ...

model.optimize(callback)

Python functions capture variable names from their enclosing scope. This means that if you define your callback function in a scope with access to your model variables, you can use them within your callback:

model = gp.Model(env=env)

x = model.addVars(5)
...

def callback(model, where):
    if where == GRB.Callback.MIPSOL:
        solution_values = model.cbGetSolution(x)
        ...

model.optimize(callback)

This approach fails if you reassign variables from the enclosing scope or otherwise attempt to use them to store data between callback invocations. See Why am I getting an UnboundLocalError when the variable has a value? in the Python documentation for details. If you need to maintain state between callback calls, you should pass an instance of a callable class to Model.optimize as described in the next section.

Python Classes as Callbacks#

If your program needs to maintain state data between callback calls, you can use an instance of a callable class to store this state. A class is made callable by implementing a __call__ method. The __call__ method must take the model and where arguments.

For example, the following class would track improvements in the objective with each new solution found during a MIP solve:

class SolutionCallback:

    def __init__(self):
        self.previous_best_objective = None

    def __call__(self, model, where):
        if where == GRB.Callback.MIPSOL:
            # Query the objective value of the new solution
            best_objective = model.cbGet(GRB.Callback.MIPSOL_OBJ)

            # Check against stored state and report improvement in the
            # (minimization) objective
            if self.previous_best_objective is None:
                print(f"First solution found with objective: {best_objective}")
            else:
                improvement = self.previous_best_objective - best_objective
                print(f"New solution found; improved by {improvement}")

            # Store the current objective for the next call
            self.previous_best_objective = best_objective


# Use a new instance of SolutionCallback as the callback
callback = SolutionCallback()
model.optimize(callback)