ipopt_nlp.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "ipopt_nlp.hpp"
27 #include "ipopt_interface.hpp"
28 #include "casadi/core/timing.hpp"
29 #include "casadi/core/convexify.hpp"
30 
31 namespace casadi {
32 
34  : solver_(solver), mem_(mem) {
35  n_ = solver_.nx_;
36  m_ = solver_.ng_;
37 
38 #ifdef WITH_IPOPT_CALLBACK
39  x_ = new double[n_];
40  g_ = new double[m_];
41  z_L_ = new double[n_];
42  z_U_ = new double[n_];
43  lambda_ = new double[m_];
44 #endif // WITH_IPOPT_CALLBACK
45 
46  }
47 
49 #ifdef WITH_IPOPT_CALLBACK
50  delete [] x_;
51  delete [] g_;
52  delete [] z_U_;
53  delete [] z_L_;
54  delete [] lambda_;
55 #endif // WITH_IPOPT_CALLBACK
56  }
57 
58  // returns the size of the problem
59  bool IpoptUserClass::get_nlp_info(Index& n, Index& m, Index& nnz_jac_g,
60  Index& nnz_h_lag, IndexStyleEnum& index_style) {
61  solver_.get_nlp_info(mem_, n, m, nnz_jac_g, nnz_h_lag);
62 
63  // use the C style indexing (0-based)
64  index_style = TNLP::C_STYLE;
65 
66  return true;
67  }
68 
69  // returns the variable bounds
70  bool IpoptUserClass::get_bounds_info(Index n, Number* x_l, Number* x_u,
71  Index m, Number* g_l, Number* g_u) {
72  casadi_assert_dev(n==solver_.nx_);
73  casadi_assert_dev(m==solver_.ng_);
74  return solver_.get_bounds_info(mem_, x_l, x_u, g_l, g_u);
75  }
76 
77  // returns the initial point for the problem
78  bool IpoptUserClass::get_starting_point(Index n, bool init_x, Number* x,
79  bool init_z, Number* z_L, Number* z_U,
80  Index m, bool init_lambda,
81  Number* lambda) {
82  casadi_assert_dev(n==solver_.nx_);
83  casadi_assert_dev(m==solver_.ng_);
84  return solver_.get_starting_point(mem_, init_x, x, init_z, z_L, z_U, init_lambda, lambda);
85  }
86 
87  // returns the value of the objective function
88  bool IpoptUserClass::eval_f(Index n, const Number* x, bool new_x, Number& obj_value) {
89  mem_->arg[0] = x;
90  mem_->arg[1] = mem_->d_nlp.p;
91  mem_->res[0] = &obj_value;
92  try {
93  return solver_.calc_function(mem_, "nlp_f")==0;
94  } catch(KeyboardInterruptException& ex) {
95  casadi_warning("KeyboardInterruptException");
97  } catch (std::exception& ex) {
98  if (solver_.show_eval_warnings_) {
99  casadi_warning("IpoptUserClass::eval_f failed:" + std::string(ex.what()));
100  }
101  return false;
102  }
103  }
104 
105  // return the gradient of the objective function grad_ {x} f(x)
106  bool IpoptUserClass::eval_grad_f(Index n, const Number* x, bool new_x, Number* grad_f) {
107  mem_->arg[0] = x;
108  mem_->arg[1] = mem_->d_nlp.p;
109  mem_->res[0] = nullptr;
110  mem_->res[1] = grad_f;
111  try {
112  return solver_.calc_function(mem_, "nlp_grad_f")==0;
113  } catch(KeyboardInterruptException& ex) {
114  casadi_warning("KeyboardInterruptException");
116  } catch (std::exception& ex) {
117  if (solver_.show_eval_warnings_) {
118  casadi_warning("IpoptUserClass::eval_grad_f failed:" + std::string(ex.what()));
119  }
120  return false;
121  }
122  }
123 
124  // return the value of the constraints: g(x)
125  bool IpoptUserClass::eval_g(Index n, const Number* x, bool new_x, Index m, Number* g) {
126  mem_->arg[0] = x;
127  mem_->arg[1] = mem_->d_nlp.p;
128  mem_->res[0] = g;
129  try {
130  return solver_.calc_function(mem_, "nlp_g")==0;
131  } catch(KeyboardInterruptException& ex) {
132  casadi_warning("KeyboardInterruptException");
134  } catch (std::exception& ex) {
135  if (solver_.show_eval_warnings_) {
136  casadi_warning("IpoptUserClass::eval_g failed:" + std::string(ex.what()));
137  }
138  return false;
139  }
140  }
141 
142  // return the structure or values of the jacobian
143  bool IpoptUserClass::eval_jac_g(Index n, const Number* x, bool new_x,
144  Index m, Index nele_jac, Index* iRow, Index *jCol,
145  Number* values) {
146  if (values) {
147  // Evaluate numerically
148  mem_->arg[0] = x;
149  mem_->arg[1] = mem_->d_nlp.p;
150  mem_->res[0] = nullptr;
151  mem_->res[1] = values;
152  try {
153  return solver_.calc_function(mem_, "nlp_jac_g")==0;
154  } catch(KeyboardInterruptException& ex) {
155  casadi_warning("KeyboardInterruptException");
157  } catch (std::exception& ex) {
158  if (solver_.show_eval_warnings_) {
159  casadi_warning("IpoptUserClass::eval_jac_g failed:" + std::string(ex.what()));
160  }
161  return false;
162  }
163  } else {
164  // Get the sparsity pattern
165  casadi_int ncol = solver_.jacg_sp_.size2();
166  const casadi_int* colind = solver_.jacg_sp_.colind();
167  const casadi_int* row = solver_.jacg_sp_.row();
168  if (nele_jac!=colind[ncol]) return false; // consistency check
169 
170  // Pass to IPOPT
171  for (casadi_int cc=0; cc<ncol; ++cc) {
172  for (casadi_int el=colind[cc]; el<colind[cc+1]; ++el) {
173  *iRow++ = row[el];
174  *jCol++ = cc;
175  }
176  }
177  return true;
178  }
179  }
180 
181 
182  bool IpoptUserClass::eval_h(Index n, const Number* x, bool new_x,
183  Number obj_factor, Index m, const Number* lambda,
184  bool new_lambda, Index nele_hess, Index* iRow,
185  Index* jCol, Number* values) {
186  if (values) {
187  // Evaluate numerically
188  mem_->arg[0] = x;
189  mem_->arg[1] = mem_->d_nlp.p;
190  mem_->arg[2] = &obj_factor;
191  mem_->arg[3] = lambda;
192  mem_->res[0] = values;
193  try {
194  if (solver_.calc_function(mem_, "nlp_hess_l")) return false;
195  } catch(KeyboardInterruptException& ex) {
196  casadi_warning("KeyboardInterruptException");
198  } catch (std::exception& ex) {
199  if (solver_.show_eval_warnings_) {
200  casadi_warning("IpoptUserClass::eval_h failed:" + std::string(ex.what()));
201  }
202  return false;
203  }
204  if (solver_.convexify_) {
205  ScopedTiming tic(mem_->fstats.at("convexify"));
206  if (convexify_eval(&solver_.convexify_data_.config, values, values, mem_->iw, mem_->w)) {
207  return false;
208  }
209  }
210  return true;
211  } else {
212  // Get the sparsity pattern
213  casadi_int ncol = solver_.hesslag_sp_.size2();
214  const casadi_int* colind = solver_.hesslag_sp_.colind();
215  const casadi_int* row = solver_.hesslag_sp_.row();
216 
217  // Pass to IPOPT
218  for (casadi_int cc=0; cc<ncol; ++cc) {
219  for (casadi_int el=colind[cc]; el<colind[cc+1]; ++el) {
220  *iRow++ = row[el];
221  *jCol++ = cc;
222  }
223  }
224  return true;
225  }
226  }
227 
228 
229  void IpoptUserClass::finalize_solution(SolverReturn status, Index n, const Number* x,
230  const Number* z_L, const Number* z_U,
231  Index m, const Number* g, const Number* lambda,
232  Number obj_value,
233  const IpoptData* ip_data,
234  IpoptCalculatedQuantities* ip_cq) {
235  solver_.finalize_solution(mem_, x, z_L, z_U, g, lambda, obj_value,
236  ip_data? ip_data->iter_count(): 0);
237  }
238 
239 
240  bool IpoptUserClass::intermediate_callback(AlgorithmMode mode, Index iter, Number obj_value,
241  Number inf_pr, Number inf_du,
242  Number mu, Number d_norm,
243  Number regularization_size,
244  Number alpha_du, Number alpha_pr,
245  Index ls_trials,
246  const IpoptData* ip_data,
247  IpoptCalculatedQuantities* ip_cq) {
248 
249  // Only do the callback every few iterations
250  if (iter % solver_.callback_step_!=0) return true;
251 
254  // http://list.coin-or.org/pipermail/ipopt/2010-April/001965.html
255 
256  bool full_callback = false;
257 
258 #ifdef WITH_IPOPT_CALLBACK
259  OrigIpoptNLP* orignlp = dynamic_cast<OrigIpoptNLP*>(GetRawPtr(ip_cq->GetIpoptNLP()));
260  if (!orignlp) return true;
261  TNLPAdapter* tnlp_adapter = dynamic_cast<TNLPAdapter*>(GetRawPtr(orignlp->nlp()));
262  if (!tnlp_adapter) return true;
263 
264  const Vector& x = *ip_data->curr()->x();
265  const Vector& z_L = *ip_data->curr()->z_L();
266  const Vector& z_U = *ip_data->curr()->z_U();
267  const Vector& c = *ip_cq->curr_c();
268  const Vector& d = *ip_cq->curr_d();
269  const Vector& y_c = *ip_data->curr()->y_c();
270  const Vector& y_d = *ip_data->curr()->y_d();
271 
272  std::fill_n(x_, n_, 0);
273  std::fill_n(g_, m_, 0);
274  std::fill_n(z_L_, n_, 0);
275  std::fill_n(z_U_, n_, 0);
276  std::fill_n(lambda_, m_, 0);
277 
278  tnlp_adapter->ResortX(x, x_); // no further steps needed
279  tnlp_adapter->ResortG(y_c, y_d, lambda_); // no further steps needed
280  tnlp_adapter->ResortG(c, d, g_);
281  // Copied from Ipopt source: To Ipopt, the equality constraints are presented with right
282  // hand side zero, so we correct for the original right hand side.
283  const Index* c_pos = tnlp_adapter->P_c_g_->ExpandedPosIndices();
284  Index n_c_no_fixed = tnlp_adapter->P_c_g_->NCols();
285  for (Index i=0; i<n_c_no_fixed; i++) {
286  g_[c_pos[i]] += tnlp_adapter->c_rhs_[i];
287  }
288 
289 #if (IPOPT_VERSION_MAJOR > 3) || (IPOPT_VERSION_MAJOR == 3 && IPOPT_VERSION_MINOR >= 14)
290  tnlp_adapter->ResortBounds(z_L, z_L_, z_U, z_U_);
291 #else
292  tnlp_adapter->ResortBnds(z_L, z_L_, z_U, z_U_);
293 #endif
294  // Copied from Ipopt source: Hopefully the following is correct to recover the bound
295  // multipliers for fixed variables (sign ok?)
296  if (tnlp_adapter->fixed_variable_treatment_==TNLPAdapter::MAKE_CONSTRAINT &&
297  tnlp_adapter->n_x_fixed_>0) {
298  const DenseVector* dy_c = static_cast<const DenseVector*>(&y_c);
299  Index n_c_no_fixed = y_c.Dim() - tnlp_adapter->n_x_fixed_;
300  if (!dy_c->IsHomogeneous()) {
301  const Number* values = dy_c->Values();
302  for (Index i=0; i<tnlp_adapter->n_x_fixed_; i++) {
303  z_L_[tnlp_adapter->x_fixed_map_[i]] = Max(0., -values[n_c_no_fixed+i]);
304  z_U_[tnlp_adapter->x_fixed_map_[i]] = Max(0., values[n_c_no_fixed+i]);
305  }
306  } else {
307  double value = dy_c->Scalar();
308  for (Index i=0; i<tnlp_adapter->n_x_fixed_; i++) {
309  z_L_[tnlp_adapter->x_fixed_map_[i]] = Max(0., -value);
310  z_U_[tnlp_adapter->x_fixed_map_[i]] = Max(0., value);
311  }
312  }
313  }
314  full_callback = true;
315 #endif // WITH_IPOPT_CALLBACK
316 
317  return solver_.intermediate_callback(mem_, x_, z_L_, z_U_, g_, lambda_, obj_value, iter,
318  inf_pr, inf_du, mu, d_norm, regularization_size,
319  alpha_du, alpha_pr, ls_trials, full_callback);
320  }
321 
323  return solver_.get_number_of_nonlinear_variables();
324  }
325 
327  Index* pos_nonlin_vars) {
328  return solver_.get_list_of_nonlinear_variables(num_nonlin_vars, pos_nonlin_vars);
329  }
330 
331  bool IpoptUserClass::get_var_con_metadata(Index n, StringMetaDataMapType& var_string_md,
332  IntegerMetaDataMapType& var_integer_md,
333  NumericMetaDataMapType& var_numeric_md,
334  Index m, StringMetaDataMapType& con_string_md,
335  IntegerMetaDataMapType& con_integer_md,
336  NumericMetaDataMapType& con_numeric_md) {
337 
338  return solver_.get_var_con_metadata(var_string_md, var_integer_md, var_numeric_md,
339  con_string_md, con_integer_md, con_numeric_md);
340  }
341 
342  void IpoptUserClass::finalize_metadata(Index n, const StringMetaDataMapType& var_string_md,
343  const IntegerMetaDataMapType& var_integer_md,
344  const NumericMetaDataMapType& var_numeric_md,
345  Index m, const StringMetaDataMapType& con_string_md,
346  const IntegerMetaDataMapType& con_integer_md,
347  const NumericMetaDataMapType& con_numeric_md) {
348  casadi_assert_dev(n==solver_.nx_);
349  casadi_assert_dev(m==solver_.ng_);
350  mem_->var_string_md = var_string_md;
351  mem_->var_integer_md = var_integer_md;
352  mem_->var_numeric_md = var_numeric_md;
353  mem_->con_string_md = con_string_md;
354  mem_->con_integer_md = con_integer_md;
355  mem_->con_numeric_md = con_numeric_md;
356  }
357 
358 } // namespace casadi
const char * what() const override
Display error.
Definition: exception.hpp:90
'ipopt' plugin for Nlpsol
void get_nlp_info(IpoptMemory *m, int &nx, int &ng, int &nnz_jac_g, int &nnz_h_lag) const
void finalize_solution(IpoptMemory *m, const double *x, const double *z_L, const double *z_U, const double *g, const double *lambda, double obj_value, int iter_count) const
bool get_bounds_info(IpoptMemory *m, double *x_l, double *x_u, double *g_l, double *g_u) const
ConvexifyData convexify_data_
Data for convexification.
int get_number_of_nonlinear_variables() const
bool get_var_con_metadata(std::map< std::string, std::vector< std::string > > &var_string_md, std::map< std::string, std::vector< int > > &var_integer_md, std::map< std::string, std::vector< double > > &var_numeric_md, std::map< std::string, std::vector< std::string > > &con_string_md, std::map< std::string, std::vector< int > > &con_integer_md, std::map< std::string, std::vector< double > > &con_numeric_md) const
bool intermediate_callback(IpoptMemory *m, const double *x, const double *z_L, const double *z_U, const double *g, const double *lambda, double obj_value, int iter, double inf_pr, double inf_du, double mu, double d_norm, double regularization_size, double alpha_du, double alpha_pr, int ls_trials, bool full_callback) const
bool get_list_of_nonlinear_variables(int num_nonlin_vars, int *pos_nonlin_vars) const
bool get_starting_point(IpoptMemory *m, bool init_x, double *x, bool init_z, double *z_L, double *z_U, bool init_lambda, double *lambda) const
void finalize_solution(SolverReturn status, Index n, const Number *x, const Number *z_L, const Number *z_U, Index m, const Number *g, const Number *lambda, Number obj_value, const IpoptData *ip_data, IpoptCalculatedQuantities *ip_cq) override
Definition: ipopt_nlp.cpp:229
~IpoptUserClass() override
Definition: ipopt_nlp.cpp:48
bool intermediate_callback(AlgorithmMode mode, Index iter, Number obj_value, Number inf_pr, Number inf_du, Number mu, Number d_norm, Number regularization_size, Number alpha_du, Number alpha_pr, Index ls_trials, const IpoptData *ip_data, IpoptCalculatedQuantities *ip_cq) override
Definition: ipopt_nlp.cpp:240
bool get_var_con_metadata(Index n, StringMetaDataMapType &var_string_md, IntegerMetaDataMapType &var_integer_md, NumericMetaDataMapType &var_numeric_md, Index m, StringMetaDataMapType &con_string_md, IntegerMetaDataMapType &con_integer_md, NumericMetaDataMapType &con_numeric_md) override
Definition: ipopt_nlp.cpp:331
void finalize_metadata(Index n, const StringMetaDataMapType &var_string_md, const IntegerMetaDataMapType &var_integer_md, const NumericMetaDataMapType &var_numeric_md, Index m, const StringMetaDataMapType &con_string_md, const IntegerMetaDataMapType &con_integer_md, const NumericMetaDataMapType &con_numeric_md) override
Definition: ipopt_nlp.cpp:342
bool get_list_of_nonlinear_variables(Index num_nonlin_vars, Index *pos_nonlin_vars) override
Definition: ipopt_nlp.cpp:326
bool eval_g(Index n, const Number *x, bool new_x, Index m, Number *g) override
Definition: ipopt_nlp.cpp:125
IpoptUserClass(const IpoptInterface &solver, IpoptMemory *mem)
Definition: ipopt_nlp.cpp:33
bool get_bounds_info(Index n, Number *x_l, Number *x_u, Index m, Number *g_l, Number *g_u) override
Definition: ipopt_nlp.cpp:70
bool eval_h(Index n, const Number *x, bool new_x, Number obj_factor, Index m, const Number *lambda, bool new_lambda, Index nele_hess, Index *iRow, Index *jCol, Number *values) override
Definition: ipopt_nlp.cpp:182
bool eval_jac_g(Index n, const Number *x, bool new_x, Index m, Index nele_jac, Index *iRow, Index *jCol, Number *values) override
Definition: ipopt_nlp.cpp:143
bool eval_f(Index n, const Number *x, bool new_x, Number &obj_value) override
Definition: ipopt_nlp.cpp:88
bool get_starting_point(Index n, bool init_x, Number *x, bool init_z, Number *z_L, Number *z_U, Index m, bool init_lambda, Number *lambda) override
Definition: ipopt_nlp.cpp:78
bool get_nlp_info(Index &n, Index &m, Index &nnz_jac_g, Index &nnz_h_lag, IndexStyleEnum &index_style) override
Definition: ipopt_nlp.cpp:59
bool eval_grad_f(Index n, const Number *x, bool new_x, Number *grad_f) override
Definition: ipopt_nlp.cpp:106
Index get_number_of_nonlinear_variables() override
Definition: ipopt_nlp.cpp:322
casadi_int ng_
Number of constraints.
Definition: nlpsol_impl.hpp:69
casadi_int callback_step_
Execute the callback function only after this amount of iterations.
Definition: nlpsol_impl.hpp:78
casadi_int nx_
Number of variables.
Definition: nlpsol_impl.hpp:66
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
bool show_eval_warnings_
Show evaluation warnings.
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
The casadi namespace.
Definition: archiver.cpp:28
casadi_convexify_config< double > config
Definition: mx.hpp:61
std::map< std::string, std::vector< double > > con_numeric_md
std::map< std::string, std::vector< std::string > > var_string_md
std::map< std::string, std::vector< std::string > > con_string_md
std::map< std::string, std::vector< int > > var_integer_md
std::map< std::string, std::vector< double > > var_numeric_md
std::map< std::string, std::vector< int > > con_integer_md
casadi_nlpsol_data< double > d_nlp
Definition: nlpsol_impl.hpp:42
std::map< std::string, FStats > fstats