8.1 Accessing the solution¶
This section contains important information about the status of the solver and the status of the solution, which must be checked in order to properly interpret the results of the optimization.
8.1.1 Solver termination¶
If an error occurs during optimization then the method Model.solve will throw an exception of type OptimizeError. The method FusionRuntimeException.toString will produce a description of the error, if available. More about exceptions in Sec. 8.2 (Errors and exceptions).
If a runtime error causes the program to crash during optimization, the first debugging step is to enable logging and check the log output. See Sec. 8.3 (Input/Output).
If the optimization completes successfully, the next step is to check the solution status, as explained below.
8.1.2 Available solutions¶
MOSEK uses three kinds of optimizers and provides three types of solutions:
basic solution:
SolutionType.Basic,interior-point solution:
SolutionType.Interior,integer solution:
SolutionType.Integer.
Which solution(s) are returned depends on the problem type, optimizer selection and additional settings. In the majority of cases only one solution is available.
In Fusion the user may be oblivious to the actual solution type by omitting the type or always referring to SolutionType.Default. This will automatically select the best available solution, if there is more than one. Moreover, the method Model.selectedSolution can be used to fix one solution type for all future references.
Under standard parameters settings each problem type implies a default optimizer and a default output solution:
Type of problem |
Default optimizer |
Default output solution |
Linear problem |
interior-point (with basis identification) |
basic |
Nonlinear continuous problem
(conic, quadratic, SDP)
|
interior-point (conic) |
interior-point |
Mixed-integer problem |
mixed-integer optimizer |
integer |
For nonlinear and mixed-integer problems both the optimizer and output solution are uniquely determined. For linear problems there are more choices.
Settings |
Optimizer |
Output solution |
Parameter to set |
default interior-point |
interior-point |
basic |
|
use any simplex solver |
simplex |
basic |
|
disable basis identification,
need only interior-point solution
|
interior-point |
interior-point |
|
need interior-point and basis sol. |
interior-point |
basic, interior-point |
Not all components of a solution are always available. For example, there is no dual solution for integer problems and no dual conic variables from the simplex optimizer.
8.1.3 Problem and solution status¶
Assuming that the optimization terminated without errors, the next important step is to check the problem and solution status and availability of solutions. There is one for every type of solution, as explained above.
Problem status
Problem status (ProblemStatus, retrieved with Model.getProblemStatus) determines whether the problem is certified as feasible. Its values can roughly be divided into the following broad categories:
feasible — the problem is feasible. For continuous problems and when the solver is run with default parameters, the feasibility status should ideally be
ProblemStatus.PrimalAndDualFeasible.primal/dual infeasible — the problem is infeasible or unbounded or a combination of those. The exact problem status will indicate the type of infeasibility.
unknown — the solver was unable to reach a conclusion, most likely due to numerical issues.
Solution status
Solution status (SolutionStatus, retrieved with Model.getPrimalSolutionStatus and Model.getDualSolutionStatus) provides the information about what the solution values actually contain. The most important broad categories of values are:
optimal (
SolutionStatus.Optimal) — the solution values are feasible and optimal.certificate — the solution is in fact a certificate of infeasibility (primal or dual, depending on the solution).
unknown — the status was not determined, typically because of numerical issues, stall etc. Some solution is available, but its quality is not guaranteed.
undefined — this type of solution is not available at all.
The solution status determines the action to be taken. For example, in some cases a suboptimal solution may still be valuable and deserve attention. It is the user’s responsibility to check the status and quality of the solution.
Typical status reports
Here are the most typical optimization outcomes described in terms of the problem and solution statuses. Note that these do not cover all possible situations that can occur.
Outcome |
Problem status |
Solution status (primal) |
Solution status (dual) |
|---|---|---|---|
Optimal |
|||
Primal infeasible |
|||
Dual infeasible (unbounded) |
|||
Uncertain (stall, numerical issues, etc.) |
Outcome |
Problem status |
Solution status (primal) |
Solution status (dual) |
|---|---|---|---|
Integer optimal |
|||
Infeasible |
|||
Integer feasible point |
|||
No conclusion |
8.1.4 Retrieving solution values¶
After the meaning and quality of the solution (or certificate) have been established, we can query for the actual numerical values. They can be accessed using:
Model.primalObjValue,Model.dualObjValue— the primal and dual objective value.Variable.level— solution values for the variables.Constraint.level— values of the constraint expressions in the current solution.Constraint.dual,Variable.dual— dual values.
Remark
By default only optimal solutions are returned. An attempt to access a solution with an incompatible status will result in an exception. This can be changed by choosing another level of acceptable solutions with the method Model.acceptedSolutionStatus. In particular, this method must be called to enable retrieving suboptimal solutions and infeasibility certificates. For instance, one could write
M->acceptedSolutionStatus(AccSolutionStatus::Feasible);
The current setting of acceptable solutions can be checked with Model.getAcceptedSolutionStatus.
8.1.5 Source code example¶
Below is a source code example with a simple framework for assessing and retrieving the solution to a conic optimization problem.
int main(int arc, char** argv)
{
Model::t M = new Model(); auto _M = finally([&]() { M->dispose(); });
// (Optional) set a log stream
// M->setLogHandler([](const std::string & msg) { std::cout << msg << std::flush; });
// (Optional) uncomment to see what happens when solution status is unknown
// M->setSolverParam("intpntMaxIterations", 1);
// In this example we set up a small conic problem
setupExample(M);
// Optimize
try
{
M->solve();
// We expect solution status OPTIMAL (this is also default)
M->acceptedSolutionStatus(AccSolutionStatus::Optimal);
auto x = M->getVariable("x");
auto xsize = x->getSize();
auto xVal = x->level();
std::cout << "Optimal value of x = ";
for(int i = 0; i < xsize; ++i)
std::cout << (*xVal)[i] << " ";
std::cout << "\nOptimal primal objective: " << M->primalObjValue() <<"\n";
// .. continue analyzing the solution
}
catch (const OptimizeError& e)
{
std::cout << "Optimization failed. Error: " << e.what() << "\n";
}
catch (const SolutionError& e)
{
// The solution with at least the expected status was not available.
// We try to diagnoze why.
std::cout << "Requested solution was not available.\n";
auto prosta = M->getProblemStatus();
switch(prosta)
{
case ProblemStatus::DualInfeasible:
std::cout << "Dual infeasibility certificate found.\n";
break;
case ProblemStatus::PrimalInfeasible:
std::cout << "Primal infeasibility certificate found.\n";
break;
case ProblemStatus::Unknown:
// The solutions status is unknown. The termination code
// indicates why the optimizer terminated prematurely.
std::cout << "The solution status is unknown.\n";
char symname[MSK_MAX_STR_LEN];
char desc[MSK_MAX_STR_LEN];
MSK_getcodedesc((MSKrescodee)(M->getSolverIntInfo("optimizeResponse")), symname, desc);
std::cout << " Termination code: " << symname << " " << desc << "\n";
break;
default:
std::cout << "Another unexpected problem status: " << prosta << "\n";
}
}
catch (const std::exception& e)
{
std::cerr << "Unexpected error: " << e.what() << "\n";
}
M->dispose();
return 0;
}