An eplex instance represents a single MP problem in a module. Constraints for the problem are posted to the module. The problem is solved with respect to an objective function.
Figure 16.3: Eplex Instance
:- lib(eplex). :- eplex_instance(prob). % a. declare an eplex instance main1(Cost, Vars) :- % b. create the problem variables and set their range Vars = [A1,A2,A3,B1,B2,B3,C1,C2,C3,D1,D2,D3], prob: (Vars $:: 0.0..1.0Inf), % c. post the constraints for the problem to the eplex instance prob: (A1 + A2 + A3 $= 21), prob: (B1 + B2 + B3 $= 40), prob: (C1 + C2 + C3 $= 34), prob: (D1 + D2 + D3 $= 10), prob: (A1 + B1 + C1 + D1 $=< 50), prob: (A2 + B2 + C2 + D2 $=< 30), prob: (A3 + B3 + C3 + D3 $=< 40), % d. set up the external solver with the objective function prob: eplex_solver_setup(min( 10*A1 + 7*A2 + 200*A3 + 8*B1 + 5*B2 + 10*B3 + 5*C1 + 5*C2 + 8*C3 + 9*D1 + 3*D2 + 7*D3)), %——————————- End of Modelling code prob: eplex_solve(Cost). % e. Solve problem using external solver |
eplex_instance/1
.
This is usually done with a directive, as in line a
.
Once
declared, an eplex instance can be referred to using its name like a module
qualifier.$::/2
.
|
$=/2
, $=</2
and $>=/2
.
|
eplex_solver_setup/1
, with the objective
function given as the argument, enclosed by either min(...)
or max(...)
. In this case, we are minimising.
Note that
generally the setup of the solver and the posting of the MP constraints can be
done in any order.eplex_solve/1
in line e
. Note that the problem variables are not instantiated by the solver. However, the `solution' values, i.e. the values that the variable are given by the solver, are available in the eplex attribute. The eplex attribute is shown as?- main1(Cost, Vars). Cost = 710.0 Vars = [A1{0.0 .. 1e+20 @ 0.0}, A2{0.0 .. 1e+20 @ 21.0}, ....]
Lo..Hi @ Sol
where
Lo
is the lower bound, Hi
the upper bound, and Sol
the
solution value for the variable (e.g., A2
has the solution value of
21.0 in the example above). Note also that the external solver may not
allow very large floats, hence 1e+20
, this external solver's
representation of infinity, is the upper bound of the
variables, even though we specified 1.0Inf
in our code. eplex_var_get/3
. The example program in the
previous section can be modified to return the solution values:
main2(Cost, Vars) :- .... % same as previous example up to line e prob: eplex_solve(Cost), % e. Solve problem using external solver (foreach(V, Vars) do % f. set the problem variables to their solution values prob: eplex_var_get(V, typed_solution, V) ). |
f
, eplex_var_get/3
is used to obtain the solution
value for a problem variable. The second argument, set to typed_solution
,
specifies that we want the solution value for the variable to be returned.
Here, we instantiate the problem variable itself to the solution value
with the third argument:
Note that, in general, an MP problem can have many optimal solutions, i.e. different solutions which give the optimal value for the objective function. As a result, the above instantiations for?- main2(Cost, Vars). Cost = 710.0 Vars = [0.0, 21.0, 0.0, 16.0, 9.0, 15.0, 34.0, 0.0, 0.0, 0.0, 0.0, 10.0]
Vars
might not be what is returned by the solver
used.integers/1
constraint on the
variables.
:- lib(eplex). :- eplex_instance(prob). main3(Cost, Vars) :- Vars = [A1,A2,A3,B1,B2,B3,C1,C2,C3,D1,D2,D3], prob: (Vars $:: 0.0..1.0Inf), prob: (A1 + A2 + A3 $= 21), prob: (B1 + B2 + B3 $= 40), prob: (C1 + C2 + C3 $= 34), prob: (D1 + D2 + D3 $= 10), prob: (A1 + B1 + C1 + D1 $=< 50), prob: (A2 + B2 + C2 + D2 $=< 30), prob: (A3 + B3 + C3 + D3 $=< 40), prob: eplex_solver_setup(min( 10*A1 + 7*A2 + 200*A3 + 8*B1 + 5*B2 + 10*B3 + 5*C1 + 5*C2 + 8*C3 + 9*D1 + 3*D2 + 7*D3)), prob: (A1 $= A2), % g. the new constraint, added after setup %——————————- End of Modelling code prob: eplex_solve(Cost), (foreach(V, Vars) do prob: eplex_var_get(V, typed_solution, V) ). |
g
is imposed after the
solver setup. In fact it can be imposed anytime before
eplex_solve(Cost)
is called.Cost
of 710, the same as the original
problem. However, the solution values are not integral:
Now, to impose the constraints that only whole units of the products can be transported, we modify the program as follows:?- main3(Cost, Vars). Cost = 710.0 Vars = [10.5, 10.5, 0.0, 5.5, 19.5, 15.0, 34.0, 0.0, 0.0, 0.0, 0.0, 10.0]
main4(Cost, Vars) :- Vars = [A1,A2,A3,B1,B2,B3,C1,C2,C3,D1,D2,D3], prob: (Vars $:: 0.0..1.0Inf), prob: integers(Vars), % h. impose the integrality constraint ....% Rest is the same as main3 |
h
, we added the integers/1
constraint. This imposes
the integrality constraint on Vars
for the eplex instance
prob
. Now,
the external solver will only assign integer solution values to the
variables in the list.
|
In this case,?- main4(Cost,Vars). Cost = 898.0 Vars = [10, 10, 1, 6, 20, 14, 34, 0, 0, 0, 0, 10]
A1
and A2
are now integers. In fact, notice
that all the values returned are now integers rather than floats. This is
because the typed_solution
option of eplex_var_get/3
returns the solution values taking into account if the variables have been
declared as integers for the eplex instance.
|
- Declare an eplex instance using eplex_instance(+Instance).
- Post the constraints ($=/2, $>=/2, $=</2, integers/1, $::/2) for the problem to the eplex instance.
- Setup the solver with the objective function using
Instance: eplex_solver_setup(+ObjFunc).
Figure 16.4: Modelling an MP Problem