- 3.0.2 optimal control module.
IpoptSolver-impl.h
Go to the documentation of this file.
1 /**********************************************************************************************************************
2 This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich
3 Licensed under the BSD-2 license (see LICENSE file in main directory)
4 **********************************************************************************************************************/
5 
7 
8 // #define DEBUG_PRINT
9 
10 
11 template <typename SCALAR>
12 IpoptSolver<SCALAR>::IpoptSolver(std::shared_ptr<tpl::Nlp<SCALAR>> nlp, const NlpSolverSettings& settings)
13  : BASE(nlp, settings), settings_(BASE::settings_.ipoptSettings_)
14 {
15  //Constructor arguments
16  //Argument 1: create console output
17  //Argument 2: create empty
18  ipoptApp_ = std::shared_ptr<Ipopt::IpoptApplication>(new Ipopt::IpoptApplication(true, false));
19  configureDerived(settings);
20 }
21 
22 template <typename SCALAR>
23 IpoptSolver<SCALAR>::~IpoptSolver()
24 {
25  // Needed to destruct all the IPOPT memory
26  Ipopt::Referencer* t = NULL;
27  this->ReleaseRef(t);
28  delete t;
29 }
30 
31 template <typename SCALAR>
32 void IpoptSolver<SCALAR>::configureDerived(const NlpSolverSettings& settings)
33 {
34  std::cout << "calling Ipopt configure derived" << std::endl;
35  settings_ = settings.ipoptSettings_;
36  setSolverOptions();
37  this->isInitialized_ = true;
38 }
39 
40 template <typename SCALAR>
41 void IpoptSolver<SCALAR>::setSolverOptions()
42 {
43  ipoptApp_->Options()->SetNumericValue("tol", settings_.tol_);
44  ipoptApp_->Options()->SetNumericValue("constr_viol_tol", settings_.constr_viol_tol_);
45  ipoptApp_->Options()->SetIntegerValue("max_iter", settings_.max_iter_);
46  // ipoptApp_->Options()->SetNumericValue("resto.tol", settings_.restoTol_);
47  // ipoptApp_->Options()->SetNumericValue("acceptable_tol", settings_.acceptableTol_);
48  // ipoptApp_->Options()->SetNumericValue("resto.acceptable_tol", settings_.restoAcceptableTol_);
49  ipoptApp_->Options()->SetStringValueIfUnset("linear_scaling_on_demand", settings_.linear_scaling_on_demand_);
50  ipoptApp_->Options()->SetStringValueIfUnset("hessian_approximation", settings_.hessian_approximation_);
51  // ipoptApp_->Options()->SetStringValueIfUnset("nlp_scaling_method", settings_.nlp_scaling_method_);
52  ipoptApp_->Options()->SetIntegerValue("print_level", settings_.printLevel_); //working now
53  ipoptApp_->Options()->SetStringValueIfUnset("print_user_options", settings_.print_user_options_);
54  // ipoptApp_->Options()->SetIntegerValue("print_frequency_iter", settings_.print_frequency_iter_);
55  ipoptApp_->Options()->SetStringValueIfUnset("derivative_test", settings_.derivativeTest_);
56  ipoptApp_->Options()->SetIntegerValue("print_level", settings_.printLevel_);
57  ipoptApp_->Options()->SetNumericValue("derivative_test_tol", settings_.derivativeTestTol_);
58  ipoptApp_->Options()->SetNumericValue("derivative_test_perturbation", settings_.derivativeTestPerturbation_);
59  ipoptApp_->Options()->SetNumericValue("point_perturbation_radius", settings_.point_perturbation_radius_);
60  ipoptApp_->Options()->SetStringValueIfUnset("linear_system_scaling", settings_.linearSystemScaling_);
61  ipoptApp_->Options()->SetStringValueIfUnset("linear_solver", settings_.linear_solver_);
62 }
63 
64 template <typename SCALAR>
66 {
67  status_ = ipoptApp_->Initialize();
68  if (!(status_ == Ipopt::Solve_Succeeded) && !this->isInitialized_)
69  throw(std::runtime_error("NLP initialization failed"));
70 
71  // Ask Ipopt to solve the problem
72  status_ = ipoptApp_->OptimizeTNLP(this);
73 
74  if (status_ == Ipopt::Solve_Succeeded || status_ == Ipopt::Solved_To_Acceptable_Level)
75  {
76  // Retrieve some statistics about the solve
77  if (settings_.printLevel_ > 1)
78  {
79  Ipopt::Index iter_count = ipoptApp_->Statistics()->IterationCount();
80  std::cout << std::endl
81  << std::endl
82  << "*** The problem solved in " << iter_count << " iterations!" << std::endl;
83 
84  SCALAR final_obj = ipoptApp_->Statistics()->FinalObjective();
85  std::cout << std::endl
86  << std::endl
87  << "*** The final value of the objective function is " << final_obj << '.' << std::endl;
88  }
89  return true;
90  }
91  else
92  {
93  if (settings_.printLevel_ > 1)
94  std::cout << " ipopt return value: " << status_ << std::endl;
95  return false;
96  }
97 }
98 
99 template <typename SCALAR>
100 void IpoptSolver<SCALAR>::prepareWarmStart(size_t maxIterations)
101 {
102  ipoptApp_->Options()->SetStringValue("warm_start_init_point", "yes");
103  ipoptApp_->Options()->SetNumericValue("warm_start_bound_push", 1e-9);
104  ipoptApp_->Options()->SetNumericValue("warm_start_bound_frac", 1e-9);
105  ipoptApp_->Options()->SetNumericValue("warm_start_slack_bound_frac", 1e-9);
106  ipoptApp_->Options()->SetNumericValue("warm_start_slack_bound_push", 1e-9);
107  ipoptApp_->Options()->SetNumericValue("warm_start_mult_bound_push", 1e-9);
108  ipoptApp_->Options()->SetIntegerValue("max_iter", (int)maxIterations);
109  ipoptApp_->Options()->SetStringValue("derivative_test", "none");
110 }
111 
112 template <typename SCALAR>
113 bool IpoptSolver<SCALAR>::get_nlp_info(Ipopt::Index& n,
114  Ipopt::Index& m,
115  Ipopt::Index& nnz_jac_g,
116  Ipopt::Index& nnz_h_lag,
117  IndexStyleEnum& index_style)
118 {
119 #ifdef DEBUG_PRINT
120  std::cout << "... entering get_nlp_info()" << std::endl;
121 #endif
122  n = this->nlp_->getVarCount();
123  assert(n == n);
124 
125  m = this->nlp_->getConstraintsCount();
126  assert(m == m);
127 
128  nnz_jac_g = static_cast<Ipopt::Index>(this->nlp_->getNonZeroJacobianCount());
129  assert(nnz_jac_g == nnz_jac_g);
130 
131  if (settings_.hessian_approximation_ == "exact")
132  nnz_h_lag = static_cast<Ipopt::Index>(this->nlp_->getNonZeroHessianCount());
133 
134  index_style = Ipopt::TNLP::C_STYLE;
135 
136 #ifdef DEBUG_PRINT
137  std::cout << "... number of decision variables = " << n << std::endl;
138  std::cout << "... number of constraints = " << m << std::endl;
139  std::cout << "... nonzeros in jacobian = " << nnz_jac_g << std::endl;
140 #endif
141 
142  return true;
143 }
144 
145 template <typename SCALAR>
146 bool IpoptSolver<SCALAR>::get_bounds_info(Ipopt::Index n,
147  SCALAR* x_l,
148  SCALAR* x_u,
149  Ipopt::Index m,
150  SCALAR* g_l,
151  SCALAR* g_u)
152 {
153 #ifdef DEBUG_PRINT
154  std::cout << "... entering get_bounds_info()" << std::endl;
155 #endif //DEBUG_PRINT
156  MapVecXs x_lVec(x_l, n);
157  MapVecXs x_uVec(x_u, n);
158  MapVecXs g_lVec(g_l, m);
159  MapVecXs g_uVec(g_u, m);
160  this->nlp_->getVariableBounds(x_lVec, x_uVec, n);
161  // bounds on optimization vector
162  // x_l <= x <= x_u
163  assert(x_l == x_l);
164  assert(x_u == x_u);
165  assert(n == n);
166 
167  // constraints bounds (e.g. for equality constraints = 0)
168  this->nlp_->getConstraintBounds(g_lVec, g_uVec, m);
169  assert(g_l == g_l);
170  assert(g_u == g_u);
171 
172 #ifdef DEBUG_PRINT
173  std::cout << "... Leaving get_bounds_info()" << std::endl;
174 #endif //DEBUG_PRINT
175 
176  return true;
177 }
178 
179 template <typename SCALAR>
180 bool IpoptSolver<SCALAR>::get_starting_point(Ipopt::Index n,
181  bool init_x,
182  SCALAR* x,
183  bool init_z,
184  SCALAR* z_L,
185  SCALAR* z_U,
186  Ipopt::Index m,
187  bool init_lambda,
188  SCALAR* lambda)
189 {
190 #ifdef DEBUG_PRINT
191  std::cout << "... entering get_starting_point()" << std::endl;
192 #endif //DEBUG_PRINT
193  //
194  if (init_x)
195  {
196  MapVecXs xVec(x, n);
197  this->nlp_->getInitialGuess(n, xVec);
198  }
199 
200  if (init_z)
201  {
202  MapVecXs z_lVec(z_L, n);
203  MapVecXs z_uVec(z_U, n);
204  this->nlp_->getBoundMultipliers(n, z_lVec, z_uVec);
205  }
206 
207  if (init_lambda)
208  {
209  MapVecXs lambdaVec(lambda, m);
210  this->nlp_->getLambdaVars(m, lambdaVec);
211  }
212 
213 
214 #ifdef DEBUG_PRINT
215  std::cout << "... entering get_starting_point()" << std::endl;
216 #endif //DEBUG_PRINT
217 
218  return true;
219 }
220 
221 template <typename SCALAR>
222 bool IpoptSolver<SCALAR>::eval_f(Ipopt::Index n, const SCALAR* x, bool new_x, SCALAR& obj_value)
223 {
224 #ifdef DEBUG_PRINT
225  std::cout << "... entering eval_f()" << std::endl;
226 #endif //DEBUG_PRINT
227  MapConstVecXs xVec(x, n);
228  this->nlp_->extractOptimizationVars(xVec, new_x);
229  obj_value = this->nlp_->evaluateCostFun();
230  assert(obj_value == obj_value);
231 
232 #ifdef DEBUG_PRINT
233  std::cout << "... leaving eval_f()" << std::endl;
234 #endif //DEBUG_PRINT
235  return true;
236 }
237 
238 template <typename SCALAR>
239 bool IpoptSolver<SCALAR>::eval_grad_f(Ipopt::Index n, const SCALAR* x, bool new_x, SCALAR* grad_f)
240 {
241 #ifdef DEBUG_PRINT
242  std::cout << "... entering eval_grad_f()" << std::endl;
243 #endif //DEBUG_PRINT
244  MapVecXs grad_fVec(grad_f, n);
245  MapConstVecXs xVec(x, n);
246  this->nlp_->extractOptimizationVars(xVec, new_x);
247  this->nlp_->evaluateCostGradient(n, grad_fVec);
248 
249 #ifdef DEBUG_PRINT
250  std::cout << "... leaving eval_grad_f()" << std::endl;
251 #endif //DEBUG_PRINT
252  return true;
253 }
254 
255 template <typename SCALAR>
256 bool IpoptSolver<SCALAR>::eval_g(Ipopt::Index n, const SCALAR* x, bool new_x, Ipopt::Index m, SCALAR* g)
257 {
258 #ifdef DEBUG_PRINT
259  std::cout << "... entering eval_g()" << std::endl;
260 #endif //DEBUG_PRINT
261  assert(m == static_cast<int>(this->nlp_->getConstraintsCount()));
262  MapConstVecXs xVec(x, n);
263  this->nlp_->extractOptimizationVars(xVec, new_x);
264  MapVecXs gVec(g, m);
265  this->nlp_->evaluateConstraints(gVec);
266 
267 
268 #ifdef DEBUG_PRINT
269  std::cout << "gVec: " << gVec.transpose() << std::endl;
270  std::cout << "... leaving eval_g()" << std::endl;
271 #endif //DEBUG_PRINT
272 
273  return true;
274 }
275 
276 template <typename SCALAR>
277 bool IpoptSolver<SCALAR>::eval_jac_g(Ipopt::Index n,
278  const SCALAR* x,
279  bool new_x,
280  Ipopt::Index m,
281  Ipopt::Index nele_jac,
282  Ipopt::Index* iRow,
283  Ipopt::Index* jCol,
284  SCALAR* values)
285 {
286  if (values == NULL)
287  {
288 #ifdef DEBUG_PRINT
289  std::cout << "... entering eval_jac_g, values == NULL" << std::endl;
290 #endif //DEBUG_PRINT
291  // set indices of nonzero elements of the jacobian
292  Eigen::Map<Eigen::VectorXi> iRowVec(iRow, nele_jac);
293  Eigen::Map<Eigen::VectorXi> jColVec(jCol, nele_jac);
294  this->nlp_->getSparsityPatternJacobian(nele_jac, iRowVec, jColVec);
295 
296 
297 #ifdef DEBUG_PRINT
298  std::cout << "... leaving eval_jac_g, values == NULL" << std::endl;
299 #endif //DEBUG_PRINT
300  }
301  else
302  {
303 #ifdef DEBUG_PRINT
304  std::cout << "... entering eval_jac_g, values != NULL" << std::endl;
305 #endif //DEBUG_PRINT
306  MapVecXs valVec(values, nele_jac);
307  MapConstVecXs xVec(x, n);
308  this->nlp_->extractOptimizationVars(xVec, new_x);
309  this->nlp_->evaluateConstraintJacobian(nele_jac, valVec);
310 
311 
312 #ifdef DEBUG_PRINT
313  std::cout << "... leaving eval_jac_g, values != NULL" << std::endl;
314 #endif //DEBUG_PRINT
315  }
316 
317  return true;
318 }
319 
320 template <typename SCALAR>
321 bool IpoptSolver<SCALAR>::eval_h(Ipopt::Index n,
322  const SCALAR* x,
323  bool new_x,
324  SCALAR obj_factor,
325  Ipopt::Index m,
326  const SCALAR* lambda,
327  bool new_lambda,
328  Ipopt::Index nele_hess,
329  Ipopt::Index* iRow,
330  Ipopt::Index* jCol,
331  SCALAR* values)
332 {
333 #ifdef DEBUG_PRINT
334  std::cout << "... entering eval_h()" << std::endl;
335 #endif
336  if (values == NULL)
337  {
338  // return the structure. This is a symmetric matrix, fill the lower left
339  // triangle only.
340  Eigen::Map<Eigen::VectorXi> iRowVec(iRow, nele_hess);
341  Eigen::Map<Eigen::VectorXi> jColVec(jCol, nele_hess);
342  this->nlp_->getSparsityPatternHessian(nele_hess, iRowVec, jColVec);
343  }
344  else
345  {
346  // return the values. This is a symmetric matrix, fill the lower left
347  // triangle only
348  MapVecXs valVec(values, nele_hess);
349  MapConstVecXs xVec(x, n);
350  MapConstVecXs lambdaVec(lambda, m);
351  this->nlp_->extractOptimizationVars(xVec, new_x);
352  this->nlp_->evaluateHessian(nele_hess, valVec, obj_factor, lambdaVec);
353  }
354 
355 // ATTENTION: for hard coding of the Hessian, one only needs the lower left corner (since it is symmetric) - IPOPT knows that
356 
357 #ifdef DEBUG_PRINT
358  std::cout << "... leaving eval_h()" << std::endl;
359 #endif
360 
361  return true;
362 }
363 
364 template <typename SCALAR>
365 void IpoptSolver<SCALAR>::finalize_solution(Ipopt::SolverReturn status,
366  Ipopt::Index n,
367  const SCALAR* x,
368  const SCALAR* z_L,
369  const SCALAR* z_U,
370  Ipopt::Index m,
371  const SCALAR* g,
372  const SCALAR* lambda,
373  SCALAR obj_value,
374  const Ipopt::IpoptData* ip_data,
375  Ipopt::IpoptCalculatedQuantities* ip_cq)
376 {
377 #ifdef DEBUG_PRINT
378  std::cout << "... entering finalize_solution() ..." << std::endl;
379 #endif
380  MapConstVecXs xVec(x, n);
381  MapConstVecXs zLVec(z_L, n);
382  MapConstVecXs zUVec(z_U, n);
383  MapConstVecXs lambdaVec(lambda, m);
384 
385  this->nlp_->extractIpoptSolution(xVec, zLVec, zUVec, lambdaVec);
386 }
void prepareWarmStart(size_t maxIterations) override
Prepares the solver for a warmstarting scenario with available (good) initial guess.
Definition: IpoptSolver.h:192
clear all close all load ct GNMSLog0 mat reformat t
Definition: gnmsPlot.m:6
CppAD::AD< CppAD::cg::CG< double > > SCALAR
ct::core::StateVector< state_dim > x
Definition: LoadFromFileTest.cpp:20
tpl::IpoptSolver< double > IpoptSolver
Definition: IpoptSolver.h:200
bool solve() override
Solves the nlp.
Definition: IpoptSolver.h:191
void configureDerived(const NlpSolverSettings &settings) override
Forwards the settings to the corresponding nlp solver.
Definition: IpoptSolver.h:193