alpaqa guanaqo
Nonconvex constrained optimization
Loading...
Searching...
No Matches
ipopt-driver.cpp
Go to the documentation of this file.
1#ifdef ALPAQA_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(auto &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 USING_ALPAQA_CONFIG(Problem::config_t);
24
25 // Dimensions
26 length_t n = problem.problem.get_num_variables(),
27 m = problem.problem.get_num_constraints();
28
29 // Initial guess
30 if (auto sz = problem.initial_guess_x.size(); sz != n)
31 throw std::invalid_argument(
32 "Invalid size for initial_guess_x (expected " + std::to_string(n) +
33 ", but got " + std::to_string(sz) + ")");
34 if (auto sz = problem.initial_guess_y.size(); sz != m)
35 throw std::invalid_argument(
36 "Invalid size for initial_guess_y (expected " + std::to_string(m) +
37 ", but got " + std::to_string(sz) + ")");
38 my_nlp->initial_guess = problem.initial_guess_x;
39 my_nlp->initial_guess_multipliers = problem.initial_guess_y;
40 if (auto sz = problem.initial_guess_w.size(); sz > 0) {
41 if (sz != n)
42 throw std::invalid_argument(
43 "Invalid size for initial_guess_w (expected " +
44 std::to_string(n) + ", but got " + std::to_string(sz) + ")");
45 my_nlp->initial_guess_bounds_multipliers = problem.initial_guess_w;
46 }
47
48 // Solve the problem
49 auto t0 = std::chrono::steady_clock::now();
50 auto status = solver->OptimizeTNLP(nlp);
51 auto t1 = std::chrono::steady_clock::now();
52
53 // Solve the problems again to average runtimes
54 using ns = std::chrono::nanoseconds;
55 auto avg_duration = duration_cast<ns>(t1 - t0);
56 os.setstate(std::ios_base::badbit);
57 for (unsigned i = 0; i < N_exp; ++i) {
58 my_nlp->initial_guess = problem.initial_guess_x;
59 my_nlp->initial_guess_multipliers = problem.initial_guess_y;
60 my_nlp->initial_guess_bounds_multipliers = problem.initial_guess_w;
61
62 auto t0 = std::chrono::steady_clock::now();
63 solver->OptimizeTNLP(nlp);
64 auto t1 = std::chrono::steady_clock::now();
65 avg_duration += duration_cast<ns>(t1 - t0);
66 }
67 os.clear();
68 avg_duration /= (N_exp + 1);
69 auto evals = *problem.evaluations;
70
71 // Results
72 auto &nlp_res = my_nlp->results;
73 if (nlp_res.status == Ipopt::SolverReturn::UNASSIGNED) {
74 nlp_res.solution_x = vec::Constant(n, alpaqa::NaN<config_t>);
75 nlp_res.solution_y = vec::Constant(m, alpaqa::NaN<config_t>);
76 nlp_res.solution_z_L = vec::Constant(n, alpaqa::NaN<config_t>);
77 nlp_res.solution_z_U = vec::Constant(n, alpaqa::NaN<config_t>);
78 }
79 SolverResults results{
80 .status = std::string(enum_name(status)),
81 .success = status == Ipopt::Solve_Succeeded,
82 .evals = evals,
83 .duration = avg_duration,
84 .solver = "Ipopt",
85 .h = 0,
86 .δ = nlp_res.infeasibility,
87 .ε = nlp_res.nlp_error,
88 .γ = 0,
89 .Σ = 0,
90 .solution = nlp_res.solution_x,
91 .multipliers = nlp_res.solution_y,
92 .multipliers_bounds = nlp_res.combine_bounds_multipliers(),
93 .penalties = vec::Zero(n),
94 .outer_iter = nlp_res.iter_count,
95 .inner_iter = nlp_res.iter_count,
96 .extra = {},
97 };
98 return results;
99}
100
101auto make_ipopt_solver(Options &opts) {
102 using namespace Ipopt;
103
104 // We are using the factory, since this allows us to compile this
105 // example with an Ipopt Windows DLL
106 SmartPtr<IpoptApplication> app = IpoptApplicationFactory();
107 app->RethrowNonIpoptException(true);
108
109 app->Options()->SetNumericValue("tol", 1e-8);
110 app->Options()->SetNumericValue("constr_viol_tol", 1e-8);
111 app->Options()->SetStringValue("linear_solver", "mumps");
112 // app->Options()->SetStringValue("print_timing_statistics", "yes");
113 // app->Options()->SetStringValue("timing_statistics", "yes");
114 app->Options()->SetStringValue("hessian_approximation", "exact");
115
116 set_params(*app, "solver", opts);
117
118 // Initialize the IpoptApplication and process the options
119 ApplicationReturnStatus status = app->Initialize();
120 if (status != Solve_Succeeded)
121 throw std::runtime_error("Error during Ipopt initialization: " +
122 std::string(enum_name(status)));
123
124 return app;
125}
126
127template <class LoadedProblem>
128SharedSolverWrapper make_ipopt_drive_impl(std::string_view direction,
129 Options &opts) {
130 if (!direction.empty())
131 throw std::invalid_argument(
132 "Ipopt solver does not support any directions");
133 auto solver = make_ipopt_solver(opts);
134 unsigned N_exp = 0;
135 set_params(N_exp, "num_exp", opts);
136 return std::make_shared<SolverWrapper>(
137 [solver{std::move(solver)}, N_exp](
138 LoadedProblem &problem, std::ostream &os) mutable -> SolverResults {
139 return run_ipopt_solver(problem, solver, os, N_exp);
140 });
141}
142
143} // namespace
144
145SharedSolverWrapper make_ipopt_driver(std::string_view direction,
146 Options &opts) {
147 static constexpr bool valid_config =
148 std::is_same_v<LoadedProblem::config_t, alpaqa::IpoptAdapter::config_t>;
149 if constexpr (valid_config)
150 return make_ipopt_drive_impl<LoadedProblem>(direction, opts);
151 else
152 throw std::invalid_argument(
153 "Ipopt solver only supports double precision");
154}
155
156#else
157
158#include "solver-driver.hpp"
159
161 throw std::invalid_argument(
162 "This version of alpaqa was compiled without Ipopt support.");
163}
164
165#endif
Based on https://coin-or.github.io/Ipopt/INTERFACES.html.
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
SharedSolverWrapper make_ipopt_driver(std::string_view, Options &)
std::string_view enum_name(Ipopt::ApplicationReturnStatus s)
constexpr const auto NaN
Definition config.hpp:114
typename Conf::length_t length_t
Definition config.hpp:103
void set_params(T &t, std::string_view prefix, Options &opts)
Definition options.hpp:126
std::shared_ptr< SolverWrapper > SharedSolverWrapper