Hi!
I’ve implemented a callback in amplpy to change the gap after a given number of explored nodes, and I notice three things that I would like to discuss here:
- With the callback the parallel mode is not applied, and only 1 thread is used to solve the problem (thus, more time was used to solve the problem).
- With the callback the MIP search method changed to traditional branch and cut, while without the callback dynamic search is used (actually I read that cplex disable dynamic search in presence of control callbacks).
- The count of nodes that I do with my callback is different from that displayed in the logfile, please, if you can explain to me that.
Finally, I ask for a method (if exist) to use callbacks preserving the parallel mode, as well as for suggestions to improve my callback.
My callback is as follows:
#Callback class
class MyCallback(ampls.GenericCallback):
def __init__(self, stoprule):
super(MyCallback, self).__init__()
self._stoprule = stoprule
self._current = 0
self._nMIPnodes = 0
self._continueOpt = True
def run(self):
t = self.getAMPLWhere()
if t == ampls.Where.MSG:
print('>' + self.getMessage())
elif t == ampls.Where.MIPNODE:
self._nMIPnodes += 1
print("New MIP node, count {}".format(self._nMIPnodes))
if self._nMIPnodes >= self._stoprule['nodes'][self._current]:
self._continueOpt = True
return -1
elif t == ampls.Where.MIPSOL:
print("MIP Solution = {}".format(self.getObj()))
return 0
def setCurrentGap(self):
gaptolpct = 100*self._stoprule['gaptol'][self._current]
stopnodes = self._stoprule['nodes'][self._current]
print("Increasing gap tolerance to "
f"{gaptolpct:.2f}% after {stopnodes:.1f} nodes")
ampls_model.setAMPLsParameter(ampls.SolverParams.DBL_MIPGap,
self._stoprule['gaptol'][self._current])
self._current += 1
#Solve using callbacks
ampls_model=ampl.exportModel(solver,[“return_mipgap=5”,“mipstartvalue=3”,“mipstartalg=2”,“mipdisplay=2”])
#Stopping rule
stopdict={‘nodes’:(100,200,300),
‘gaptol’:(.001,.02,.3)}
callback=MyCallback(stopdict)
ampls_model.setCallback(callback)
#Invoke solver
while callback._continueOpt:
callback._continueOpt = False
ampls_model.optimize()
if callback._continueOpt:
callback.setCurrentGap()
Overview results without callback:
…
MIP search method: dynamic search.
Parallel mode: deterministic, using up to 8 threads.
…
690 391 16850.0251 423 16859.1290 16819.9129 379322 0.23%
…
Total (root+branch&cut) = 3713.24 sec. (1033774.39 ticks)
CPLEX 20.1.0.0: optimal integer solution within mipgap or absmipgap; objective 16859.12903
Overview results with the callback:
…
MIP search method: traditional branch-and-cut.
Parallel mode: none, using 1 thread.
…
New MIP node, count 99
48 48 16827.1844 467 16882.0053 16819.8279 277235 0.37% x133190 U 48 45 24
New MIP node, count 100
Flow cuts applied: 461
Mixed integer rounding cuts applied: 1461
Root node processing (before b&c):
Total (root+branch&cut) = 5092.52 sec. (4336608.06 ticks)
Increasing gap tolerance to 0.10% after 100.0 nodes
…
Thanks in advance