CP_SAT Callback types

1,018 views
Skip to first unread message

Nara Torres Moreira

unread,
Sep 30, 2022, 5:54:27 AM9/30/22
to or-tools-discuss
Hi, is there any other type of callback available in CP-SAT other than SolutionCallback?

I'm trying to find a way to interrupt the optimizer in case X seconds have passed without having found any new solution (including any solution at all).

Thanks!

Laurent Perron

unread,
Sep 30, 2022, 7:05:19 AM9/30/22
to or-tools-discuss
You do not need a callback for this.
Which language ?
Laurent Perron | Operations Research | lpe...@google.com | (33) 1 42 68 53 00



--
You received this message because you are subscribed to the Google Groups "or-tools-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to or-tools-discu...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/or-tools-discuss/89fd2de0-4d51-43b7-b59a-e440a69880d1n%40googlegroups.com.

Laurent Perron

unread,
Sep 30, 2022, 7:07:07 AM9/30/22
to or-tools-discuss

Laurent Perron | Operations Research | lpe...@google.com | (33) 1 42 68 53 00


Nara Torres Moreira

unread,
Sep 30, 2022, 10:30:32 AM9/30/22
to or-tools-discuss
I'm using Python.
I currently use StopSearch if I want to stop when reaching a certain number of solutions, but that is done inside a callback that implements OnSolutionCallback, which is called only when new solutions are found. So I guess that doesn't work for me.
The only think I could imagine from what you said is that I need to do some parallelisation so that one thread can interrupt the CpSolver.Solve() call? Am I over complicating it?

Laurent Perron

unread,
Sep 30, 2022, 11:13:49 AM9/30/22
to or-tools-discuss
No, this is the way to go.

Laurent Perron | Operations Research | lpe...@google.com | (33) 1 42 68 53 00


Louie Hext

unread,
Oct 1, 2022, 6:00:39 AM10/1/22
to or-tools-discuss
Something like this can work - https://github.com/stradivari96/ortools-utils/blob/master/ortools_utils/callbacks.py

It utilisws the Timer() function from threading to call a StopSearch() method after a desired time. 
The timer starts in the initialisation of the call back so it should work even if no solutions are found.

Nara Torres Moreira

unread,
Oct 4, 2022, 6:24:02 AM10/4/22
to or-tools-discuss
Hi, thanks, but I don't see how this callback in the link using Timer could work for my purpose. The StopSearch() cannot be inside the SolutionCallback. Maybe I'm missing something?

I tried the following simple threading code, but it didn't work as I expected. The model continues after calling StopSearch(). I don't have much experience with parallelisation though, so I was wondering if anyone could spot something wrong I'm doing. Or should I use multi processing instead of multi threading?

Thank you!


    import time
    from threading import Thread    

    # more code here...

    def solve_with_ortools(self):
        self.solver = cp_model.CpSolver()
        solution_printer = SolutionPrinter(algorithm=self)
        self.solver.Solve(self.model, solution_printer)

    def solve_with_time_stop_control(self):
        thread = Thread(target=self.solve_with_ortools)
        thread.start()
        start = time.time()
        while True:
            elapsed = time.time() - start
            if elapsed > 30:
                print(f"Stopping thread after {elapsed} seconds")
                self.solver.StopSearch()
                break
        thread.join()


Kai Müller

unread,
Oct 4, 2022, 6:48:58 AM10/4/22
to or-tools-discuss
Hi,

why don't you just set the maxTimeInSeconds? Solver will then stop when time is reached.

Kai Müller

unread,
Oct 4, 2022, 6:50:02 AM10/4/22
to or-tools-discuss

Nara Torres Moreira

unread,
Oct 4, 2022, 7:00:09 AM10/4/22
to or-tools-discuss
Sorry, I know that my code is not complete yet for what I want, at this stage I just want to know how to force or-tools from another thread. Simply setting the time limit doesn't work for me because ultimately what I want is to "stop if X seconds have passed since the last solution improvement", which should be an easy condition to add given the StopSearch works.

Pracheer Gupta

unread,
Oct 4, 2022, 7:07:58 AM10/4/22
to or-tools...@googlegroups.com
If setMaxTimeInSeconds doesn’t work because you do not how long it will be before you stop seeing significant progress (per your original message), one possible option could be to send a termination signal such as SIGINT to the solver thread whenever you see fit. (The behavior should be similar to pressing ctrl-c in which case you should see a graceful shutdown of the solver).

On Oct 4, 2022, at 4:20 PM, Kai Müller <kai.m...@soobr.ch> wrote:

Sorry for another answer:

Laurent Perron

unread,
Oct 4, 2022, 11:17:18 AM10/4/22
to or-tools-discuss
It could be another problem, that is the solver slow to stop.
If you launch search (without this extra code), and type Ctrl-C. Does the search stop fast ?
Do you see the solver exit search cleanly ?

Laurent Perron | Operations Research | lpe...@google.com | (33) 1 42 68 53 00


Louie Hext

unread,
Oct 4, 2022, 3:32:03 PM10/4/22
to or-tools-discuss
The StopSearch() is not called by the call back but by the Timer() function, this calls a function after a given number of settings. So in this case you pass the StopSearch() to the Timer() with X of seconds ( "stop if X seconds have passed since the last solution improvement ") . If a solution is found in under X seconds, the Timer() will be restarted, if not the  StopSearch()  will be called from another thread. 

Its been a while since i've used that code but its intended to solve your exact issue.

Xiang Chen

unread,
Oct 4, 2022, 6:23:37 PM10/4/22
to or-tools...@googlegroups.com
Here is an example with a simple callback that works (you can ignore the code that is grayed out):


from threading import Timer
from itertools import permutations

from ortools.sat.python import cp_model


class ObjectiveEarlyStopping(cp_model.CpSolverSolutionCallback):
    def __init__(self, timer_limit: int):
        super(ObjectiveEarlyStopping, self).__init__()
        self._timer_limit = timer_limit
        self._timer = None
        self._reset_timer()

    def on_solution_callback(self):
        self._reset_timer()

    def _reset_timer(self):
        if self._timer:
            self._timer.cancel()
        self._timer = Timer(self._timer_limit, self.StopSearch)
        self._timer.start()

    def StopSearch(self):
        print(f"{self._timer_limit} seconds without improvement")
        super().StopSearch()


if __name__ == "__main__":
    model = cp_model.CpModel()
    nodes = range(1, 100)
    all_arcs = []

    literals = {}
    for i in nodes:
        literals[0, i] = model.NewBoolVar(f"0 -> {i}")
        literals[i, 0] = model.NewBoolVar(f"{i} -> 0")
        all_arcs.append([0, i, literals[0, i]])
        all_arcs.append([i, 0, literals[i, 0]])

    for i, j in permutations(nodes, 2):
        literals[i, j] = model.NewBoolVar(f"{i} -> {j}")
        all_arcs.append([i, j, literals[i, j]])

    model.AddCircuit(all_arcs)
    model.Maximize(sum(literals[i, j] * abs(i - j) for i, j in permutations(nodes, 2)))

    solver = cp_model.CpSolver()
    solver.parameters.cp_model_presolve = False
    solver.parameters.num_search_workers = 1
    solver.parameters.log_search_progress = True


    solver.SolveWithSolutionCallback(model, ObjectiveEarlyStopping(5))

Nara Torres Moreira

unread,
Oct 5, 2022, 8:28:03 AM10/5/22
to or-tools-discuss
Ohh brilliant! Super thank you both, that works indeed (the solution proposed by louie and xiang10 was the same)!
You saved my day :)

Sysjack

unread,
Nov 9, 2022, 6:59:36 AM11/9/22
to or-tools-discuss
How would I cancel the timer if the optimal solution is reached first? I haven't been able to figure it out.

Xiang Chen

unread,
Nov 9, 2022, 7:13:43 AM11/9/22
to or-tools...@googlegroups.com
The program would just continue after the solve, but if you really need to cancel it:

callback = ObjectiveEarlyStopping(5)
solver.SolveWithSolutionCallback(model, callback)
callback._timer.cancel()

you can also add an attribute to the callback class to check if it was cancelled or not, etc.

harryh...@gmail.com

unread,
Nov 15, 2022, 2:25:25 AM11/15/22
to or-tools-discuss
Hi,

why don't you just set the maxTimeInSeconds? Solver will then stop when time is reached.

PC See

unread,
Nov 15, 2022, 8:22:46 AM11/15/22
to or-tools-discuss
Her original question is "...  in case X seconds have passed without having found any new solution  ... "

Let say solutions found is already S1, S2, S3, .... [then, if take longer than time limit, X] ... break
It could be taking somewhere near to X in between S1---S2, S2---S3. Setting maxTimeInSeconds = X will yield a different outcome

Reply all
Reply to author
Forward
0 new messages