alpaqa 1.0.0a8
Nonconvex constrained optimization
Loading...
Searching...
No Matches
ipopt-driver.cpp
Go to the documentation of this file.
1#ifdef WITH_IPOPT
2
5#include <IpIpoptApplication.hpp>
6
7#include <stdexcept>
8#include <string>
9
10#include "results.hpp"
11#include "solver-driver.hpp"
12
13namespace {
14
15SolverResults run_ipopt_solver(LoadedProblem &problem,
16 Ipopt::SmartPtr<Ipopt::IpoptApplication> &solver,
17 std::ostream &os, unsigned N_exp) {
18 // Ipopt problem adapter
19 using Problem = alpaqa::IpoptAdapter;
20 Ipopt::SmartPtr<Ipopt::TNLP> nlp = new Problem(problem.problem);
21 auto *my_nlp = dynamic_cast<Problem *>(GetRawPtr(nlp));
22
23 // Initial guess
24 if (auto sz = problem.initial_guess_x.size(); sz != problem.problem.get_n())
25 throw std::invalid_argument(
26 "Invalid size for initial_guess_x (got " + std::to_string(sz) +
27 ", expected " + std::to_string(problem.problem.get_n()) + ")");
28 if (auto sz = problem.initial_guess_y.size(); sz != problem.problem.get_m())
29 throw std::invalid_argument(
30 "Invalid size for initial_guess_y (got " + std::to_string(sz) +
31 ", expected " + std::to_string(problem.problem.get_m()) + ")");
32 my_nlp->initial_guess = problem.initial_guess_x;
33 my_nlp->initial_guess_multipliers = problem.initial_guess_y;
34 if (auto sz = problem.initial_guess_w.size(); sz > 0) {
35 if (sz != problem.problem.get_n() * 2)
36 throw std::invalid_argument(
37 "Invalid size for initial_guess_w (got " + std::to_string(sz) +
38 ", expected " + std::to_string(problem.problem.get_n() * 2) +
39 ")");
40 my_nlp->initial_guess_bounds_multipliers_l =
41 problem.initial_guess_w.bottomRows(problem.problem.get_n());
42 my_nlp->initial_guess_bounds_multipliers_u =
43 problem.initial_guess_w.topRows(problem.problem.get_n());
44 }
45
46 // Solve the problem
47 auto t0 = std::chrono::steady_clock::now();
48 auto status = solver->OptimizeTNLP(nlp);
49 auto t1 = std::chrono::steady_clock::now();
50 auto evals = *problem.evaluations;
51
52 // Solve the problems again to average runtimes
53 using ns = std::chrono::nanoseconds;
54 auto avg_duration = duration_cast<ns>(t1 - t0);
55 os.setstate(std::ios_base::badbit);
56 for (unsigned i = 0; i < N_exp; ++i) {
57 my_nlp->initial_guess = problem.initial_guess_x;
58 my_nlp->initial_guess_multipliers = problem.initial_guess_y;
59
60 auto t0 = std::chrono::steady_clock::now();
61 solver->OptimizeTNLP(nlp);
62 auto t1 = std::chrono::steady_clock::now();
63 avg_duration += duration_cast<ns>(t1 - t0);
64 }
65 os.clear();
66 avg_duration /= (N_exp + 1);
67
68 // Results
69 auto &nlp_res = my_nlp->results;
70 SolverResults results{
71 .status = enum_name(status),
72 .success = status == Ipopt::Solve_Succeeded,
73 .evals = evals,
74 .duration = avg_duration,
75 .solver = "Ipopt",
76 .h = 0,
77 .δ = nlp_res.infeasibility,
78 .ε = nlp_res.nlp_error,
79 .γ = 0,
80 .Σ = 0,
81 .solution = nlp_res.solution_x,
82 .multipliers = nlp_res.solution_y,
83 .multipliers_bounds = Problem::vec(problem.problem.get_n() * 2),
84 .outer_iter = nlp_res.iter_count,
85 .inner_iter = nlp_res.iter_count,
86 .extra = {},
87 };
88 results.multipliers_bounds << nlp_res.solution_z_L, nlp_res.solution_z_U;
89 return results;
90}
91
92auto make_ipopt_solver(Options &opts) {
93 using namespace Ipopt;
94
95 // We are using the factory, since this allows us to compile this
96 // example with an Ipopt Windows DLL
97 SmartPtr<IpoptApplication> app = IpoptApplicationFactory();
98 app->RethrowNonIpoptException(true);
99
100 app->Options()->SetNumericValue("tol", 1e-8);
101 app->Options()->SetNumericValue("constr_viol_tol", 1e-8);
102 app->Options()->SetStringValue("linear_solver", "mumps");
103 // app->Options()->SetStringValue("print_timing_statistics", "yes");
104 // app->Options()->SetStringValue("timing_statistics", "yes");
105 app->Options()->SetStringValue("hessian_approximation", "exact");
106
107 set_params(*app, "solver", opts);
108
109 // Initialize the IpoptApplication and process the options
110 ApplicationReturnStatus status = app->Initialize();
111 if (status != Solve_Succeeded)
112 throw std::runtime_error("Error during Ipopt initialization: " +
113 std::string(enum_name(status)));
114
115 return app;
116}
117
118} // namespace
119
120solver_func_t make_ipopt_driver(std::string_view direction, Options &opts) {
121 if (!direction.empty())
122 throw std::invalid_argument(
123 "Ipopt solver does not support any directions");
124 auto solver = make_ipopt_solver(opts);
125 unsigned N_exp = 0;
126 set_params(N_exp, "num_exp", opts);
127 return [solver{std::move(solver)},
128 N_exp](LoadedProblem &problem,
129 std::ostream &os) mutable -> SolverResults {
130 return run_ipopt_solver(problem, solver, os, N_exp);
131 };
132}
133
134#else
135
136#include "solver-driver.hpp"
137
139 throw std::invalid_argument(
140 "This version of alpaqa was compiled without Ipopt support.");
141}
142
143#endif
Based on https://coin-or.github.io/Ipopt/INTERFACES.html.
length_t get_n() const
[Required] Number of decision variables.
length_t get_m() const
[Required] Number of constraints.
solver_func_t make_ipopt_driver(std::string_view, Options &)
std::string_view enum_name(Ipopt::ApplicationReturnStatus s)
Definition: ipopt-enums.hpp:6
@ Ipopt
The stopping criterion used by Ipopt, see https://link.springer.com/article/10.1007/s10107-004-0559-y...
decltype(auto) set_params(T &t, std::string_view prefix, Options &opts)
Definition: options.hpp:29
vec initial_guess_y
Unknowns.
Definition: problem.hpp:20
vec initial_guess_x
Definition: problem.hpp:19
vec initial_guess_w
Multipliers g.
Definition: problem.hpp:21
alpaqa::TypeErasedProblem< config_t > problem
Definition: problem.hpp:15
std::shared_ptr< alpaqa::EvalCounter > evaluations
Definition: problem.hpp:18
std::function< solver_free_func_t > solver_func_t
std::string_view status
Definition: results.hpp:24