Subsection 6.3.1 Finding Dual Values and Certificates using Julia
First, letβs examine LOP in
(6.2.1) using Julia.
m = Model(HiGHS.Optimizer)
@variable(m, xβ β₯ 0)
@variable(m, xβ β₯ 0)
@variable(m, xβ β₯ 0)
@objective(m, Max, 3xβ + 4xβ + 7xβ)
@constraint(m, c1, xβ + 2xβ + 3xβ β€ 12)
@constraint(m, c2, 4xβ + 3xβ + 2xβ β€ 18)
print(m)
And as we will see below, including the terms
c1 and
c2 in the constraints is important. The result of this is the LOP as expected.
Max 3 xβ + 4 xβ + 7 xβ
Subject to
c1 : xβ + 2 xβ + 3 xβ β€ 12
c2 : 4 xβ + 3 xβ + 2 xβ β€ 18
xβ β₯ 0
xβ β₯ 0
xβ β₯ 0
And then solving this with the following:
set_silent(m)
optimize!(m)
is_solved_and_feasible(m)
The results is
true and finding the optimal values of the variables and objective with:
value(xβ), value(xβ), value(xβ)
results in
\((3.0, 0.0, 3.0)\text{,}\) and for the objective use
objective_value(m) which results in
\(30.0\text{.}\)
Since we have also used constants
c1 and
c2 in the constraints, we can now get the values of these as well with
value(c1) and
value(c2). These return
12.0 and
18.0 respectively. These are the values of the left-hand sides of the constraints at optimality also since they equal the right-hand sides, the slack variables for each of these are 0.0.
As we saw in
SectionΒ 6.2, the solution to the dual is in the objective row of the optimal tableau. However, it doesnβt appear that we have the tableau. Letβs first solve the dual problem from
(6.2.3) explicitly with the following:
md = Model(HiGHS.Optimizer)
@variable(md, yβ β₯ 0)
@variable(md, yβ β₯ 0)
@objective(md, Min, 12yβ + 18yβ)
@constraint(md, c1, yβ + 4yβ β₯ 3)
@constraint(md, c2, 2yβ + 3yβ β₯ 4)
@constraint(md, c3, 3yβ + 2yβ β₯ 7)
print(md)
Min 12 yβ + 18 yβ
Subject to
c1 : yβ + 4 yβ β₯ 3
c2 : 2 yβ + 3 yβ β₯ 4
c3 : 3 yβ + 2 yβ β₯ 7
yβ β₯ 0
yβ β₯ 0
And then solving this with the following:
set_silent(md)
optimize!(md)
is_solved_and_feasible(md)
The results is
true and finding the optimal values of the variables and objective with:
results in
\((2.1999999999999997, 0.2)\text{,}\) and for the objective use
objective_value(md) which results in
\(30.0\text{,}\) which is the same value as that of the primal problem. (Yes, the strong duality theorem comes through.) Again, we can find the values of the constraints with
value(c1),
value(c2), and
value(c3), which return
3.0,
5.0, and
7.0 respectively. Since the first and third values are the same as the right-hand sides, the slack variables for these constraints are 0.0, while the second constraint has a slack variable value of 1.0.
It seems unnecessary to have to solved the dual problem in light of the work in
SectionΒ 6.2 and its true that we donβt need to. If instead, we enter the following code:
The result is
(-2.2, -0.19999999999999973). The
dual function returns the value of the slack variable coefficients in the objective function written in dictionary form. That is, returning to the final tableau in
(6.2.2), the objective row can be written as
\begin{equation*}
\begin{aligned} 10 z \amp = 300 - 10 x_2 - 22x_4 - 2x_5 \amp\amp \\ \amp\amp\amp\text{or} \\ z \amp = 30 - x_2 - \frac{22}{10} x_4 - \frac{2}{10} x_5 \end{aligned}
\end{equation*}
The values of the dual variables using Julia results in the coefficients of the slack variables that correspond to the two constraints and in fact the result of the
dual command is the negative of the values of the dual variables found directly.
You may notice that
dual gave the coefficients of
\(x_4\) and
\(x_5\text{,}\) but not
\(x_2\text{.}\) We need a little more work for that.
Subsection 6.3.3 Finding a basis using Julia
The primal and dual example that we saw above showed that we can solve the primal problem and get the values of the dual problem. However, another useful part of the solution that we have seen over the first few chapters of this book is the basis of the solution.
Although the basis is important in the simplex method, often packages like
HiGHS may not use the simplex tableau as we have in this book. We provide a possible alternative in
ChapterΒ 8. However, with some work, we can get the basis out of the solution using
JuMP and
HiGHS.
The following code will show which of the original variables are basic:
[
xi => get_attribute(xi, MOI.VariableBasisStatus()) for
xi in all_variables(m)
]
and for the Primal problem above (this is the
m model), this results in
3-element Vector{Pair{VariableRef, MathOptInterface.BasisStatusCode}}:
xβ => MathOptInterface.BASIC
xβ => MathOptInterface.NONBASIC_AT_LOWER
xβ => MathOptInterface.BASIC
which shows that
\(x_1\) and
\(x_3\) are basic and
\(x_2\) is non-basic. The
NONBASIC_AT_LOWER indicates that the variable is basic and is at its lower bound of zero.
Similarly, we can get the basis status of the constraints, which correspond to the dual variables, with the following code:
[ name(c) => MOI.get(m, MOI.ConstraintBasisStatus(), c)
for c in all_constraints(m, include_variable_in_set_constraints = false)]
2-element Vector{Pair{String, MathOptInterface.BasisStatusCode}}:
"xβ" => MathOptInterface.NONBASIC
"xβ
" => MathOptInterface.NONBASIC
Indicating that both constraints are non-basic.