Search code examples
pythondocplex

How to asynchronously abort a solve in docplex?


I have 2 simultaneous solves running using docplex and I want to stop one of the solve as soon as the other one finishes. Is there an way to end one solve?


Solution

  • Thank you, I was able to add the feature using below code:

    from docplex.mp.progress import ProgressListener, ProgressClock
    class AutomaticAborter(ProgressListener):
        """ a simple implementation of an automatic search stopper.
        """
        def __init__(self):
            super(AutomaticAborter, self).__init__(ProgressClock.All)
            self.last_obj = None
            self.last_obj_time = None
            self.con = 0
            
        def notify_start(self):
            super(AutomaticAborter, self).notify_start()
            self.last_obj = None
            self.last_obj_time = None    
            self.con = 0
        def is_improving(self, new_obj, eps=1e-4):
            last_obj = self.last_obj
            return last_obj is None or (abs(new_obj- last_obj) >= eps)
                
        def notify_progress(self, pdata):
            super(AutomaticAborter, self).notify_progress(pdata)
            global con_f
            if pdata.has_incumbent and self.is_improving(pdata.current_objective):
                self.last_obj = pdata.current_objective
                self.last_obj_time = pdata.time
                print('----> #new objective={0}, time={1}s'.format(self.last_obj, self.last_obj_time))
            else:
                # a non improving move
                last_obj_time = self.last_obj_time
                this_time = pdata.time
                if last_obj_time is not None:
                    self.con=con_f
                    if con_f>0:
                        print('!! aborting cplex, con_f >{}'.format(self.con))
                        self.abort()
                    else:
                        print('----> running {}'.format(con_f))
    con_f=0
    
    solver.add_progress_listener(AutomaticAborter())
    solver1.add_progress_listener(AutomaticAborter())
    
    def work(worker_queue, id, stop_event):
        while not stop_event.is_set():
    
            if id==1:
                work.msol=solver.solve(clean_before_solve=True,log_output=True)
            else:
                work.msol1=solver1.solve(clean_before_solve=True,log_output=True)
            # put worker ID in queue
            if not stop_event.is_set():
                worker_queue.put(id)
            break
    # queue for workers
    worker_queue = Queue()
    
    # indicator for other threads to stop
    stop_event = threading.Event()
    
    # run workers
    threads = []
    threads.append(Thread(target=work, args=(worker_queue, 1, stop_event)))
    threads.append(Thread(target=work, args=(worker_queue, 2, stop_event)))
    
    for thread in threads:
        thread.start()
    # this will block until the first element is in the queue
    first_finished = worker_queue.get()
    
    print(first_finished, 'was first!')
    
    con_f=1
    
    
    # signal the rest to stop working
    stop_event.set()```