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
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:[ic,eplex]: (X+2 $>= Y)
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.pos_overlap
:
pos_overlap(Start,Duration,Time,Bool) :- % if Bool is 1 then the task with start time Start and % duration Duration overlaps time point Time Max1 is maxdiff(Start,Time), Max2 is maxdiff(Time,Start+Duration-1), eplex: (Time+(1-Bool)*Max1 $>= Start), % lin1 eplex: (Time $=< Start+Duration-1+(1-Bool)*Max2). % lin2 maxdiff(Expr1,Expr2,MaxDiff) :- % the maximum diffrence between Expr1 and Expr2 is the max val % of (Expr1 - Expr2) MaxDiff is max_val(Expr1 - Expr2). max_val(Expr, Max) :- % the maximum value of a variable is its upper bound var(Expr),!, get_var_bounds(Expr, _, Max). max_val(Expr, Max) :- % the maximum value of a number is itself number(Expr),!, Max = Expr. max_val(Expr1 + Expr2, Max) :- % the maximum value of (Exrp1 + Expr2) is the maximum value of % Expr1 plus the maximum value of Expr2 Max is max_val(Expr1) + max_val(Expr2). max_val(Expr1 - Expr2, Max) :- % the maximum value of (Exrp1 - Expr2) is the maximum value of % Expr1 minus the minimum value of Expr2 Max is max_val(Expr1) - min_val(Expr2). min_val(Expr, Min) :- % the minimum value of a variable is its lower bound var(Expr),!, get_var_bounds(Expr, Min, _). min_val(Expr, Min) :- % the minimum value of a number is itself number(Expr),!, Min = Expr. min_val(Expr1 + Expr2, Max) :- % the minimum value of (Exrp1 + Expr2) is the minimum value of % Expr1 plus the minimum value of Expr2 Max is min_val(Expr1) + min_val(Expr2). min_val(Expr1 - Expr2, Max) :- % the minimum value of (Exrp1 - Expr2) is the minimum value of % Expr1 minus the maximum value of Expr2 Max is min_val(Expr1) - max_val(Expr2). |
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.pos_overlap
is logically weaker than overlap
because
pos_overlap
is a linear relaxation of the disjunctive
constraint), and
pos_overlap
constraint to the original encoding:
eplex_constraints_2(Time,S1,S2,S3,B1,B2) :- % task 1 with duration 3 and task 2 with duration 5 are both % completed before the start time of task 3 before(S1,3,S3), before(S2,5,S3), % task 1 with duration 3 overlaps time point Time if B1 = 1 pos_overlap(S1,3,Time,B1), % task 2 with duration 5 overlaps time point Time if B2 = 1 pos_overlap(S2,5,Time,B2). hybrid3(Time, [S1,S2,S3], End) :- % give the eplex cost variable some default bounds ic:(End $:: -1.0Inf..1.0Inf), % we must give the start time of task 3 ic bounds in order to % suspend on changes to them ic: (S3::1..20), % setup the problem constraints ic_constraints(Time,S1,S2,B1,B2), eplex_constraints(Time,S1,S2,S3,B1,B2), % perform the optimisation both_opt(labeling([B1,B2,S1,S2]),min(S3),End). |
neg_overlap(Start,Duration,Time,Bool1,Bool2) :- % if Bool1 is 1 then the task with start time Start and duration % Duration starts after time point Time Max1 is maxdiff(Time,Start-1), eplex:(Time $=< Start-1+(1-Bool1)*Max1), % if Bool2 is 1 then the task with start time Start and duration % Duration is completed before time point Time Max2 is maxdiff(Start+Duration,Time), eplex:(Time+(1-Bool2)*Max2 $>= Start+Duration). eplex_constraints_3(T,S1,S2,S3,B1,N1B1,N2B1,B2,N1B2,N2B2) :- % task 1 with duration 3 and task 2 with duration 5 are both % completed before the start time of task 3 before(S1,3,S3), before(S2,5,S3), % task 1 with duration 3 either overlaps time point Time, % starts after it or is completed before it pos_overlap(S1,3,T,B1), neg_overlap(S1,3,T,N1B1,N2B1), eplex:(N1B1+N2B1 $= 1-B1), % task 2 with duration 5 either overlaps time point Time, % starts after it or is completed before it pos_overlap(S2,5,T,B2), neg_overlap(S2,5,T,N1B2,N2B2), eplex:(N1B2+N2B2 $= 1-B2), % exactly one of task 1 with duration 3 and task 2 with % duration 5 overlaps time point Time eplex:(B1+B2 $= 1). hybrid4(Time, [S1,S2,S3], End) :- % give the eplex cost variable some default bounds ic:(End $:: -1.0Inf..1.0Inf), % we must give the start time of task 3 and the non-overlap % booleans ic bounds in order to suspend on changes to them ic:(S3::1..20), ic:([N1B1,N2B1,N1B2,N2B2]::0..1), % setup the problem constraints ic_constraints(Time,S1,S2,B1,B2), eplex_constraints_3(Time,S1,S2,S3,B1,N1B1,N2B1,B2,N1B2,N2B2), % perform the optimisation both_opt(labeling([B1,N1B1,N2B1,B2,N1B2,N2B2,S1,S2]),min(S3),End). |
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:
diff2ic(X,Y) :- % X and Y must differ by at least 2 ic: ((X+2 $=< Y) or (Y+2 $=< X)). list_diff2ic(List) :- % each pair must differ by at least 2 ( fromto(List, [X|Rest], Rest, []) do ( foreach(Y, Rest), param(X) do diff2ic(X,Y) ) ). |
diff2eplex(X,Y,Length,B) :- % if B is 1 then Y is at least 2 greater than X eplex: (X+2+B*Length $=< Y+Length), % if B is 0 then X is at least 2 greater than Y eplex: (X+Length $>= Y+2+(1-B)*Length). list_diff2eplex(List, Length, Bools) :- % each pair must differ by at least 2 ( fromto(List, [X|Rest], Rest, []), fromto(Bools, Out, In, []), param(Length) do ( foreach(Y, Rest), fromto(Out, [B|Bs], Bs, In), param(X, Length) do diff2eplex(X,Y,Length,B) ) ). |
ic_list(List) :- length(List, Length), Max is 2*(Length-1), % each element must take a value between 1 and 2*(Length-1) ic: (List::1..Max), list_diff2ic(List), labeling(List). |
eplex_list(List) :- length(List, Length), Max is 2*(Length-1), % each element must take a value between 1 and 2*(Length-1) eplex: (List::1..Max), list_diff2eplex(List, Length, Bools), % enforce Bools to be 0..1 and integer eplex: integers(Bools), eplex: (Bools::0..1), % setup the eplex solver with a dummy objective function eplex:eplex_solver_setup(min(0),Cost,[],[]), % solve by linear solver eplex:eplex_solve(Cost). |
hybrid_list(List) :- % give the eplex cost variable some default bounds ic:(Cost $:: -1.0Inf..1.0Inf), length(List, Length), Max is 2*(Length-1), % each element must take a value between 1 and 2*(Length-1) ic: (List::1..Max), list_diff2ic(List), list_diff2eplex(List, Length, Bools), % enforce Bools to be 0..1 (but not integer in eplex) ic: (Bools::0..1), % setup the eplex solver with a dummy objective function eplex:eplex_solver_setup(min(0),Cost,[sync_bounds(yes)],[ic:min,ic:max]), % minimize Cost by branch-and-bound minimize((labeling(Bools),eplex_get(cost,Cost)),Cost). |
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:
Note that it is not possible to test for equality betweenic: '$>='(MinCost + 1, CostP,BP), % con3 ic: (QtyP*Count $= BP*Qty) % con4
MinCost
and CostP
because they are not integers but
real number variables.MinCost
and CostP
have tight bounds.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. 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.
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