Callback functions¶
LocalSolver allows you to have your own functions called regularly during the search. It can be used for example to control when to stop the search or to display some specific information during the search.
Note
Note that this functionality requires using LocalSolver through its APIs, it is not available in the LSP language.
When adding a callback function to LocalSolver (with the addCallback
function)
you can specify when your function will be called, thanks to the LSCallbackType
parameter:
PhaseEnded
means that your function will be called at the end of each search phase.PhaseStarted
means that your function will be called at the beginning of each search phase.Ticked
means that your function will be called periodically, everytimeBetweenDisplays
seconds.
When a callback is called, the solver is paused. You can stop the resolution from a callback, retrieve the current solution, retrieve the statistics of the solver and, in general, call all the methods marked as “allowed in state Paused”. The same callback can be used for different events.
In this section, we detail how callback functions are introduced in each programming language (Python, C++, Java, .NET). To illustrate this description we will use a simple example where a callback function allows to stop the search when no improvement of the objective function if found during a period of 5 seconds (the solved problem is a random knapsack).
The corresponding source files can be retrieved in examples/callback
.
Callbacks in Python¶
In Python, a callback is simply a function or a method passed to LocalSolver.
A callback takes two parameters: the LocalSolver object and the type of
the callback. It can be a static function or a method on a class. In the
following example, we use a method in order to use two user-defined fields
last_best_value
and last_best_running_time
.
The callback is registered with the instruction
ls.add_callback(localsolver.LSCallbackType.TICKED, my_callback)
,
while my_callback
is defined as follows:
class callback_example:
...
def my_callback(self, ls, cb_type):
stats = ls.statistics
obj = ls.model.objectives[0]
if obj.value > self.last_best_value:
self.last_best_runnig_time = stats.running_time
if stats.running_time - self.last_best_running_time > 5:
print(">>>>>>> No improvement during 5 seconds: resolution is stopped")
ls.stop()
else:
print(">>>>>> Objective %d" % obj.value)
self.last_best_value = obj.value
Each time this callback will be invoked by LocalSolver (namely every
time_between_displays
seconds) it retrieves the statistics of the search and
consider the total running time and the current best value of the objective
function. If no improvement has been found during 5 consecutive seconds, it
calls the stop()
function to stop the search.
Callbacks in C++¶
In C++, a callback function is passed to LocalSolver as an function pointer the
callback type and a pointer to some user data. Here we create a small class
MyCallbackData containing a pointer to the LocalSolver object and the two fields
lastBestValue
and lastBestRunningTime
. The callback function uses this
data to access to LocalSolver statistics and objective value and to store
observed value and time. The callback is registered with the instruction
localsolver.addCallback(CT_Ticked,&mycallback,&cbdata);
while mycallback
is defined as follows:
void mycallback(LSCallbackType type, void *userdata) {
MyCallbackData* mydata =(MyCallbackData*) userdata;
LocalSolver* ls = mydata->ls;
assert(type == CT_Ticked);
LSStatistics stats = ls->getStatistics();
LSExpression obj = ls->getModel().getObjective(0);
if(obj.getValue() > mydata->lastBestValue)
mydata->lastBestRunningTime = stats.getRunningTime();
if(stats.getRunningTime() - mydata->lastBestRunningTime > 5) {
cout << ">>>>>>> No improvement during 5 seconds: resolution is stopped"<<endl;
ls->stop();
} else {
cout<<">>>>>> Objective improved by " << (obj.getValue() - mydata->lastBestValue)<<endl;
}
mydata->lastBestValue = obj.getValue();
}
Each time this callback will be invoked by LocalSolver (namely every
timeBetweenDisplays
seconds) it retrieves the statistics of the search and
consider the total running time and the current best value of the objective
function. If no improvement has been found during 5 consecutive seconds,
it calls the stop()
function to stop the search.
Callbacks in Java¶
In Java, a callback function is passed to LocalSolver as an object implementing
the LSCallback interface. This interface has a single method callback
taking as parameter the LocalSolver object and the callback type.
It can be conveniently introduced as an anonymous class:
ls.addCallback(LSCallbackType.Ticked, new LSCallback() {
private long lastBestValue = 0;
private int lastBestRunningTime = 0;
public void callback(LocalSolver ls, LSCallbackType type) {
assert(type == LSCallbackType.Ticked);
LSStatistics stats = ls.getStatistics();
LSExpression obj = ls.getModel().getObjective(0);
if(obj.getValue() > lastBestValue)
lastBestRunningTime = stats.getRunningTime();
if(stats.getRunningTime() - lastBestRunningTime > 5) {
System.out.println(">>>>>>> No improvement during 5 seconds: resolution is stopped");
ls.stop();
} else {
System.out.println(">>>>>> Objective improved by " + (obj.getValue() - lastBestValue));
}
lastBestValue = obj.getValue();
}
});
In the above code, an anonymous LSCallback
object is introduced, with two
numeric fields (lastBestValue
and lastBestRunningTime
). Each time this
callback will be invoked by LocalSolver (namely every timeBetweenDisplays
seconds) it retrieves the statistics of the search and consider the total
running time and the current best value of the objective function. If no
improvement has been found during 5 consecutive seconds, it calls the stop()
function to stop the search.
Callbacks in .NET¶
In .NET, a callback function is passed to LocalSolver as a delegate method
taking as parameter the LocalSolver object and the callback type. It can be a
static method or an instance method. We use an instance method in the example
below in order to use two user-defined fields lastBestValue
and
lastBestRunningTime
. The callback is registered with the instruction
ls.AddCallback(LSCallbackType.Ticked, MyCallback);
, while MyCallback
is defined as follows:
public void MyCallback(LocalSolver ls, LSCallbackType type)
{
Debug.Assert(type == LSCallbackType.Ticked);
LSStatistics stats = ls.GetStatistics();
LSExpression obj = ls.GetModel().GetObjective(0);
if (obj.GetValue() > lastBestValue)
lastBestRunningTime = stats.GetRunningTime();
if (stats.GetRunningTime() - lastBestRunningTime > 5)
{
Console.WriteLine(">>>>>>> No improvement during 5 seconds: resolution is stopped");
ls.Stop();
}
else
{
Console.WriteLine(">>>>>> Objective " + (obj.GetValue()));
}
lastBestValue = obj.GetValue();
}
Each time this callback will be invoked by LocalSolver (namely every
TimeBetweenDisplays
seconds) it retrieves the statistics of the search and
consider the total running time and the current best value of the objective
function. If no improvement has been found during 5 consecutive seconds, it
calls the Stop()
function to stop the search.