JuMP Interface
Our JuMP interface allows you to describe and modify your optimisation problem with JuMP and use COSMO as the backend solver. The interface is defined in /src/MOIWrapper.jl
.
We assume here that the latest JuMP release (~0.21.0
) is used.
Use COSMO
To specify COSMO as the solver for your JuMP model, load the solver module with using COSMO
and then pass a COSMO.Optimizer
when initialising the JuMP model:
m = JuMP.Model(COSMO.Optimizer);
Specify Solver Settings
Solver-specific settings can be passed using the optimizer_with_attributes()
function. For example, if you want to adjust the maximum number of iterations and turn on verbose printing use
m = JuMP.Model(optimizer_with_attributes(COSMO.Optimizer, "max_iter" => 5000, "verbose" => true));
Note that the attributes are passed as key-value pairs and the keys are strings. This is slightly different to using the native COSMO interface. Equivalently, one can also use:
m = JuMP.Model(COSMO.Optimizer);
set_optimizer_attribute(m, "max_iter", 5000)
set_optimizer_attribute(m, "verbose", true)
The full list of available settings can be found in the Settings section. All of them should be compatible with JuMP.
Results
After solving the problem the result can be obtained using the standard JuMP commands. To see if the optimisation was successful use
JuMP.termination_status(m)
JuMP.primal_status(m)
If a solution is available, the optimal objective value can be queried using
JuMP.objective_value(m)
and the value of a decision variable x
can be obtained with
JuMP.value.(x)
To query the number of iterations of the algorithm use:
iter = MOI.get(m, COSMO.ADMMIterations())
Feasibility of solution
In case JuMP.termination_status(m)
is MOI.ITERATION_LIMIT
, JuMP.primal_status(m)
(resp. JuMP.dual_status(m)
) should be checked to determine the feasibility status of the primal (resp. dual) solutions. A primal (resp. dual) solution is considered feasible (resported as MOI.FEASIBLE_POINT
) if
\[r < \epsilon_{\text{abs}} + s\epsilon_{\text{rel}}\]
where
- $r$ is the primal (resp. dual) residual
r_prim
$= \lVert Ax + s - b \rVert_\infty$
(resp. r_dual
$= \lVert P x + q - A^\top * μ \rVert_\infty$),
- $s$ is the primal (resp. dual) scaling of this residual
max_norm_prim
$= \max(\lVert Ax \rVert_\infty, \lVert s \rVert_\infty, \lVert b \rVert_\infty)$ (resp. max_norm_dual
$= \max(\lVert P x \rVert_\infty, \lVert q \rVert_\infty, \lVert A^\top * μ \rVert_\infty)$).
- $\epsilon_{\text{abs}}$ is the optimizer attribute
eps_abs
and - $\epsilon_{\text{rel}}$ is the optimizer attribute
eps_rel
.
A primal (resp. dual solution) is considered nearly feasible (resported as MOI.NEARLY_FEASIBLE_POINT
) if it is not feasible but the same inequality is satisfied where the right-hand side is multiplied by the optimizer attribute nearly_ratio
.
Otherwise, the solution is considered infeasible (resported as MOI.INFEASIBLE_POINT
). Note that this does not mean that the problem is infeasible, it only means that the current primal (resp. dual) solution is infeasible.
Note that, in case JuMP.termination_status(m)
is MOI.ITERATION_LIMIT
, the feasibility of the solution is checked with the current optimizer attributes, eps_abs
, eps_rel
and nearly_ratio
. This means that if they are changed after optimize!
, the status may change! This can be used to check whether the solution is feasible up to a relaxed tolerance. For instance, the following could happen:
optimize!(m)
primal_status(m) # MOI.INFEASIBLE_POINT
# This means that `r_prim >= nearly_ratio * (eps_abs * max_norm_prim * eps_rel)`
set_optimizer_attribute(m, "eps_abs", 1e-4)
primal_status(m) # MOI.NEARLY_FEASIBLE_POINT
# This means that `1 <= r_prim / (eps_abs * max_norm_prim * eps_rel) < nearly_ratio`
The values of r_prim
, max_norm_prim
, r_dual
and max_norm_dual
can also be accessed as the fields of the res_info
struct that can be obtained as follows
res_info = MOI.get(m, COSMO.RawResult()).info
Then, the feasibility can either be checked manually as in
res_info.r_prim < eps_abs + res_info.max_norm_prim * eps_rel
or using one of the following:
COSMO.is_primal_feasible(res_info, eps_abs, eps_rel)
COSMO.is_primal_nearly_feasible(res_info, eps_abs, eps_rel)
COSMO.is_dual_feasible(res_info, eps_abs, eps_rel)
COSMO.is_dual_nearly_feasible(res_info, eps_abs, eps_rel)
For more information on how to use JuMP check the JuMP documentation.