- Setting Up a Solver State
- Adding Constraints to a Solver State
- Running a Solver State Explicitly
- Accessing the Solver State
- Expandable Problem and Constraints
- Changing Solver State Settings
- Destroying a Solver State
- Miscellaneous Predicates

For many applications, the facilities presented so far should be
appropriate for using Simplex/MIP through ECL^{i}PS^{e}.
However, sometimes it may be more convenient or efficient
to directly access the solver state
instead of going through the abstraction of the eplex instances.
This section describes lower level operations like how to set up
solvers manually. In fact, these lower level predicates are used to
implement the predicates provided with eplex instances.

These predicates accesses the external solver state via a handle, which is returned when the solver state is set up, and subsequently used to access a particular solver state by the other predicates. The handle should be treated as a opaque data structure that is used by the eplex library to refer to a particular solver state.

This is used to set up a demon solver, and eplex_solver_setup/4 calls this predicate. There is one extra argument compared to eplex_solver_setup/4: the solver state handle Handle, which is returned by this predicate when the new solver state is created. The other arguments are the same as in eplex_solver_setup/4, except that there is an additional option in ListOfOptions: collect_from/1. This is used to specify which, if any, eplex instance the solver state should be collecting constraints from. If an eplex instance is specified (as pool(Instance)), then the solver state is associated with that instance. If the eplex instance is not to be associated with an eplex instance, none should be given as the argument to collect_from. This allows a solver state to be set up without the overhead of an eplex instance. The solver state will not collect any constraints automatically when it is invoked; instead the constraints must be added explicitly via the handle (using lp_add_constraints/3).

By default, the external solver is invoked once after set up by lp_demon_setup, if any TriggerModes is specified. Otherwise, the solver is not invoked and the predicate returns after set up.

This is an even lower-level primitive, setting up a solver state without any automatic triggering. It creates a new solver state for the set of constraints NormConstraints (see below for how to obtain a set of normalised constraints). Apart from the explicitly listed constraints, the variable’s ranges will be taken into account as the variable bounds for the simplex algorithm. Undeclared variables are implicitly declared as reals/1.

However, when variables have been declared integers in other solvers (e.g.
using ic:integers/1),
that is not taken into account by the solver by default.
This means that the solver will only work on the *relaxed problem*
(ie. ignoring the integrality constraints),
unless specified otherwise in the options.
Objective is either min(Expr) or max(Expr)
where Expr is a linear (or quadratic) expression.
ListOfOptions is a list of solver options, the same as for
lp_demon_setup/5 and eplex_solver_setup/4, except for the collect_from and initial_solve options, which are specific for the
demon solvers.

Constraints can be added directly to a solver state without posting them to an eplex instance. This is done by:

Add new constraints (with possibly new variables) to the solver state represented by Handle The new constraints will be taken into account the next time the solver is run. The constraints will be removed on backtracking.

The constraints are first normalised, and simple constraints filtered out (as discussed in section 10.2.1) before they are added to the external solver (by calling lp_add/3 described below).

This adds the constraints (both linear and integrality) to the external solver represented by Handle. The linear arithmetic constraints must be normalised. Note that it is possible to add trivial constraints, which would be filtered out by the higher level lp_add_constraints/3 using this predicate. Integrality constraints on non-problem variables are filtered out and a warning given.

This adds the variables in Vars to the external solver state represented by Handle. The variables should not contain variables which are already problem variables. The variables are given the default bounds of -infinity..infinity.

This updates the bounds for the problem variable Var in the external solver state represented by Handle. Failure occurs if Var is not a problem variable.

Apply the external solver’s LP or MIP solver to the problem represented by Handle. Precisely which method is used depends on the options given to lp_setup/4. lp_solve/2 fails (by default) if there is no solution or succeeds if an optimal solution is found, returning the solution’s cost in Cost (unlike with lp_demon_setup/6, Cost gets instantiated to a number). After a success, various solution and status information can be retrieved using lp_get/3 and lp_var_get/4.

The set of constraints considered by the solver is the one given when the solver was created plus any new constraints that were added (e.g by lp_add_constraints/3) in the meantime.

If there was an error condition, or limits were exceeded, lp_solve/2 raises an event. See section 10.9 for details.

Similar to lp_solve/2, but optimize for a modified problem as specified by Probes. This is the predicate called by eplex_probe/2

In section 10.3.1, we discussed how solver state information can be accessed via the eplex instance. Here are the lower level predicates that directly access this information via the solver state’s handle:

Retrieve information about solver state and results. See the reference manual description of lp_get/3 for a detailed description of the available values for What.

For example, it is possible to obtain the solution values from the last successful invocation of the external solver using the following:

instantiate_solution(Handle) :- lp_get(Handle, vars, Vars), lp_get(Handle, typed_solution, Values), Vars = Values.

Retrieve information about solver state represented by Handle, related to a specific variable Var. Again, see the reference manual for the available parameters.

Retrieve the bounds of the problem variable Var from the solver state represented by Handle.

This predicate implements a technique to prune variable bounds based on a global cost bound and the reduced costs of some solution to a problem relaxation. The assumptions are that there is a global problem whose cost variable is GlobalCost, and that Handle refers to a linear relaxation of this global problem. The pruning potentially affects all variables involved in the relaxed problem.

We provide low-level primitives to ‘expand’ an eplex problem. Such a problem is considered to have as yet unspecified components in the objective function and posted constraints. These constraints are known as expandable constraints. The as yet unspecified component involve variables that have not yet been added to the problem. When these variables are added, coefficients for the variables can be added to the expandable constraints, as well as the objective function. These primitives are the basis for implementing column generation, and are used by the column generation library, lib(colgen).

These primitives modify an existing eplex problem non-monotonically, and can only be used on problems that are not represented by an eplex instance, and was not setup as a demon solver (i.e. no trigger conditions are specified).

This adds expandable constraints Constraints to the solver state represented by Handle. The predicate returns a list of indices for these constraints in Idxs. The indices are used to refer to the constraints when new variables are added to expand the problem.

This expands the problem by adding new variables (columns) to the solver state represented by Handle. Columns is a list of variable:column-specification pair, where variable is the variable to be added as a new column, and column-specification the specification for the non-zero components of the column, i.e. coefficients for the expandable constraints (referred to using the index obtained from lp_add_constraints/4) and the objective for this variable.

In addition to accessing information from the solver state, some options (a subset of those specified during solver set up) can be changed by:

This primitive can be used to change some of the initial options
even after setup. *Handle* refers to an existing solver state. See the
reference manual for details.

Destroy the specified solver state, free all memory, etc. If the solver state is associated with an eplex handle, the solver state is disassociated with the eplex instance. However, unlike eplex_cleanup/0, the outstanding constraints not yet collected by the solver is not removed.

As with eplex_cleanup/0, care should be taken before using this non-logical predicate.

Read a problem from a file and setup a solver for it. Format is lp or mps. The result is a handle similar to the one obtained by lp_setup/4.

Write out the problem in the solver state represented by Handle to the file File in format Format.

where Constraints is a list of terms of the form
X `$=`

Y, X `$>=`

Y or X `$=<`

Y
where X and Y are arithmetic expressions.
The linear constraints are returned in normalised form in NormConstraints,
the nonlinear ones are returned unchanged in NonlinConstr.