Overview | Group | Tree | Graph | Index |
Branch & Cut |
Goals |
IloCplex
,
like all CPLEX technologies, uses
branch & cut search
when solving mixed integer programming (MIP) models.
The branch & cut search procedure manages a search tree
consisting of nodes. Every node represents
an LP or QP subproblem to be processed; that is, to be solved,
to be checked for integrality, and perhaps to be analyzed further.
Nodes are called active if they have not yet
been processed. After a node has been processed, it is no longer active.
IloCplex
processes active nodes in the tree until
either no more active nodes are available or some limit has been reached.
A branch is the creation of two new nodes from a parent node. Typically, a branch occurs when the bounds on a single variable are modified, with the new bounds remaining in effect for that new node and for any of its descendants. For example, if a branch occurs on a binary variable, that is, one with a lower bound of 0 (zero) and an upper bound of 1 (one), then the result will be two new nodes, one node with a modified upper bound of 0 (the downward branch, in effect requiring this variable to take only the value 0), and the other node with a modified lower bound of 1 (the upward branch, placing the variable at 1). The two new nodes will thus have completely distinct solution domains.
A cut is a constraint added to the model. The purpose of adding any cut is to limit the size of the solution domain for the continuous LP or QP problems represented at the nodes, while not eliminating legal integer solutions. The outcome is thus to reduce the number of branches required to solve the MIP.
As an example of a cut, first consider the following constraint involving three binary (0-1) variables:
20x + 25y + 30z <= 40
That sample constraint can be strengthened by adding the following cut to the model:
1x + 1y + 1z <= 1
No feasible integer solutions are ruled out by the cut, but some fractional solutions, for example (0.0, 0.4, 1.0), can no longer be obtained in any LP or QP subproblems at the nodes, possibly reducing the amount of searching needed.
The branch & cut method, then, consists of performing branches and applying cuts at the nodes of the tree. Here is a more detailed outline of the steps involved.
First, the branch & cut tree is initialized to contain the root node
as the only active node. The root node of the tree represents the entire
problem, ignoring all of the explicit integrality requirements. Potential
cuts are generated for the root node but, in the interest of keeping the
problem size reasonable, not all such cuts are applied to the model
immediately. An incumbent solution, that is, the best known
solution that satisfies all the integrality requirements, is established
at this point for later use in the algorithm. Ordinarily, no such solution
is available at this stage, but the user can specify a starting solution
using the method setVectors
.
Alternatively, when solving a sequence of modified problems,
the user can set the parameter MIPStart
to the value 1 (one) to indicate that the solution of the previous problem
should be used as a starting incumbent for the present problem.
When processing a node, IloCplex
starts by solving the continuous relaxation of its subproblem.
that is, the subproblem without integrality constraints.
If the solution violates any cuts, IloCplex
may add some or all of them to the node problem and resolves it.
This procedure is iterated until no more violated cuts are detected
(or deemed worth adding at this time) by the algorithm.
If at any point in the addition of cuts the node becomes infeasible,
the node is pruned (that is, it is removed from the tree).
Otherwise, IloCplex
checks whether the solution of the node-problem satisfies the
integrality constraints. If so, and if its objective value is better
than that of the current incumbent, the solution of the node-problem
is used as the new incumbent. If not, branching will occur, but first
a heuristic method may be tried at this point to see if a new incumbent
can be inferred from the LP/QP solution at this node,
and other methods of analysis may be performed on this node.
The branch, when it occurs, is performed on a variable where the
value of the present solution violates its integrality requirement.
This practice results in two new nodes being added to the tree for
later processing.
Each node, after its relaxation is solved, possesses an optimal objective function value Z. At any given point in the algorithm, there is a node whose Z value is better (less, in the case of a minimization problem, or greater for a maximization problem) than all the others. This Best Node value can be compared to the objective function value of the incumbent solution. The resulting MIP Gap, expressed as a percentage of the incumbent solution, serves as a measure of progress toward finding and proving optimality. When active nodes no longer exist, then these two values will have converged toward each other, and the MIP Gap will thus be zero, signifying that optimality of the incumbent has been proven.
It is possible to tell IloCplex
to terminate the branch & cut procedure sooner
than a completed proof of optimality. For example, a user can set
a time limit or a limit on the number of nodes to be processed.
Indeed, with default settings, IloCplex
will terminate the search when the MIP Gap has been brought
lower than 0.0001 (0.01%), because it is often the case that
much computation is invested in moving the Best Node value after
the eventual optimal incumbent has been located. This termination
criterion for the MIP Gap can be changed by the user, of course.
Goals can be used to control the branch and cut search in
IloCplex
. Goals are implemented in the class
IloCplex::GoalI
. The class
IloCplex::Goal
is the handle class. That is, it contains a pointer to an instance of
IloCplex::GoalI
along with accessors of objects in the implmenetation class.
To implement your own goal, you need to subclass
IloCplex::GoalI
and implement its virtual member functions execute
and duplicateGoal
. The method execute
controls the branch & cut search. The method duplicateGoal
creates a copy of the invoking goal object to be used for
parallel branch & cut search. Implementing your goal can be greatly
simplified if you use one of the macros
ILOCPLEXGOALn
.
Every branch & cut node maintains a goal stack, possibly containing
IloCplex::GoalI
objects. After
IloCplex
solves the relaxation of a node, it pops the top goal
from the goal stack and calls its method execute
.
There are several types of goals:
OrGoal
is executed,
IloCplex
will create child nodes. Each of the child nodes will be
initialized with a copy of the goal stack of the current node.
Then, for each child node, the specified goal in the OrGoal
is pushed onto the corresponding goal stack of the child node.
Finally, the current node is deleted. (See
IloCplex#GoalI::OrGoal
for a more detailed discussion.)AndGoal
is executed, its subgoals are simply pushed onto the stack. (See
IloCplex::GoalI::AndGoal
for a more detailed discussion.)
IloCplex::GoalI::FailGoal
is executed,
IloCplex
will prune the current node; that is,
it will discontinue the search at the current node.
IloCplex
will continue with another node
if there is one still available in the tree.
IloCplex::GoalI::SolutionGoal
is executed,
IloCplex
will attempt to inject a user-provided solution as a new incumbent.
Before ILOG CPLEX accepts the injected solution,
it first tests whether the injected solution is feasible with respect to
the model and goals.IloCplex
continues popping goals from the goal stack until OrGoal
or FailGoal
is executed,
or until the stack becomes empty; in the case of an empty stack,
it will continue with a built-in search strategy.
The predefined goals OrGoal
and AndGoal
allow you to combine goals. AndGoal
allows you to execute different goals at one node,
while OrGoal
allows you to execute different goals on different, newly created nodes.
A conventional use of these two goals in a return statement of a
user-written goal looks like this:
return AndGoal ( OrGoal (branch1, branch2), this);
This AndGoal
first pushes this
(the goal currently being executed) onto the goal stack,
and then it pushes the OrGoal
.
Thus the OrGoal
is on top of the stack and will be executed next.
When the OrGoal
executes,
it creates two new nodes and copies the remaining goal stack to both of them.
Thus both new nodes will have this
goal on top of the goal stack at this point. Then the OrGoal
proceeds to push branch1
onto the goal stack of the first child node and branch2
onto the goal stack of the second goal child node.
Conventionally, branch1
and branch2
contain cut goals, so by executing branch1
and branch2
at the respective child nodes,
the child nodes will be restricted to represent smaller subproblems
than their parent. After branch1
and branch2
have been executed, this
is on top of the node stack of both child nodes;
that is, both child nodes will continue branching according to the same rule.
In summary, this example creates the branches branch1
and branch2
and continues in both branches to control the same search strategy
this
.
To perform a search using a goal, you need to solve the extracted model
by calling the method IloCplex::solve(goal)
with the goal to use as an argument instead of the standard
IloCplex::solve
. The method solve(goal)
simply pushes the goal
onto the goal stack of the root node before calling the standard
solve
.
See Also
IloCplex::Goal
and IloCplex::GoalI