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)