Since ECL^{i}PS^{e} version 5.7, standalone eplex have become the standard
eplex, loaded with lib(eplex)
. The previous lib(eplex)
, which
loads eplex with the range bounds keeper and the IC variant have now been
phased out, so users of these old variants must now move
to using standalone eplex.
There are some differences at the source level between standalone and the older non-standalone eplex. This chapter outlines these differences to help users to port their existing code to standalone eplex.
The main difference between the standalone eplex and the non-standalone eplex is that the standalone version does not use an ECL^{i}PS^{e} ‘bounds keeper’ like lib(ic) or lib(range) to provide the ranges for the problem variables. Instead, ranges for variables are treated like another type of eplex constraint, i.e., they are posted to an eplex instance, and are stored with the external solver state.
In the non-standalone eplex, the bounds of all problem variables are transferred from the bounds keeper to the external solver each time the solver is invoked, regardless of if the bounds for the variables have changed or not since the last invocation. This can become very expensive if a problem has many variables. With the standalone eplex, this overhead is avoided as the external solver bounds for variables are only updated if they are explicitly changed. A possible inconvenience is that for hybrid programming, where eplex is being used with another ECL^{i}PS^{e} solver, any bound updates due to inferences made by the ECL^{i}PS^{e} solver are not automatically transferred to the external solver. This can be an advantage in that it leaves the programmer the freedom of when and how these bound changes should be transferred to the external solver.
The main user visible differences with the non-standalone eplex are:
[eclipse 3]: eplex_instance(instance). ... [eclipse 4]: instance: eplex_solver_setup(min(X)), instance: (X:: 0.0..10.0), instance: eplex_solve(C). X = X{0.0 .. 10.0 @ 0.0} C = 0.0 Yes (0.00s cpu)
The ::/2 ($::/2) constraints are treated like other eplex constraints, that is, the bounds for the variables are specific to their eplex instance. Other eplex instances (and indeed any other bounds-keeping solver) can have different and even incompatible bounds set for the same variable. Also, if the variable(s) do not already occur in the eplex instance, they will be added. Both of these are different from the non-standalone eplex, where bound constraints were treated separately from the eplex constraints.
Like other eplex constraints, inconsistency within the same eplex instance will lead to failure, i.e. if the upper bound of a variable becomes smaller than its lower bound, this will result in failure, either immediately or when the solver is invoked.
One potential problem is that with the non-standalone eplex, the bound
keeper’s ::/2
was re-exported through the eplex module (but not
through the eplex instances). One was able to write
eplex: (X :: 1.0..2.0)
and affect the bounds of the variable for all instances,
even though this was not posting a constraint to any eplex instance.
With the standalone eplex, the same code, eplex: (X :: 1.0..2.0)
has
different semantics and is a constraint for the eplex instance
eplex
only.
A variable never becomes ground as a result of an eplex instance bound constraint, even when the upper and lower bounds are identical.
Posting eplex arithmetic constraints involving one variable is the same as posting a bounds constraint. Unlike the non-standalone eplex, the variable will be added to the eplex instance even if it does not occur in any other constraints.
No propagation of the bounds is performed at the ECLiPSe level: the bounds are simply passed on to the external solver. In general, the external solver also does not do any bounds propagation that may be implied by the other constraints in the eplex instance.
Note that the generic get_var_bounds/3 and set_var_bounds/3 applies to all the eplex instances/solver states. If set_var_bounds/3 is called, then failure will occur if the bounds are inconsistent between the eplex instances.
sync_bounds(yes)
option can be
specified during solver setup (using eplex_solver_setup/4). This will ‘synchronise’ the bounds of all problem
variables when the external solver is invoked, by calling
get_var_bounds/3
for all problem variables. Note that it is the generic
get bounds handler that is called. set_var_bounds/3
. However, if there are
no bounds on this variable, the update will be lost. A warning is given
during the setup of the demon if the objective variable has no bounds. One possible solution is to add the objective variable to the problem
(e.g. by giving it bounds for the eplex instance). However, this can induce
extra ‘self-waking’ that needlessly invokes the solver (e.g. if the bounds
trigger option is used). Another solution is to add bounds to the variable
via some other bounds keeper, e.g. lib(ic)
. Note that it is always
possible to retrieve the objective value via the objective
option of
eplex_get/2.
standalone
which returns the value yes
for standalone eplex
and no
otherwise.use_var_names(yes)
options in setup should make
this somewhat easier as the variables would have the same names.