6.2. Callback¶
MindOpt adopts the classical branch and bound method to solve MIP problems. MindOpt provides users with callback functions to track and modify the optimization process of MIP problems. Users can use callbacks to modify the behavior of the MIP optimizer, including:
Add cutting planes to cut off branches that will not lead to the optimal solutions.
Modify the branch selection strategy of MindOpt to control node branching methods and traversal order.
Add feasible solutions (e.g., solutions obtained through self-implemented heuristic algorithms). A good feasible solution can improve the solving efficiency of MindOpt .
Note
Users need to ensure that the cutting planes added through the callback function must truly be cutting planes. If the node containing the optimal solutions is cut off, it may prevent MindOpt from finding an optimal solution.
6.2.1. Callback API¶
Read operation to track the MIP solving process:
CB_GET
Get context information from a particular solving process
Write operations to modify the MIP solving process:
CB_CUT
Add a user-defined cutting plane
CB_BRANCH
Modify the branching strategies and the traversal order of nodes
CB_SETSOLUTION
Provide a feasible solution to MindOpt.
APIs for different languages:
Operation
C
C++
Java
Python
CB_GET
CB_CUT
CB_BRANCH
CB_SETSOLUTION
6.2.2. Callback Code¶
When a user callback function is called, the where argument informs the user about the optimizer status. Possible where values are:
Where
Value
The MIP optimizer Status
MIPSTART
3
In the initialization phase
MIPSOL
4
Found a new incumbent
MIPNODE
5
Currently exploring a node
When the cbGet is called in a user callback function, an integer argument named what is needed. Allowable what values are:
What
Value
Result type
Result length
Result description
PRE_NUMVARS
200
int
The number of variables in the presolved model
PRE_NUMCONSTRS
201
int
The number of constraints in the presolved model
PRE_NUMNZS
202
int
The number of nonzero elements in the presolved model
MIP_OBJBST
300
double
Current best objective
MIP_OBJBND
301
double
Current best objective bound
MIP_RELVAL
302
double
Current objective of the relaxation model
MIP_NODCNT
306
int
Current explored node count
MIP_SOLCNT
307
int
Current count of feasible solutions found
MIP_CUTCNT
308
int
Current count of cutting planes applied
MIP_NODLFT
309
int
Current unexplored node count
MIP_NODEID
310
int
ID of current node
MIP_DEPTH
311
int
Depth of current node
MIP_PHASE
312
int
Current phase in the MIP solution process (0:no solution, 2:far from optimal, 3:near to optimal)
MIP_SOL
316
double array
MODEL_NUMVARS
Current best solution
MIP_REL
317
double array
MODEL_NUMVARS
Current relaxation solution
Note
When developing with MindOpt C SDK, for write operations (MDOcbcut()
,MDOcbbranch()
,MDOcbsolution()
), the variable indices to be passed should be the indices in the presolved model rather than the original model.
6.2.3. Callback SDK¶
C SDK
/** * The model argument is the original model. * The cbdata argument is the callback context for read or write operations. * The where argument indicates where in the optimization process the callback function is being called. * The userdata argument is the user-defined context. * The callback function should return zero by default. */ int callbackfn(const MDOmodel* model, void* cbdata, int where, const void* usrdata);
C++ SDK
/** * The model argument is the original model. * The cbdata argument is the callback context for read or write operations. * The where argument indicates where in the optimization process the callback function is being called. * The userdata argument is the user-defined context. * The callback function should return zero by default. */ int cbfn(const MDOmodel* model, void* cbdata, int where, const void* usrdata);
Java SDK
/** * The model argument is the original model. * The cbdata argument is the callback context for read or write operations. * The where argument indicates where in the optimization process the callback function is being called. * The userdata argument is the user-defined context. * The callback function should return zero by default. */ int java_callback(const MDOmodel* model, void* cbdata, int where, const void* usrdata)
Python SDK
# The model argument is the presolved model. # The where argument indicates the current phase in the optimization process. # The return value is ignored. def cbfn(model, where)
6.2.4. Example¶
The following example shows you how to use callback in the Python API of MindOpt.
from mindoptpy import *
m = Model()
m.modelsense = MDO.MAXIMIZE
x = m.addMVar((3,), obj=[20, 6, 8], vtype=['C', 'I', 'I'])
A = [
[0.8, 0.2, 0.3],
[0.4, 0.3, 0],
[0.2, 0, 0.1]
]
b = [4, 2, 1]
c = m.addMConstr(A, x, '<', b)
def callback(model, where):
where_names = {
3: "MIPSTART",
4: "MIPSOL",
5: "MIPNODE"
}
print("where = {}".format(where_names[where]))
# Count call times
model._calls += 1
# When using the Python SDK, the model passed to the callback is a presolved model. Note that the C SDK uses the original model.
# Print the constraint matrix of the presolved model here. The getA() function returns a scipy.sparse.csc_matrix.
# The constraint matrix returned by getA() is a reference, and any modifications to the constraint matrix should not be allowed as it may lead to unforeseen issues.
print(model.getA())
cur_sols = model.cbGetSolution(model.getVars())
# Print the current best solution
print(cur_sols)
rel_sols = model.cbGetNodeRel(model.getVars())
# Print the current relaxation solution
print(rel_sols)
# Member variables starting with "_" can be added to the model.
# The member variable "_calls" in this context is used to show the passing of the callback context.
m._calls = 0
# Pass the callback function to the solver
m.optimize(callback)
print("Number of calls: {}".format(m._calls))
# Print the optimization result
if m.status == MDO.OPTIMAL:
print("Objective value: {}".format(m.objval))
print("Solution")
sol = x.X
for i in range(len(sol)):
print(" x[{}] = {}".format(i, sol[i]))
else:
print("No feasible solution found so far")