next up previous index
Next: Low-Level Solver Interface Up: EPLEX: The ECLiPSe/CPLEX Interface Previous: Black-Box Interface   Index

Subsections

Interface for CLP-Integration

Simplex Demons

To implement hybrid algorithms where a run of a simplex solver is only a part of the global solving process, the black-box model presented above is not appropriate any more. As a more convenient model, we introduce the concept of a simplex demon. A simplex demon collects linear constraints and re-solves the problem whenever bounds change or new constraints appear.

lp_demon_setup(+Objective, -Cost, +ListOfOptions, +Priority, +TriggerModes, -Handle)

Declaratively, this can be seen as a compound constraint representing all the individual linear constraints that have been set so far and are going to be set up later. Operationally, the delayed constraints are collected and an external solver is set up (as with lp_setup/4). Then the problem is solved once initially (as with lp_solve/2) and a delayed goal lp_demon/7 is set up which will re-trigger the solver when certain conditions are met.

Handle refers to the created solver state (as in lp_setup/4 or lp_read/3 described below). It can be used to access and modify the state of the solver, retrieve solution information etc.

Unlike with lp_solve/2, Cost will not be instantiated to a solution's cost, but only be bounded by it: For a minimization problem, each solution's cost becomes a lower bound, for maximization an upper bound on Cost. This technique allows for repeated re-solving with reduced bounds or added constraints.

ListOfOptions is a list of solver options as described is section 8.6.1.1 for lp_setup/4.

Priority is the scheduling priority with which the solver gets woken up. This priority determines whether the solver is run before or after other constraints. It is recommended to choose a priority that lies below the priority of more efficient propagation constraints, e.g. 5.

TriggerModes specifies under which conditions the solver demon will be re-triggered. It can be a list of the following specifiers

inst:
re-trigger if a problem variable gets instantiated.
deviating_inst:
re-trigger if a problem variable gets instantiated to a value that differs from its lp-solution more than a tolerance.
bounds:
re-trigger each time a variable bound changes.
deviating_bounds:
re-trigger each time a variable bound changes such that its lp-solution gets excluded more than a tolerance.
new_constraint:
re-trigger each time a new constraint appears.
trigger(Atom):
re-trigger each time the symbolic trigger Atom is pulled by invoking schedule_suspensions/1.
pre(Goal):
an additional condition to be used together with other triggers. When the demon is triggered, it first executes PreGoal. Only if that succeeds, does the appropriate external solver get invoked. This provides a way of reducing the number of (possibly expensive) solver invocations when given preconditions are not met.
post(Goal):
this is not a trigger condition, but specifies a goal to be executed after solver success, but before the Cost variable gets constrained. It is intended as a hook for exporting solution information, e.g. copying solutions from the solver state into variable attributes (eg. tentative value), or computing weights for labeling heuristics from the solver state.
The tolerances mentioned can be specified in lp_setup/2 or lp_set/3 as demon_tolerance.

Some common invocations patterns for this predicate are the following. The first triggers the solver only on instantiation of variables to values that don't fit with the simplex solution:

lp_demon_setup(min(Expr), C, [], 5, [deviating_inst], H)
The next one is more eager and triggers on significant bound changes or whenever new constraints arrive:
lp_demon_setup(max(Expr), C, [], 5, [new_constraint,deviating_bounds], H)
The solver can also be triggered explicitly by setting it up with
lp_demon_setup(min(Expr), C, [], 5, [trigger(run_simplex)], H)
and then issuing the command
schedule_suspensions(run_simplex),wake
If several trigger conditions are specified, then any of them will trigger the solver.

When a solver demon runs frequently on relatively small problems, it can be important for efficieny to switch the external solver's presolving off (lp_set(presolve,0)) to reduce overheads.

solution_out_of_range(+Handle)

This is intended as a useful pre(Goal) for lp_demon_setup/6 in connection with the bounds trigger mode. It succeeds if any of the solutions (computed by the most recent successful solving) of Handle are more than a tolerance outside the range of the corresponding variables, ie. couldn't be instantiated to this value. The admissible tolerances can be specified in lp_setup/2 or lp_set/3 as demon_tolerance.

instantiation_deviates(+Handle)

This is intended as a useful pre(Goal) for lp_demon_setup/6 in connection with the inst trigger mode. It succeeds if any of the variables originally involved in Handle have been instantiated to a value that is not within +/- tolerance from the latest simplex solution for that variable. The admissible tolerances can be specified in lp_setup/2 or lp_set/3 as demon_tolerance.

Example

The simplest case of having a simplex solver automatically cooperating with a CLP program, is to set up a solver demon which will repeatedly check whether the continuous relaxation of a set of constraints is still feasible. The code could look as follows:

simplex :-
    lp_demon_setup(min(0), C, [solution(no)], 5, [bounds], _).
First, the constraints are normalised and checked for linearity. Then a solver with a dummy objective function is set up. The option solution(no) indicates that we are not interested in solution values. Then we start a solver demon which will re-examine the problem whenever a change of variable bounds occurs. The demon can be regarded as a compound constraint implementing the conjunction of the individual constraints. It is able to detect some infeasibilities that for instance could not be detected by the finite domains solver, e.g.
[eclipse]: X+Y+Z $>= K, X+Y+Z $=< 1,
    lp_demon_setup(min(0), C, [solution(no)], 5, [bounds], _),
    K = 2.

no (more) solution.
In the example, the initial simplex is successful, but instantiating K wakes the demon again, and the simplex fails this time.

A further step is to take advantage of the cost bound that the simplex procedure provides. The setup is similar to above, but we accept an objective function and add a cost variable. The bounds of the cost variable will be updated whenever a simplex invocation finds a better cost bound on the problem. In the example below, an upper bound for the cost of 1.5 is found initially:

[eclipse 14]: X+Y $=< 1, Y+Z $=< 1, X+Z $=< 1,
        lp_demon_setup(max(X+Y+Z), Cost, [solution(no)], 5, [bounds], _).

X = X{-1e+20 .. 1e+20}
Y = Y{-1e+20 .. 1e+20}
Z = Z{-1e+20 .. 1e+20}
Cost = Cost{-1e+20 .. 1.500001}

Delayed goals:
        lp_demon(prob(...), ...)
yes.
If the variable bounds change subsequently, the solver will be re-triggered, possibly improving the cost bound to 1.3:
[eclipse 16]: X+Y $=< 1, Y+Z $=< 1, X+Z $=< 1,
        lp_demon_setup(max(X+Y+Z), Cost, [solution(no)], 5, [bounds], _), 
        Y $=< 0.3.

X = X{-1e+20 .. 1e+20}
Z = Z{-1e+20 .. 1e+20}
Cost = Cost{-1e+20 .. 1.300001}
Y = Y{-1e+20 .. 0.3}

Delayed goals:
        lp_demon(prob(...), ...)
yes.

A further example is the implementation of a MIP-style branch-and-bound procedure. Source code is provided in the library file mip.pl.


next up previous index
Next: Low-Level Solver Interface Up: EPLEX: The ECLiPSe/CPLEX Interface Previous: Black-Box Interface   Index

1999-08-06