11#include <IpIpoptApplication.hpp>
12#include <IpIpoptData.hpp>
18namespace smooth::feedback {
32 inline IpoptNLP(Problem && nlp,
bool use_hessian =
false) : nlp_(std::move(nlp)), use_hessian_(use_hessian) {}
37 inline IpoptNLP(
const Problem & nlp,
bool use_hessian =
false) : nlp_(nlp), use_hessian_(use_hessian) {}
50 Ipopt::Index & nnz_jac_g,
51 Ipopt::Index & nnz_h_lag,
52 IndexStyleEnum & index_style)
override
57 const auto J = nlp_.dg_dx(Eigen::VectorXd::Zero(n));
58 nnz_jac_g = J.nonZeros();
63 H_ = nlp_.d2f_dx2(Eigen::VectorXd::Zero(n));
64 H_ += nlp_.d2g_dx2(Eigen::VectorXd::Zero(n), Eigen::VectorXd::Zero(m));
66 nnz_h_lag = H_.nonZeros();
69 assert(use_hessian_ ==
false);
72 index_style = TNLP::C_STYLE;
81 Ipopt::Index n, Ipopt::Number * x_l, Ipopt::Number * x_u, Ipopt::Index m, Ipopt::Number * g_l, Ipopt::Number * g_u)
84 Eigen::Map<Eigen::VectorXd>(x_l, n) = nlp_.xl().cwiseMax(Eigen::VectorXd::Constant(n, -2e19));
85 Eigen::Map<Eigen::VectorXd>(x_u, n) = nlp_.xu().cwiseMin(Eigen::VectorXd::Constant(n, 2e19));
86 Eigen::Map<Eigen::VectorXd>(g_l, m) = nlp_.gl().cwiseMax(Eigen::VectorXd::Constant(m, -2e19));
87 Eigen::Map<Eigen::VectorXd>(g_u, m) = nlp_.gu().cwiseMin(Eigen::VectorXd::Constant(m, 2e19));
104 Ipopt::Number * lambda)
override
106 if (init_x) { Eigen::Map<Eigen::VectorXd>(x, n) = sol_.
x; }
109 Eigen::Map<Eigen::VectorXd>(z_L, n) = sol_.
zl;
110 Eigen::Map<Eigen::VectorXd>(z_U, n) = sol_.
zu;
113 if (init_lambda) { Eigen::Map<Eigen::VectorXd>(lambda, m) = sol_.
lambda; }
122 eval_f(Ipopt::Index n,
const Ipopt::Number * x, [[maybe_unused]]
bool new_x, Ipopt::Number & obj_value)
override
124 obj_value = nlp_.f(Eigen::Map<const Eigen::VectorXd>(x, n));
132 eval_grad_f(Ipopt::Index n,
const Ipopt::Number * x, [[maybe_unused]]
bool new_x, Ipopt::Number * grad_f)
override
134 const auto & df_dx = nlp_.df_dx(Eigen::Map<const Eigen::VectorXd>(x, n));
135 for (
auto i = 0; i < n; ++i) { grad_f[i] = df_dx.coeff(0, i); }
143 Ipopt::Index n,
const Ipopt::Number * x, [[maybe_unused]]
bool new_x, Ipopt::Index m, Ipopt::Number * g)
override
145 Eigen::Map<Eigen::VectorXd>(g, m) = nlp_.g(Eigen::Map<const Eigen::VectorXd>(x, n));
154 const Ipopt::Number * x,
155 [[maybe_unused]]
bool new_x,
156 [[maybe_unused]] Ipopt::Index m,
157 [[maybe_unused]] Ipopt::Index nele_jac,
160 Ipopt::Number * values)
override
162 if (values == NULL) {
163 const auto & J = nlp_.dg_dx(Eigen::VectorXd::Zero(n));
164 assert(nele_jac == J.nonZeros());
166 for (
auto cntr = 0u, od = 0u; od < J.outerSize(); ++od) {
167 for (Eigen::InnerIterator it(J, od); it; ++it) {
168 iRow[cntr] = it.row();
169 jCol[cntr++] = it.col();
173 const auto & J = nlp_.dg_dx(Eigen::Map<const Eigen::VectorXd>(x, n));
174 assert(nele_jac == J.nonZeros());
176 for (
auto cntr = 0u, od = 0u; od < J.outerSize(); ++od) {
177 for (Eigen::InnerIterator it(J, od); it; ++it) { values[cntr++] = it.value(); }
188 const Ipopt::Number * x,
189 [[maybe_unused]]
bool new_x,
192 const Ipopt::Number * lambda,
193 [[maybe_unused]]
bool new_lambda,
194 [[maybe_unused]] Ipopt::Index nele_hess,
197 Ipopt::Number * values)
override
199 assert(use_hessian_);
201 assert(H_.isCompressed());
202 assert(H_.nonZeros() == nele_hess);
204 H_.coeffs().setZero();
206 if (values == NULL) {
207 for (
auto cntr = 0u, od = 0u; od < H_.outerSize(); ++od) {
208 for (Eigen::InnerIterator it(H_, od); it; ++it) {
210 iRow[cntr] = it.col();
211 jCol[cntr++] = it.row();
215 Eigen::Map<const Eigen::VectorXd> xvar(x, n);
216 Eigen::Map<const Eigen::VectorXd> lvar(lambda, m);
218 H_ += nlp_.d2f_dx2(xvar);
220 H_ += nlp_.d2g_dx2(xvar, lvar);
222 for (
auto cntr = 0u, od = 0u; od < H_.outerSize(); ++od) {
223 for (Eigen::InnerIterator it(H_, od); it; ++it) { values[cntr++] = it.value(); }
234 Ipopt::SolverReturn status,
236 const Ipopt::Number * x,
237 const Ipopt::Number * z_L,
238 const Ipopt::Number * z_U,
240 [[maybe_unused]]
const Ipopt::Number * g,
241 const Ipopt::Number * lambda,
242 Ipopt::Number obj_value,
243 const Ipopt::IpoptData * ip_data,
244 [[maybe_unused]] Ipopt::IpoptCalculatedQuantities * ip_cq)
override
247 case Ipopt::SolverReturn::SUCCESS:
248 sol_.
status = NLPSolution::Status::Optimal;
250 case Ipopt::SolverReturn::STOP_AT_ACCEPTABLE_POINT:
251 sol_.
status = NLPSolution::Status::Optimal;
253 case Ipopt::SolverReturn::MAXITER_EXCEEDED:
254 sol_.
status = NLPSolution::Status::MaxIterations;
256 case Ipopt::SolverReturn::CPUTIME_EXCEEDED:
257 sol_.
status = NLPSolution::Status::MaxTime;
259 case Ipopt::SolverReturn::LOCAL_INFEASIBILITY:
260 sol_.
status = NLPSolution::Status::PrimalInfeasible;
262 case Ipopt::SolverReturn::DIVERGING_ITERATES:
263 sol_.
status = NLPSolution::Status::DualInfeasible;
266 sol_.
status = NLPSolution::Status::Unknown;
270 sol_.
iter = ip_data->iter_count();
272 sol_.
x = Eigen::Map<const Eigen::VectorXd>(x, n);
273 sol_.
zl = Eigen::Map<const Eigen::VectorXd>(z_L, n);
274 sol_.
zu = Eigen::Map<const Eigen::VectorXd>(z_U, n);
275 sol_.
lambda = Eigen::Map<const Eigen::VectorXd>(lambda, m);
284 Eigen::SparseMatrix<double> H_;
300 std::optional<NLPSolution> warmstart = {},
301 std::vector<std::pair<std::string, int>> opts_integer = {},
302 std::vector<std::pair<std::string, std::string>> opts_string = {},
303 std::vector<std::pair<std::string, double>> opts_numeric = {})
305 using nlp_t = std::decay_t<
decltype(nlp)>;
306 bool use_hessian =
false;
307 const auto n = nlp.n();
309 auto it = std::find_if(
310 opts_string.begin(), opts_string.end(), [](
const auto & p) { return p.first ==
"hessian_approximation"; });
311 if (it != opts_string.end() && it->second ==
"exact") { use_hessian =
true; }
313 Ipopt::SmartPtr<IpoptNLP<nlp_t>> ipopt_nlp =
new IpoptNLP<nlp_t>(std::forward<
decltype(nlp)>(nlp), use_hessian);
314 Ipopt::SmartPtr<Ipopt::IpoptApplication> app =
new Ipopt::IpoptApplication();
317 app->Options()->SetStringValue(
"sb",
"yes");
319 if (warmstart.has_value()) {
321 app->Options()->SetStringValue(
"warm_start_init_point",
"yes");
322 ipopt_nlp->sol() = warmstart.value();
325 app->Options()->SetStringValue(
"warm_start_init_point",
"no");
326 ipopt_nlp->sol().x.setZero(n);
330 for (
auto [opt, val] : opts_integer) { app->Options()->SetIntegerValue(opt, val); }
331 for (
auto [opt, val] : opts_string) { app->Options()->SetStringValue(opt, val); }
332 for (
auto [opt, val] : opts_numeric) { app->Options()->SetNumericValue(opt, val); }
335 app->OptimizeTNLP(ipopt_nlp);
337 return ipopt_nlp->sol();
Ipopt interface to solve NLPs.
IpoptNLP(Problem &&nlp, bool use_hessian=false)
Ipopt wrapper for NLP (rvlaue version).
bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g, Ipopt::Index &nnz_h_lag, IndexStyleEnum &index_style) override
IPOPT info overload.
IpoptNLP(const Problem &nlp, bool use_hessian=false)
Ipopt wrapper for NLP (lvalue version).
bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number *x, bool init_z, Ipopt::Number *z_L, Ipopt::Number *z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number *lambda) override
IPOPT initial guess overload.
bool eval_grad_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number *grad_f) override
IPOPT method to define gradient of objective.
bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values) override
IPOPT method to define jacobian of constraint function.
bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number &obj_value) override
IPOPT method to define objective.
bool get_bounds_info(Ipopt::Index n, Ipopt::Number *x_l, Ipopt::Number *x_u, Ipopt::Index m, Ipopt::Number *g_l, Ipopt::Number *g_u) override
IPOPT bounds overload.
bool eval_h(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number sigma, Ipopt::Index m, const Ipopt::Number *lambda, bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values) override
IPOPT method to define problem Hessian.
bool eval_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Number *g) override
IPOPT method to define constraint function.
NLPSolution & sol()
Access solution.
void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n, const Ipopt::Number *x, const Ipopt::Number *z_L, const Ipopt::Number *z_U, Ipopt::Index m, const Ipopt::Number *g, const Ipopt::Number *lambda, Ipopt::Number obj_value, const Ipopt::IpoptData *ip_data, Ipopt::IpoptCalculatedQuantities *ip_cq) override
IPOPT method called after optimization done.
Nonlinear Programming Problem with Hessian information.
Nonlinear Programming Problem.
NLPSolution solve_nlp_ipopt(NLP auto &&nlp, std::optional< NLPSolution > warmstart={}, std::vector< std::pair< std::string, int > > opts_integer={}, std::vector< std::pair< std::string, std::string > > opts_string={}, std::vector< std::pair< std::string, double > > opts_numeric={})
Solve an NLP with the Ipopt solver.
Nonlinear program definition.
Solution to a Nonlinear Programming Problem.
Eigen::VectorXd zl
Inequality multipliers.
double objective
Objective.
Eigen::VectorXd lambda
Constraint multipliers.
Eigen::VectorXd x
Variable values.
Status status
Solver status.
std::size_t iter
Number of iterations.
Eigen::VectorXd zu
Inequality multipliers.