0

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?

Roshin Raphel
  • 2,612
  • 4
  • 22
  • 40

2 Answers2

0

You can do this using the low-level engine object that is returned by the get_cplex() function. This is an instance of the Cplex class of the CPLEX Python API. You can install an Aborter into that instance using function use_aborter().

If you use the same aborter for all Cplex instances then you can eventually call the aborters abort() function to stop all the solves.

Note that the request to abort is not always handled immediately. There may be a small delay before the CPLEX engine acknowledges the abort.

Daniel Junglas
  • 5,830
  • 1
  • 5
  • 22
  • Thank you, but do you have some example of this. I tried adding solver.get_cplex(cplex.Cplex().use_aborter(cplex.Aborter().abort())) to thread as soon as it completes but it doesn't seems to be working – Kartik Kaushik Sep 16 '20 at 23:13
  • You have to install the aborter *before* you start the solve and keep a reference to that aborter. Then, when you want to abort the solve you call that aborter's `abort()` method. You can take a look at the `mipex4.py` example that ships with cplex (of course you should call `abort()` way later than in this example). – Daniel Junglas Sep 17 '20 at 06:09
0

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()```