next up previous index
Next: Black-Box Interface Up: EPLEX: The ECLiPSe/CPLEX Interface Previous: Ranged and Typed Variables   Index

Subsections
  • Linear Constraints
  • Linear Expressions
  • Integrality
  • Solving Simple Eplex Problems
  • Examples

    Eplex Instances

    In this chapter, the problem passed to the external solver will be referred to as an eplex problem. An eplex problem consists of a set of linear arithmetic constraints, whose variables may possibly have integrality constraints. The external solver will solve such a problem by optimising these constraints with respect to an objective function.

    With the eplex library, it is possible to have more than one eplex problem within one program. The simplest way to write such programs with the library is through Eplex Instances. An eplex instance is an instance of the eplex solver, to which an eplex problem can be sent. An external solver state can be associated with each eplex instance, which can be invoked to solve the eplex problem. Declaratively, an eplex instance can be seen as a compound constraint consisting of all the variables and constraints of the eplex problem.

    Like other solvers, each eplex instance has its own module. To use an eplex instance, it must first be declared, so that the module can be created. This is done by:


    eplex_instance(+Name)

    This predicate will initialise an eplex instance Name. Once initialised, a Name module will exist, to which the user can post the constraints for the eplex problem and setup and use the external solver state to solve the eplex problem. Normally, this predicate should be issued as a directive in the user's program, so that the program code can refer to the instance directly in their code. For example:

       :- eplex_instance(instance).
    

    For convenience, the eplex library declares eplex as an eplex instance when the library is loaded.


    Linear Constraints

    The constraints provided are equalities and inequalities over linear expressions. Their operational behaviour is as follows:

    In exceptional cases, it is possible for a problem variable not to be transferred to the external solver at all. This would happen when the variable occurs only in trivial single-variable constraints, and does not occur in the objective function either.

    As with all predicates defined for an eplex instance, these constraints should be module-qualified with the name of the eplex instance. In the following they are shown qualified with the eplex instance. Other instances can be used if they have been declared using eplex_instance/1.


    eplex: (X =:= Y)

    X is equal to Y. X and Y are linear expressions.


    eplex: (X >= Y)

    X is greater or equal to Y. X and Y are linear expressions.


    eplex: (X =< Y)

    X is less or equal to Y. X and Y are linear expressions.

    Linear Expressions

    The following arithmetic expression can be used inside the constraints:

    X
    Variables. If X is not yet a ranged variable, it is turned into one via an implicit declaration X :: -inf..inf.

    123, 3.4
    Integer or floating point constants.

    +Expr
    Identity.

    -Expr
    Sign change.

    E1+E2
    Addition.

    sum(ListOfExpr)
    Equivalent to the sum of all list elements.

    E1-E2
    Subtraction.

    E1*E2
    Multiplication.

    ListOfExpr1*ListOfExpr2
    Scalar product: The sum of the products of the corresponding elements in the two lists. The lists must be of equal length.

    Integrality

    The difference between using an LP vs. an MIP solver is made by declaring integrality to the solver via the integers/1 constraint:
    integers(Vars)
    Constrains list of variables to integers. The lib(eplex)-version of this constraint subsumes the integers/1 of either the ic- or range libraries. In addition, the constraint gets delayed until an external solver is invoked using optimize/2 or lp_demon_setup/6 or one of the lower-level primitives. The external solver will then take the integrality into account, i.e. to solve a MIP rather than a relaxed LP problem. Note that unless eplex:integers/1 (or lp_add/3, see section 11.6.2.2) is invoked, any invocation of the eplex external solver (via lp_solve/2, lp_demon_setup/6 or optimize/2) will only solve a continuous relaxation, even when problem variables have been declared as integers in other solvers (range, ic, fd). However, when a typed_solution is retrieved (via lp_get/3 or lp_var_get/3), this will be rounded to the nearest integer even when the integrality has only been declared with the lib(range) or lib(ic) versions of ::/2 or integers/1.


    Solving Simple Eplex Problems

    In order to solve an eplex problem, the eplex instance must be set up for an external solver state. The solver state can then be invoked to solve the problem. The simplest way to do this is to use:

    eplex_solver_setup(+Objective)
    This predicate creates a new external solver state and associates it with the eplex instance. After this, the solver state can be invoked to solve the eplex problem.

    Objective is either min(Expr) or max(Expr) where Expr is a linear expression (or quadratic, if supported by the external solver).

    eplex_solve(-Cost)
    Explicitly invokes the external solver state. First, all the arithmetic and integrality constraints posted for this eplex instance are collected, and the external solver state is invoked with these constraints taken into account. If the external solver can find an optimal to the eplex problem, then the predicate succeeds and Cost is instantiated to the optimal value. If the problem is infeasible (has no solution) or unbounded (Cost is not bounded by the constraints), then the predicate fails.

    Examples

    Here is a simple linear program, handled by the predefined eplex instance 'eplex':

    :- lib(eplex).
    
    lp_example(Cost) :-
         eplex: eplex_solver_setup(min(X)),
         eplex: (X+Y >= 3),
         eplex: (X-Y =:= 0),
         eplex: eplex_solve(Cost).
    

    The same example using a user-defined eplex instance:

    :- lib(eplex).
    :- eplex_instance(my_instance).
    
    lp_example(Cost) :-
         my_instance: eplex_solver_setup(min(X)),
         my_instance: (X+Y >= 3),
         my_instance: (X-Y =:= 0),
         my_instance: eplex_solve(Cost).
    

    Running the program gives the optimal value for Cost:

    [eclipse 2]: lp_example(Cost).
    
    Cost = 1.5
    

    Note that if the eplex eplex instance is used instead of my_instance, then the eplex_instance/1 declaration is not necessary.

    By declaring one variable as integer, we obtain a Mixed Integer Problem:

    :- lib(eplex).
    :- eplex_instance(my_instance).
    
    mip_example(Cost) :-
         my_instance: (X+Y >= 3),
         my_instance: (X-Y =:= 0),
         my_instance: integers([X]),
         my_instance: eplex_solver_setup(min(X)),
         my_instance: eplex_solve(Cost).
    
    ....
    [eclipse 2]: mip_example(Cost).
    
    Cost = 2.0
    

    The cost is now higher because X is constrained to be an integer. Note also that in this example, we posted the constraints before setting up the external solver, whereas in the previous example we set up the solver first. Generally the solver set up and constraint posting can be done in any order, with one restriction: at set up, eplex must know if the problem is a MIP problem or not. This means that for the above problem, the integers/1 call must be before the eplex_solver_setup/1 call.

    This section has introduced the most basic ways to use the eplex library. We will discuss more advanced methods of using the eplex instances in section 11.5.


    next up previous index
    Next: Black-Box Interface Up: EPLEX: The ECLiPSe/CPLEX Interface Previous: Ranged and Typed Variables   Index
    Warwick Harvey
    2002-05-15