Because of the cooperation between solvers, it is often useful to send
constraints to multiple solvers.
A linear constraint, such as X+2 ≥ Y, can be posted to eplex by
the code eplex: (X+2 $>= Y). The same constraint can be posted
to ic by the code ic: (X+2 $>= Y).
The constraint can be sent to both solvers by the code
[ic,eplex]: (X+2 $>= Y)
By sending constraints to both solvers, where possible, we can improve search algorithms for solving constraint problems. Through enhanced constraint reasoning at each node of the search tree we can:
The second advantage is a particular benefit of combining different solvers, as opposed to enhancing the reasoning power of a single solver. See [22] and [23] for experimental results and application examples using multiple solvers in this way.
The overlap constraint example above is disjunctive and
therefore non-linear, and is only handled by ic.
However as soon as the boolean variable is labelled to 1, during
search, the constraint becomes linear.
The cooperation between the eplex and ic solvers could therefore be improved by passing the resulting linear constraint to eplex as soon as the boolean is labelled to 1. This could be achieved using a constraint handling rule (see CHR) or a suspended goal (see chapter 14).
However the same improved cooperation can be achieved by a well known mathematical programming technique (see e.g. [30]) that builds the boolean variable into a linear constraint that can be sent to eplex even before the boolean is instantiated. This linear constraint effectively enforces the overlap constraint if the boolean is instantiated to 1, but does not enforce it if the boolean is instantiated to 0.
To achieve this we introduce sufficiently big multipliers, that when the boolean is set to 0 the constraint is satisfied for all values within the variables’ bounds. This method is known as the bigM transformation.
It is illustrated in the following encoding
of pos_overlap:
|
The linear constraints, which will enforce the overlap condition when
the variable Bool is set to 1, are labelled
lin1 and lin2.
If the variable Bool is instantiated to 0, then the variables (or
values) Start, Time and Duration are free to take
any value in their respective domains.
Notice that pos_overlap is logically weaker than overlap
because
pos_overlap is a linear relaxation of the disjunctive
constraint), and
The tighter cooperation is achieved simply by adding the
pos_overlap constraint to the original encoding:
|
Although it may at first glance seem better to enforce the integerality of all variables in the linear solver as well, this is in fact counter-productive for variables that will be explicitly labelled during search in hybrid algorithms. The external solver would then perform its own branch-and-bound search in addition to the branch-and-bound search being performed within the ECLiPSe program.
The same technique, of introducing boolean variables and sufficiently large multipliers, can be used to translate any disjunction of linear constraints into linear constraints (and integrality constraints on the booleans) which can be handled by eplex.
Consider the negation of the overlap constraints above: if a task does not overlap a time point then it is either completed before the time point or starts after the timepoint. This disjunction can be expressed in eplex using two further boolean variables:
|
Now the negation of the overlap will be enforced whenever either of the non-overlap booleans is set to 1. Note that it is not strictly necessary to label the non-overlap booleans: whenever the start time of a task is labelled in such a way that the task falls to one side of the time point, the other non-overlap boolean will be forced to 0 by its linear non-overlap constraint. The constraint requiring a task to either overlap or fall to one side of the time point will then force the remaining non-overlap boolean to be 1.
In fact in this simple example we gain nothing by including the neg_overlap constraints on the “direction” of non-overlap. As soon as a labeling decision has been made as to whether one task overlaps the time point, the earliest possible start time of both tasks is updated in the linear solver. Since the problem cost is minimized by starting all tasks as early as possible, the relaxed eplex solution will conicide with the integer solution.
As another simple example consider a naive program to choose values for the
elements of a finite list (of length Length) such that each
pair of values differs by at least 2.
The diff2 constraint on each pair X and Y
of elements can be expressed as a disjunction in ic:
|
Alternatively it can be expressed in eplex using a boolean variable:
|
Suppose each element E of the list must take a value between 1 and 2*(Length−1), then any attempted labelling of the elements must fail. Sending the constraints to ic and labelling the elements of the list is inefficient.
|
Sending the constraints to eplex and enforcing integrality of the booleans is more efficient.
|
Better still is to post the constraints to both ic and eplex, and label the booleans.
|
For more complex applications, sending all “linearisable” constraints to both ic and eplex is rarely the best method. Sending too many constraints to ic can result in many wakings but little useful propagation. Sending too many constraints to eplex can cause a big growth in the size of the constraint store, which slows down constraint solving with little improvement in the relaxed optimum. If the extra variables are constrained to be integer, then the (MIP) solver may enter a deep search tree with disastrous consequences for efficiency. In this example we briefly illustrate the point, though there is no space to include the whole program, and complete supporting results.
Consider the problem of generating test networks for IP (internet protocol). To generate such networks, it is necessary to assign capacities to each line. We assume a routing algorithm that sends each message along a “cheapest” path, where the cost is dependent on the bandwidth. Messages from a particular start to end node are divided equally amongst all cheapest paths.

Path Flows
Given a total quantity Qty of messages, between a particular
start and end node, it is necessary to compute the quantity of
messages QtyP along each path P between the two nodes.
The variable CostP represents the cost of this path, and the
variable MinCost represents the cost of the cheapest path.
The variable Count represents the number of cheapest paths
(between which the messages were equally divided).
A boolean variable BP records whether the
current path is a cheapest path, and therefore whether QtyP
is non-zero.
The encoding in ic is as follows:
ic: '$>='(MinCost + 1, CostP,BP), % con3 ic: (QtyP*Count $= BP*Qty) % con4
Note that it is not possible to test for equality between
MinCost and CostP because they are not integers but
real number variables.
These constraints are very precise but propagate little until the
variables MinCost and CostP have tight bounds.
The challenge is to find a combination of ic and eplex
constraint handling that efficiently extract the maximum information
from the constraints.
Linearising con3 so it can be handled by eplex does not
help prune the search tree.
Worse, it
may significantly increase the size of the linear constraint store and
the number of integer (boolean) variables, which impacts solver
performance.
Once all the boolean variables are instantiated, the
sum of QtyP for all the paths equals the total quantity
Qty (because precisely Count paths have a non-zero
PQty = Qty / Count).
We therefore introduce a variable Qties constrained to be the
sum of all the path quantities. If QtyList is a list of the
path quantities, we can express the constraint thus
Qties $= sum(QtyList).
We can now add a redundant constraint Qty $= Qties.
The above constraints are both linear and can be handled by
eplex.
In practice this encoding dramatically enhances the efficiency of the test network generation. Experimentation with this program revealed that posting the redundant constraints to eplex yields a much more significant improvement than just posting them to ic.
It is easy to send a constraint to more than one solver. Even disjunctive constraints can be encoded in a form that enables them to be sent to both solvers. However for large applications it is best to send constraints only to those solvers that can extract useful information from them. This requires experimentation.
Figure 17.3: Sending Constraints to Multiple Solvers