Callbacks
COPT provides the callbacks utility, which supports users in obtaining information during the MIP solving process, e.g., the current best bound, the current optimal objective value, etc.; or controlling the solving process, e.g., by adding lazy constraints and cutting planes, or terminating the solving process. The problem types supporting the use of callbacks are MILP, MISOCP, MIQ(C)P.
A callback function is a user-provided function called by COPT during the solving process. The user can register one custom callback function via their preferred API for one or multiple callback contexts. Section Using the callback utilities in different APIs gives a detailed introduction to how to setup a callback function. The callback function will be invoked at certain moments during the solving process, depending on the callback contexts. When invoked, the user can access information and control the solving process, respectively. The available information and operations depend on the context. Currently, COPT supports four callback contexts:
CBCONTEXT_INCUMBENT
: Invokes the callback after a new incumbent was found.CBCONTEXT_MIPNODE
: Invokes the callback after a MIP node was processed.CBCONTEXT_MIPRELAX
: Invokes the callback when an LP-relaxation was solved.CBCONTEXT_MIPSOL
: Invokes the callback when a new MIP candidate solution is found.
The content of this chapter is organized as follows:
Notes
Only one callback function can be registered in COPT at a time. But one callback can be registered for multiple contexts. If a user wants to call different operations for different contexts (such as adding lazy constraints under CBCONTEXT_MIPSOL
and adding user cuts under CBCONTEXT_MIPRELAX
), they need to register one callback for all relevant contexts and have this callback call the respective operations based on the context in which it was called.
Obtaining information during the solving process
The information that can be obtained during the MIP solving process depends on the context the callback is invoked in, see the table below. Information is usually obtained by calling (an API dependent version of) getInfo
/ GetCallbackInfo
from within the callback function, specifying the desired information via a string supplied as the function argument. For a detailed description of the available callback information arguments, please refer to Callback information.
The following table lists information that can be obtained in different contexts:
Context |
Callback Information |
---|---|
|
|
|
|
|
|
|
In addition to the corresponding Callback Context and Information listed above, BestObj
, BestBnd
, HasIncumbent
, and Incumbent
can be obtained in any context.
Notes
If
HasIncumbent == False
, thenIncumbent
cannot be obtained.The return value of the “NodeStatus” information is constant, representing the solving status of the current node’s LP relaxation. For possible values, please refer to General Constants Section: Solution Status (Partial).
Incumbent
,RelaxSolution
, andMipCandidate
are obtained through different methods in different interfaces:C API: through the function
COPT_GetCallbackInfo
, the name of the intermediate information to be obtained is provided as arguments of the function;In object-oriented programming languages (C++/C#/Java/Python), the
CallbackBase
class provides specialized functions to obtain the corresponding intermediate information. E.g., in Python/C++CallbackBase
providesGetIncumbent
,GetRelaxSol
, andGetSolution
. Other programming language interfaces are similar, please refer to theCallbackBase
class of each API.
Controlling the MIP solving process
COPT provides functions to allow the user to interactively add lazy constraints or cutting planes during the solving process of the MIP branch-and-cut to control the MIP solving process. There are three main types of operations:
Adding lazy constraints
Adding cutting planes
Adding feasible solutions
Adding lazy constraints
Lazy constraints are constraints that are added to the model only when they are violated. For some models with a large number of constraints, adding lazy constraints only when violated can effectively reduce the size of the model during the solution process and improve the efficiency of the solving process. A popular example of this is the TSP model, see "cb_ex1"
in the examples directory in the installation package.
COPT supports two ways of adding lazy constraints. One is to explicitly add lazy constraints to the model before starting the solution process. The other is to add lazy constraints during the solving process through a user callback. For this purpose, each API provides two sets of methods, one for adding lazy constraints to the initial model and one for adding them from a callback. In the C API, the methods can be distinguished according to whether or not the function name contains "Callback"
, e.g., COPT_AddLazyConstr
and COPT_AddCallbackLazyConstr
. In object-oriented APIs, the two sets of functions correspond to the Model
class and the CallbackBase
class respectively. Taking python as an example:
Before solving, a user can directly add lazy constraints to the model by calling
Model.addLazyConstr()
orModel.addLazyConstrs()
respectively.During the solving process, a user can dynamically add lazy constraints (if supported by the current context, see below) from within the callback function via
CallbackBase.addLazyConstr()
orCallbackBase.addLazyConstrs()
.
In both cases, the added lazy constraints will be stored by COPT separately from the actual model and are only added to the model, when they are violated by a solution found during the solving process.
In order to ensure correctness, COPT will check whether any lazy constraints added so far are violated by any solution found during the solving process. This will increase the solving time, especially when many non-violated lazy constraints have been added. It is recommended that the user only adds lazy constraints when necessary, e.g. when they are violated by a solution.
Lazy constraints can only be added in the callback contexts CBCONTEXT_MIPSOL
and CBCONTEXT_MIPRELAX
. While is is not strictly necessary to check every LP relaxation solution for violated lazy constraints, a user has to check every solution provided in CBCONTEXT_MIPSOL
for feasibility against the lazy constraints in order to not produce wrong results.
To avoid adding unnecessarily many lazy constraints, COPT has some simple redundancy checks for lazy constraints in place. Exact duplicates will be discarded. However, adding many, very similar but redundant lazy constraints will negatively affect COPT’s performance. This should be avoided by the user.
Notes
Registering a callback function for the
CBCONTEXT_MIPSOL
will make COPT believe that the user wants to add lazy constraints. As lazy constraints are not actually part of the model, this will lead to the deactivation of dual reductions during COPT’s presolve, as dual arguments rely on the knowledge of all model rows. If the user does not intend to add lazy constraints but still wants to use theCBCONTEXT_MIPSOL
, COPT provides the LazyConstaints parameter which enables the user to explicitly tell COPT whether or not lazy constraints will be added to the model. By default, this parameter is set to-1
meaning COPT will turn off dual presolve reductions if either lazy constraints are part of the model or a callback for contextCBCONTEXT_MIPSOL
has been installed. Explicitly setting the parameter to0
will allow dual reductions during COPT’s presolve even if lazy constraints or a callback for contextCBCONTEXT_MIPSOL
are present. This is useful only in very rare cases, e.g., if the callback only prints information about solution candidates but never adds lazy constraints. As soon as lazy constraints are added, this might lead to wrong results, however. For printing information about solutions, consider using theCBCONTEXT_INCUMBENT
context instead.If a user invokes a function to add lazy constraints from a callback in the
CBCONTEXT_MIPSOL
context, the current MIP candidate solution will be rejected, no matter whether the added lazy constraint(s) are actually violated or not. This enables the user to reject arbitrary solutions by adding empty lazy constraints when an undesirable solution is found. Note however, that COPT might find the same solution multiple times if no lazy constraint is provided. The LP relaxation solution will not necessarily be rejected if lazy constraints are added inCBCONTEXT_MIPRELAX
, only when these are actually violated.It is invalid to call the any functions of the
Model
class for object-oriented languages (or their C equivalent) to add lazy constraints in a callback. More generally, the model cannot be changed during the solving process, except by adding lazy constraints or cutting planes.
Adding cutting planes
Cutting planes are added to the model during the solving process to strengthen the LP relaxation, e.g., cut off fractional LP solutions and improve the lower bound of the MIP problem.
COPT supports the addition of custom cutting planes to the model during the solving process. Similar to lazy constraints, cutting planes can be added to the model before and, via the callback, during the solving process. Each API provides two sets of methods, one for adding cutting planes to the initial model and one for adding them from a callback. In the C API, the methods can be distinguished according to whether or not the function name contains "Callback"
, e.g., COPT_AddUserCut
and COPT_AddCallbackUserCut
. In object-oriented APIs, the two sets of functions correspond to the Model
class and the CallbackBase
class respectively. Taking python as an example:
Before solving, user can directly add cutting planes to the model by calling
Model.addUserCut()
orModel.addUserCuts()
.During the solving process, a user can dynamically add cutting planes (if supported by the current context, see below) from within the callback function via
CallbackBase.addUserCut()
orCallbackBase.addUserCuts()
.
Cutting planes can only be added in the CBCONTEXT_MIPRELAX
context. Here, the user is provided with the current LP relaxation solution to separate their own cutting planes.
Notes
Cutting planes that do not violate the current LP relaxation solution are discarded by COPT.
It is invalid to call any functions of the
Model
class (or their C equivalent) to add cutting planes in a callback. More generally, the model cannot be changed during the solution process, except by adding lazy constraints or cutting planes.
Adding feasible solutions
COPT supports adding feasible solutions during the MIP solving process. This enables the user to provide any feasible solution they found in parallel to the COPT solution process, e.g., in a self-implemented heuristic. Known solutions can either be supplied as starting solutions by calling COPT_AddMipStart
(see MIP Starts) or from within a callback function. If a solution is know beforehand, supplying it as a MIP starting solution is preferred. For solutions found during the solving process, in the C API, a solution can be added by calling COPT_AddCallbackSolution
inside the callback. In object-oriented APIs, the functions needed to add a solution are provided by the CallbackBase
class and the workflow consists of calling two functions:
Set the feasible solution:
CallbackBase.setSolution(vars, val)
Load a custom solution into the model:
CallbackBase.loadSolution()
COPT will check any provided solution for feasibility and compute its objective value. The computed objective is return by loadSolution
/ COPT_AddCallbackSolution
. If a solution is infeasible or worse than the current incumbent, it is discarded and the objective value returned by COPT is set to 1.0e+30
.
Solutions can be added in any callback context.
Notes
Currently, COPT only supports complete feasible solutions within callbacks.
Using the callback utilities in different APIs
For object oriented programming languages the basic steps of setting up a callback function are:
Implement a custom callback class, inheriting from the
CallbackBase
class.Implement the
CallbackBase.callback()
function. This is the callback function that will be invoked by COPT. Here, the user can invoke the callback-specific functions for obtaining information or controlling the solution process.Create an object of the custom callback class.
Register the callback in COPT through
Model.setCallback()
, and input the Callback Context as a parameter. For registering the callback function for multiple contexts, one can bitwise-or the desired contexts, e.g.,COPT.CBCONTEXT_MIPSOL | COPT.CBCONTEXT_MIPNODE
.
In the subsequent solving process, the user-supplied CallbackBase.callback()
function will be called in each context registered. The currently invoked context can be obtained by calling the CallbackBase
class method where()
.
As already mentioned in the sections above, functions in CallbackBase
class (or their corresponding C functions) can only be called in certain contexts. The following table lists for each callback context the allowed callback operations, using the Python API:
Context |
Function |
---|---|
CBCONTEXT_INCUMBENT |
|
CBCONTEXT_MIPNODE |
|
CBCONTEXT_MIPRELAX |
|
CBCONTEXT_MIPSOL |
|
Notes
While
getInfo
can be called in all contexts, the information available depends on the context. See Obtaining information during the solving process for details.In other APIs
getInfo
is often split intogetIntInfo
andgetDblInfo
.The functions above may look slightly different for a certain API, but the presented relationships are the same.
While the above steps use Python as the reference API, the implementation in each object oriented programming language API is similar, and the user can refer to the provided sample code. For Python, "cb_ex1.py"
is available in the examples directory in the installation package. For C, the main differences when implementing and registering a callback are as follows:
The custom callback function can be any function using the signature
int COPT_CALL <function>(copt_prob* prob, void* cbdata, int cbctx, void* usrdata)
where<function>
is arbitrary.Instead of using
Where()
for obtaining the current context, the callback context is supplied incbctx
.Callback-relevant information can be passed by defining a custom
struct
and passing it as theusrdata
argument.
See cb_ex1.c
in the C examples folder for a reference implementation.
The calling method and function name of the callback function in different programming interfaces are slightly different, but the supported functions and function meanings are the same. Please refer to the corresponding chapters of different programming interface API reference manuals for specific introductions: