LocalSolver logo
is now
Hexaly logo

We're excited to share that we are moving forward. We're leaving behind the LocalSolver brand and transitioning to our new identity: Hexaly. This represents a leap forward in our mission to enable every organization to make better decisions faster when faced with operational and strategic challenges.

This page is for an old version of Hexaly Optimizer. We recommend that you update your version and read the documentation for the latest stable release.

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, every timeBetweenDisplays 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.