Originally posted by: SystemAdmin
[prubin said:]
Yes, but it's a bit complicated. You will need, besides the incumbent callback, a branch callback and maybe a cut callback. Note that, unless I am grossly mistaken, you cannot add a cut in the incumbent callback. You can only reject the incumbent there.
Part of the problem is in part that CPLEX can generate incumbents more than one way. One, of course, is that the node LP problem produces an integer feasible solution. If that happens, I don't think CPLEX will call the cut callback (which occurs after solving the node LP the first time), but I'm not positive. What I am more certain of is that the other way CPLEX can find an incumbent is by applying heuristics at the node. That happens after the LP is solved and after the cut callback is called, so it's too late to apply any cuts to that node.
Another issue is that you don't necessarily want CPLEX fathoming the node where the incumbent was found. Normally, in the absence of callbacks, if CPLEX finds an integer solution to the node LP, it fathoms the node (whereas if heuristics produced the incumbent, the node stays alive). If you are going to reject the incumbent and add a cut, though, the descendants of that node might contain an optimal solution, even if the incumbent came from an integer-feasible solution to the node LP. You would only want to fathom the node if the new cut either made the node infeasible or made its bound worse than the current incumbent. But if the node LP gives an integer-feasible solution, CPLEX does not know how to branch, since it normally branches on a fractional value in an integer variable.
Here is the way I have (successfully) implemented this:
1. The incumbent callback processes the incumbent and, if it does not like it, creates a new cut which it queues someplace in my code, then uses the reject() method to reject the incumbent. (As a passing note, you could queue a cut and also accept the incumbent, if that made sense in your algorithm.)
2. The cut callback checks the cut queue to see if any cuts are pending. If so, it adds them (either locally or globally, whichever is appropriate) and purges the queue.
3. The branch callback looks at the same cut queue. If it sees a cut queued up, it creates one child node using that cut. Otherwise, it does nothing (letting CPLEX handle branching the normal way). The branch callback does [i]not[/i] purge the queue.
Note that the branch callback will be called at the current node, whereas the cut callback will not be called until the next node, so there is no danger that the new cut will be purged from the queue before the branch callback sees it. In effect, the branch callback creates a clone of the current node with the new cut added. (If you want, you can use a node callback to force CPLEX to process the cloned node next. I don't; I'm content to let CPLEX process nodes according to whatever search strategy is in effect.) The cut callback applies the cut to all other nodes.
If you want the cut to be local, you don't need the cut callback at all; the branch callback applies the cut to the clone of the current node (and by implication its descendants). My cuts are all global, which is why I need the cut callback. If you do only local cuts and skip the cut callback, then the branch callback purges the queue while implementing the new cut.
/Paul
#CPLEXOptimizers#DecisionOptimization