Crocoddyl
ipopt-iface.cpp
1 // BSD 3-Clause License
3 //
4 // Copyright (C) 2022-2023, IRI: CSIC-UPC, Heriot-Watt University
5 // Copyright note valid unless otherwise stated in individual files.
6 // All rights reserved.
8 
9 #include <cmath>
10 #include <iostream>
11 #ifdef CROCODDYL_WITH_MULTITHREADING
12 #include <omp.h>
13 #endif // CROCODDYL_WITH_MULTITHREADING
14 
15 #include "crocoddyl/core/solvers/ipopt/ipopt-iface.hpp"
16 
17 namespace crocoddyl {
18 
20  const boost::shared_ptr<ShootingProblem>& problem)
21  : problem_(problem) {
22  const std::size_t T = problem_->get_T();
23  xs_.resize(T + 1);
24  us_.resize(T);
25  datas_.resize(T + 1);
26  ixu_.resize(T + 1);
27 
28  nconst_ = 0;
29  nvar_ = 0;
30  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
31  problem_->get_runningModels();
32  for (std::size_t t = 0; t < T; ++t) {
33  const std::size_t nxi = models[t]->get_state()->get_nx();
34  const std::size_t ndxi = models[t]->get_state()->get_ndx();
35  const std::size_t nui = models[t]->get_nu();
36 
37  xs_[t] = models[t]->get_state()->zero();
38  us_[t] = Eigen::VectorXd::Zero(nui);
39  datas_[t] = createData(nxi, ndxi, nui);
40  ixu_[t] = nvar_;
41  nconst_ += ndxi; // T*ndx eq. constraints for dynamics
42  nvar_ += ndxi + nui; // Multiple shooting, states and controls
43  }
44  ixu_[T] = nvar_;
45 
46  // Initial condition
47  nconst_ += models[0]->get_state()->get_ndx();
48 
49  const boost::shared_ptr<ActionModelAbstract>& model =
50  problem_->get_terminalModel();
51  const std::size_t nxi = model->get_state()->get_nx();
52  const std::size_t ndxi = model->get_state()->get_ndx();
53  nvar_ += ndxi; // final node
54  xs_[T] = model->get_state()->zero();
55  datas_[T] = createData(nxi, ndxi, 0);
56 }
57 
58 void IpoptInterface::resizeData() {
59  const std::size_t T = problem_->get_T();
60  nvar_ = 0;
61  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
62  problem_->get_runningModels();
63  for (std::size_t t = 0; t < T; ++t) {
64  const std::size_t nxi = models[t]->get_state()->get_nx();
65  const std::size_t ndxi = models[t]->get_state()->get_ndx();
66  const std::size_t nui = models[t]->get_nu();
67 
68  xs_[t].conservativeResize(nxi);
69  us_[t].conservativeResize(nui);
70  datas_[t]->resize(nxi, ndxi, nui);
71  ixu_[t] = nvar_;
72  nconst_ += ndxi; // T*ndx eq. constraints for dynamics
73  nvar_ += ndxi + nui; // Multiple shooting, states and controls
74  }
75  ixu_[T] = nvar_;
76 
77  // Initial condition
78  nconst_ += models[0]->get_state()->get_ndx();
79 
80  const boost::shared_ptr<ActionModelAbstract>& model =
81  problem_->get_terminalModel();
82  const std::size_t nxi = model->get_state()->get_nx();
83  const std::size_t ndxi = model->get_state()->get_ndx();
84  nvar_ += ndxi; // final node
85  xs_[T].conservativeResize(nxi);
86  datas_[T]->resize(nxi, ndxi, 0);
87 }
88 
89 IpoptInterface::~IpoptInterface() {}
90 
91 bool IpoptInterface::get_nlp_info(Ipopt::Index& n, Ipopt::Index& m,
92  Ipopt::Index& nnz_jac_g,
93  Ipopt::Index& nnz_h_lag,
94  IndexStyleEnum& index_style) {
95  n = static_cast<Ipopt::Index>(nvar_); // number of variables
96  m = static_cast<Ipopt::Index>(nconst_); // number of constraints
97 
98  nnz_jac_g = 0; // Jacobian nonzeros for dynamic constraints
99  nnz_h_lag = 0; // Hessian nonzeros (only lower triangular part)
100  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
101  problem_->get_runningModels();
102  const std::size_t T = problem_->get_T();
103  for (std::size_t t = 0; t < T; ++t) {
104  const std::size_t ndxi = models[t]->get_state()->get_ndx();
105  const std::size_t ndxi_next =
106  t + 1 == T ? problem_->get_terminalModel()->get_state()->get_ndx()
107  : models[t + 1]->get_state()->get_ndx();
108  const std::size_t nui = models[t]->get_nu();
109  nnz_jac_g += ndxi * (ndxi + ndxi_next + nui);
110 
111  // Hessian
112  std::size_t nonzero = 0;
113  for (std::size_t i = 1; i <= (ndxi + nui); ++i) {
114  nonzero += i;
115  }
116  nnz_h_lag += nonzero;
117  }
118 
119  // Initial condition
120  nnz_jac_g +=
121  models[0]->get_state()->get_ndx() * models[0]->get_state()->get_ndx();
122 
123  // Hessian nonzero for the terminal cost
124  const std::size_t ndxi =
125  problem_->get_terminalModel()->get_state()->get_ndx();
126  std::size_t nonzero = 0;
127  for (std::size_t i = 1; i <= ndxi; ++i) {
128  nonzero += i;
129  }
130  nnz_h_lag += nonzero;
131 
132  // use the C style indexing (0-based)
133  index_style = Ipopt::TNLP::C_STYLE;
134 
135  return true;
136 }
137 
138 #ifndef NDEBUG
139 bool IpoptInterface::get_bounds_info(Ipopt::Index n, Ipopt::Number* x_l,
140  Ipopt::Number* x_u, Ipopt::Index m,
141  Ipopt::Number* g_l, Ipopt::Number* g_u) {
142 #else
143 bool IpoptInterface::get_bounds_info(Ipopt::Index, Ipopt::Number* x_l,
144  Ipopt::Number* x_u, Ipopt::Index,
145  Ipopt::Number* g_l, Ipopt::Number* g_u) {
146 #endif
147  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
148  "Inconsistent number of decision variables");
149  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
150  "Inconsistent number of constraints");
151 
152  // Adding bounds
153  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
154  problem_->get_runningModels();
155  for (std::size_t t = 0; t < problem_->get_T(); ++t) {
156  // Running state bounds
157  const std::size_t ndxi = models[t]->get_state()->get_ndx();
158  const std::size_t nui = models[t]->get_nu();
159 
160  for (std::size_t j = 0; j < ndxi; ++j) {
161  x_l[ixu_[t] + j] = std::numeric_limits<double>::lowest();
162  x_u[ixu_[t] + j] = std::numeric_limits<double>::max();
163  }
164  for (std::size_t j = 0; j < nui; ++j) {
165  x_l[ixu_[t] + ndxi + j] = models[t]->get_has_control_limits()
166  ? models[t]->get_u_lb()(j)
167  : std::numeric_limits<double>::lowest();
168  x_u[ixu_[t] + ndxi + j] = models[t]->get_has_control_limits()
169  ? models[t]->get_u_ub()(j)
170  : std::numeric_limits<double>::max();
171  }
172  }
173 
174  // Final state bounds
175  const std::size_t ndxi =
176  problem_->get_terminalModel()->get_state()->get_ndx();
177  for (std::size_t j = 0; j < ndxi; j++) {
178  x_l[ixu_.back() + j] = std::numeric_limits<double>::lowest();
179  x_u[ixu_.back() + j] = std::numeric_limits<double>::max();
180  }
181 
182  // Dynamics & Initial conditions (all equal to zero)
183  for (Ipopt::Index i = 0; i < static_cast<Ipopt::Index>(nconst_); ++i) {
184  g_l[i] = 0;
185  g_u[i] = 0;
186  }
187 
188  return true;
189 }
190 
191 #ifndef NDEBUG
192 bool IpoptInterface::get_starting_point(Ipopt::Index n, bool init_x,
193  Ipopt::Number* x, bool init_z,
194  Ipopt::Number* /*z_L*/,
195  Ipopt::Number* /*z_U*/, Ipopt::Index m,
196  bool init_lambda,
197  Ipopt::Number* /*lambda*/) {
198 #else
199 bool IpoptInterface::get_starting_point(Ipopt::Index, bool /*init_x*/,
200  Ipopt::Number* x, bool, Ipopt::Number*,
201  Ipopt::Number*, Ipopt::Index, bool,
202  Ipopt::Number*) {
203 #endif
204  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
205  "Inconsistent number of decision variables");
206  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
207  "Inconsistent number of constraints");
208  assert_pretty(init_x == true,
209  "Make sure to provide initial value for primal variables");
210  assert_pretty(init_z == false,
211  "Cannot provide initial value for bound multipliers");
212  assert_pretty(init_lambda == false,
213  "Cannot provide initial value for constraint multipliers");
214 
215  // initialize to the given starting point
216  // State variables are always at 0 since they represent increments from the
217  // given initial point
218  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
219  problem_->get_runningModels();
220  for (std::size_t t = 0; t < problem_->get_T(); ++t) {
221  const std::size_t ndxi = models[t]->get_state()->get_ndx();
222  const std::size_t nui = models[t]->get_nu();
223  for (std::size_t j = 0; j < ndxi; ++j) {
224  x[ixu_[t] + j] = 0;
225  }
226  for (std::size_t j = 0; j < nui; ++j) {
227  x[ixu_[t] + ndxi + j] = us_[t](j);
228  }
229  }
230  const std::size_t ndxi =
231  problem_->get_terminalModel()->get_state()->get_ndx();
232  for (std::size_t j = 0; j < ndxi; j++) {
233  x[ixu_.back() + j] = 0;
234  }
235 
236  return true;
237 }
238 
239 #ifndef NDEBUG
240 bool IpoptInterface::eval_f(Ipopt::Index n, const Ipopt::Number* x,
241  bool /*new_x*/, Ipopt::Number& obj_value) {
242 #else
243 bool IpoptInterface::eval_f(Ipopt::Index, const Ipopt::Number* x, bool,
244  Ipopt::Number& obj_value) {
245 #endif
246  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
247  "Inconsistent number of decision variables");
248 
249  // Running costs
250  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
251  problem_->get_runningModels();
252  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
253  problem_->get_runningDatas();
254  const std::size_t T = problem_->get_T();
255 #ifdef CROCODDYL_WITH_MULTITHREADING
256 #pragma omp parallel for num_threads(problem_->get_nthreads())
257 #endif
258  for (std::size_t t = 0; t < T; ++t) {
259  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
260  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
261  const std::size_t ndxi = model->get_state()->get_ndx();
262  const std::size_t nui = model->get_nu();
263 
264  datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
265  datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
266  model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
267  model->calc(data, datas_[t]->x, datas_[t]->u);
268  }
269 
270 #ifdef CROCODDYL_WITH_MULTITHREADING
271 #pragma omp simd reduction(+ : obj_value)
272 #endif
273  for (std::size_t t = 0; t < T; ++t) {
274  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
275  obj_value += data->cost;
276  }
277 
278  // Terminal costs
279  const boost::shared_ptr<ActionModelAbstract>& model =
280  problem_->get_terminalModel();
281  const boost::shared_ptr<ActionDataAbstract>& data =
282  problem_->get_terminalData();
283  const std::size_t ndxi = model->get_state()->get_ndx();
284 
285  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
286  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
287  model->calc(data, datas_[T]->x);
288  obj_value += data->cost;
289 
290  return true;
291 }
292 
293 #ifndef NDEBUG
294 bool IpoptInterface::eval_grad_f(Ipopt::Index n, const Ipopt::Number* x,
295  bool /*new_x*/, Ipopt::Number* grad_f) {
296 #else
297 bool IpoptInterface::eval_grad_f(Ipopt::Index, const Ipopt::Number* x, bool,
298  Ipopt::Number* grad_f) {
299 #endif
300  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
301  "Inconsistent number of decision variables");
302 
303  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
304  problem_->get_runningModels();
305  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
306  problem_->get_runningDatas();
307  const std::size_t T = problem_->get_T();
308 #ifdef CROCODDYL_WITH_MULTITHREADING
309 #pragma omp parallel for num_threads(problem_->get_nthreads())
310 #endif
311  for (std::size_t t = 0; t < T; ++t) {
312  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
313  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
314  const std::size_t ndxi = model->get_state()->get_ndx();
315  const std::size_t nui = model->get_nu();
316 
317  datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
318  datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
319  model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
320  model->get_state()->Jintegrate(xs_[t], datas_[t]->dx, datas_[t]->Jint_dx,
321  datas_[t]->Jint_dx, second, setto);
322  model->calc(data, datas_[t]->x, datas_[t]->u);
323  model->calcDiff(data, datas_[t]->x, datas_[t]->u);
324  datas_[t]->Ldx.noalias() = datas_[t]->Jint_dx.transpose() * data->Lx;
325  }
326  for (std::size_t t = 0; t < T; ++t) {
327  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
328  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
329  const std::size_t ndxi = model->get_state()->get_ndx();
330  const std::size_t nui = model->get_nu();
331  for (std::size_t j = 0; j < ndxi; ++j) {
332  grad_f[ixu_[t] + j] = datas_[t]->Ldx(j);
333  }
334  for (std::size_t j = 0; j < nui; ++j) {
335  grad_f[ixu_[t] + ndxi + j] = data->Lu(j);
336  }
337  }
338 
339  // Terminal model
340  const boost::shared_ptr<ActionModelAbstract>& model =
341  problem_->get_terminalModel();
342  const boost::shared_ptr<ActionDataAbstract>& data =
343  problem_->get_terminalData();
344  const std::size_t ndxi = model->get_state()->get_ndx();
345 
346  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
347  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
348  model->get_state()->Jintegrate(xs_[T], datas_[T]->dx, datas_[T]->Jint_dx,
349  datas_[T]->Jint_dx, second, setto);
350  model->calc(data, datas_[T]->x);
351  model->calcDiff(data, datas_[T]->x);
352  datas_[T]->Ldx.noalias() = datas_[T]->Jint_dx.transpose() * data->Lx;
353  for (std::size_t j = 0; j < ndxi; ++j) {
354  grad_f[ixu_.back() + j] = datas_[T]->Ldx(j);
355  }
356 
357  return true;
358 }
359 
360 #ifndef NDEBUG
361 bool IpoptInterface::eval_g(Ipopt::Index n, const Ipopt::Number* x,
362  bool /*new_x*/, Ipopt::Index m, Ipopt::Number* g) {
363 #else
364 bool IpoptInterface::eval_g(Ipopt::Index, const Ipopt::Number* x,
365  bool /*new_x*/, Ipopt::Index, Ipopt::Number* g) {
366 #endif
367  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
368  "Inconsistent number of decision variables");
369  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
370  "Inconsistent number of constraints");
371 
372  // Dynamic constraints
373  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
374  problem_->get_runningModels();
375  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
376  problem_->get_runningDatas();
377  const std::size_t T = problem_->get_T();
378 #ifdef CROCODDYL_WITH_MULTITHREADING
379 #pragma omp parallel for num_threads(problem_->get_nthreads())
380 #endif
381  for (std::size_t t = 0; t < T; ++t) {
382  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
383  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
384  const std::size_t ndxi = model->get_state()->get_ndx();
385  const std::size_t nui = model->get_nu();
386  const boost::shared_ptr<ActionModelAbstract>& model_next =
387  t + 1 == T ? problem_->get_terminalModel() : models[t + 1];
388  const std::size_t ndxi_next = model_next->get_state()->get_ndx();
389 
390  datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
391  datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
392  datas_[t]->dxnext =
393  Eigen::VectorXd::Map(x + ixu_[t] + ndxi + nui, ndxi_next);
394  model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
395  model_next->get_state()->integrate(xs_[t + 1], datas_[t]->dxnext,
396  datas_[t]->xnext);
397  model->calc(data, datas_[t]->x, datas_[t]->u);
398  model->get_state()->diff(data->xnext, datas_[t]->xnext, datas_[t]->x_diff);
399  }
400 
401  std::size_t ix = 0;
402  for (std::size_t t = 0; t < T; ++t) {
403  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
404  const std::size_t ndxi = model->get_state()->get_ndx();
405  for (std::size_t j = 0; j < ndxi; ++j) {
406  g[ix + j] = datas_[t]->x_diff[j];
407  }
408  ix += ndxi;
409  }
410 
411  // Initial conditions
412  const boost::shared_ptr<ActionModelAbstract>& model = models[0];
413  const std::size_t ndxi = model->get_state()->get_ndx();
414  datas_[0]->dx = Eigen::VectorXd::Map(x, ndxi);
415  model->get_state()->integrate(xs_[0], datas_[0]->dx, datas_[0]->x);
416  model->get_state()->diff(datas_[0]->x, problem_->get_x0(),
417  datas_[0]->x_diff); // x(0) - x_0
418  for (std::size_t j = 0; j < ndxi; j++) {
419  g[ix + j] = datas_[0]->x_diff[j];
420  }
421 
422  return true;
423 }
424 
425 #ifndef NDEBUG
426 bool IpoptInterface::eval_jac_g(Ipopt::Index n, const Ipopt::Number* x,
427  bool /*new_x*/, Ipopt::Index m,
428  Ipopt::Index nele_jac, Ipopt::Index* iRow,
429  Ipopt::Index* jCol, Ipopt::Number* values) {
430 #else
431 bool IpoptInterface::eval_jac_g(Ipopt::Index, const Ipopt::Number* x, bool,
432  Ipopt::Index, Ipopt::Index, Ipopt::Index* iRow,
433  Ipopt::Index* jCol, Ipopt::Number* values) {
434 #endif
435  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
436  "Inconsistent number of decision variables");
437  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
438  "Inconsistent number of constraints");
439 
440  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
441  problem_->get_runningModels();
442  if (values == NULL) {
443  // Dynamic constraints
444  std::size_t idx = 0;
445  std::size_t ix = 0;
446  const std::size_t T = problem_->get_T();
447  for (std::size_t t = 0; t < T; ++t) {
448  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
449  const std::size_t ndxi = model->get_state()->get_ndx();
450  const std::size_t nui = model->get_nu();
451  const std::size_t ndxi_next =
452  t + 1 == T ? problem_->get_terminalModel()->get_state()->get_ndx()
453  : models[t + 1]->get_state()->get_ndx();
454  for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
455  for (std::size_t idx_col = 0; idx_col < (ndxi + nui + ndxi_next);
456  ++idx_col) {
457  iRow[idx] = static_cast<Ipopt::Index>(ix + idx_row);
458  jCol[idx] = static_cast<Ipopt::Index>(ixu_[t] + idx_col);
459  idx++;
460  }
461  }
462  ix += ndxi;
463  }
464 
465  // Initial condition
466  const std::size_t ndxi = models[0]->get_state()->get_ndx();
467  for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
468  for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
469  iRow[idx] = static_cast<Ipopt::Index>(ix + idx_row);
470  jCol[idx] = static_cast<Ipopt::Index>(idx_col);
471  idx++;
472  }
473  }
474 
475  assert_pretty(nele_jac == static_cast<Ipopt::Index>(idx),
476  "Number of jacobian elements set does not coincide with the "
477  "total non-zero Jacobian values");
478  } else {
479  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
480  problem_->get_runningDatas();
481  // Dynamic constraints
482  const std::size_t T = problem_->get_T();
483 #ifdef CROCODDYL_WITH_MULTITHREADING
484 #pragma omp parallel for num_threads(problem_->get_nthreads())
485 #endif
486  for (std::size_t t = 0; t < T; ++t) {
487  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
488  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
489  const boost::shared_ptr<ActionModelAbstract>& model_next =
490  t + 1 == T ? problem_->get_terminalModel() : models[t + 1];
491  const std::size_t ndxi = model->get_state()->get_ndx();
492  const std::size_t ndxi_next = model_next->get_state()->get_ndx();
493  const std::size_t nui = model->get_nu();
494  datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
495  datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
496  datas_[t]->dxnext =
497  Eigen::VectorXd::Map(x + ixu_[t] + ndxi + nui, ndxi_next);
498 
499  model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
500  model_next->get_state()->integrate(xs_[t + 1], datas_[t]->dxnext,
501  datas_[t]->xnext);
502  model->calcDiff(data, datas_[t]->x, datas_[t]->u);
503  model_next->get_state()->Jintegrate(
504  xs_[t + 1], datas_[t]->dxnext, datas_[t]->Jint_dxnext,
505  datas_[t]->Jint_dxnext, second,
506  setto); // datas_[t]->Jsum_dxnext == eq. 81
507  model->get_state()->Jdiff(
508  data->xnext, datas_[t]->xnext, datas_[t]->Jdiff_x,
509  datas_[t]->Jdiff_xnext,
510  both); // datas_[t+1]->Jdiff_x == eq. 83, datas_[t]->Jdiff_x == eq.82
511  model->get_state()->Jintegrate(xs_[t], datas_[t]->dx, datas_[t]->Jint_dx,
512  datas_[t]->Jint_dx, second,
513  setto); // datas_[t]->Jsum_dx == eq. 81
514  datas_[t]->Jg_dxnext.noalias() =
515  datas_[t]->Jdiff_xnext * datas_[t]->Jint_dxnext; // chain rule
516  datas_[t]->FxJint_dx.noalias() = data->Fx * datas_[t]->Jint_dx;
517  datas_[t]->Jg_dx.noalias() = datas_[t]->Jdiff_x * datas_[t]->FxJint_dx;
518  datas_[t]->Jg_u.noalias() = datas_[t]->Jdiff_x * data->Fu;
519  }
520  std::size_t idx = 0;
521  for (std::size_t t = 0; t < T; ++t) {
522  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
523  const boost::shared_ptr<ActionModelAbstract>& model_next =
524  t + 1 == T ? problem_->get_terminalModel() : models[t + 1];
525  const std::size_t ndxi = model->get_state()->get_ndx();
526  const std::size_t nui = model->get_nu();
527  const std::size_t ndxi_next = model_next->get_state()->get_ndx();
528  for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
529  for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
530  values[idx] = datas_[t]->Jg_dx(idx_row, idx_col);
531  idx++;
532  }
533  for (std::size_t idx_col = 0; idx_col < nui; ++idx_col) {
534  values[idx] = datas_[t]->Jg_u(idx_row, idx_col);
535  idx++;
536  }
537  // This could be more optimized since there are a lot of zeros!
538  for (std::size_t idx_col = 0; idx_col < ndxi_next; ++idx_col) {
539  values[idx] = datas_[t]->Jg_dxnext(idx_row, idx_col);
540  idx++;
541  }
542  }
543  }
544 
545  // Initial condition
546  const boost::shared_ptr<ActionModelAbstract>& model = models[0];
547  const std::size_t ndxi = model->get_state()->get_ndx();
548  datas_[0]->dx = Eigen::VectorXd::Map(x, ndxi);
549 
550  model->get_state()->integrate(xs_[0], datas_[0]->dx, datas_[0]->x);
551  model->get_state()->Jdiff(datas_[0]->x, problem_->get_x0(),
552  datas_[0]->Jdiff_x, datas_[0]->Jdiff_x, first);
553  model->get_state()->Jintegrate(xs_[0], datas_[0]->dx, datas_[0]->Jint_dx,
554  datas_[0]->Jint_dx, second, setto);
555  datas_[0]->Jg_ic.noalias() = datas_[0]->Jdiff_x * datas_[0]->Jint_dx;
556  for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
557  for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
558  values[idx] = datas_[0]->Jg_ic(idx_row, idx_col);
559  idx++;
560  }
561  }
562  }
563 
564  return true;
565 }
566 
567 #ifndef NDEBUG
568 bool IpoptInterface::eval_h(Ipopt::Index n, const Ipopt::Number* x,
569  bool /*new_x*/, Ipopt::Number obj_factor,
570  Ipopt::Index m, const Ipopt::Number* /*lambda*/,
571  bool /*new_lambda*/, Ipopt::Index nele_hess,
572  Ipopt::Index* iRow, Ipopt::Index* jCol,
573  Ipopt::Number* values) {
574 #else
575 bool IpoptInterface::eval_h(Ipopt::Index, const Ipopt::Number* x, bool,
576  Ipopt::Number obj_factor, Ipopt::Index,
577  const Ipopt::Number*, bool, Ipopt::Index,
578  Ipopt::Index* iRow, Ipopt::Index* jCol,
579  Ipopt::Number* values) {
580 #endif
581  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
582  "Inconsistent number of decision variables");
583  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
584  "Inconsistent number of constraints");
585 
586  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
587  problem_->get_runningModels();
588  const std::size_t T = problem_->get_T();
589  if (values == NULL) {
590  // return the structure. This is a symmetric matrix, fill the lower left
591  // triangle only
592 
593  // Running Costs
594  std::size_t idx = 0;
595  for (std::size_t t = 0; t < problem_->get_T(); ++t) {
596  const boost::shared_ptr<ActionModelAbstract> model = models[t];
597  const std::size_t ndxi = model->get_state()->get_ndx();
598  const std::size_t nui = model->get_nu();
599  for (std::size_t idx_row = 0; idx_row < ndxi + nui; ++idx_row) {
600  for (std::size_t idx_col = 0; idx_col < ndxi + nui; ++idx_col) {
601  // We need the lower triangular matrix
602  if (idx_col > idx_row) {
603  break;
604  }
605  iRow[idx] = static_cast<Ipopt::Index>(ixu_[t] + idx_row);
606  jCol[idx] = static_cast<Ipopt::Index>(ixu_[t] + idx_col);
607  idx++;
608  }
609  }
610  }
611 
612  // Terminal costs
613  const std::size_t ndxi =
614  problem_->get_terminalModel()->get_state()->get_ndx();
615  for (std::size_t idx_row = 0; idx_row < ndxi; idx_row++) {
616  for (std::size_t idx_col = 0; idx_col < ndxi; idx_col++) {
617  // We need the lower triangular matrix
618  if (idx_col > idx_row) {
619  break;
620  }
621  iRow[idx] = static_cast<Ipopt::Index>(ixu_.back() + idx_row);
622  jCol[idx] = static_cast<Ipopt::Index>(ixu_.back() + idx_col);
623  idx++;
624  }
625  }
626 
627  assert_pretty(nele_hess == static_cast<Ipopt::Index>(idx),
628  "Number of Hessian elements set does not coincide with the "
629  "total non-zero Hessian values");
630  } else {
631  // return the values. This is a symmetric matrix, fill the lower left
632  // triangle only
633  // Running Costs
634  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
635  problem_->get_runningDatas();
636 #ifdef CROCODDYL_WITH_MULTITHREADING
637 #pragma omp parallel for num_threads(problem_->get_nthreads())
638 #endif
639  for (std::size_t t = 0; t < T; ++t) {
640  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
641  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
642  const std::size_t ndxi = model->get_state()->get_ndx();
643  const std::size_t nui = model->get_nu();
644  datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
645  datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
646 
647  model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
648  model->calcDiff(data, datas_[t]->x,
649  datas_[t]->u); // this might be removed
650  model->get_state()->Jintegrate(xs_[t], datas_[t]->dx, datas_[t]->Jint_dx,
651  datas_[t]->Jint_dx, second, setto);
652  datas_[t]->Ldxdx.noalias() =
653  datas_[t]->Jint_dx.transpose() * data->Lxx * datas_[t]->Jint_dx;
654  datas_[t]->Ldxu.noalias() = datas_[t]->Jint_dx.transpose() * data->Lxu;
655  }
656 
657  std::size_t idx = 0;
658  for (std::size_t t = 0; t < T; ++t) {
659  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
660  const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
661  const std::size_t ndxi = model->get_state()->get_ndx();
662  const std::size_t nui = model->get_nu();
663  for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
664  for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
665  // We need the lower triangular matrix
666  if (idx_col > idx_row) {
667  break;
668  }
669  values[idx] = obj_factor * datas_[t]->Ldxdx(idx_row, idx_col);
670  idx++;
671  }
672  }
673  for (std::size_t idx_row = 0; idx_row < nui; ++idx_row) {
674  for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
675  values[idx] = obj_factor * datas_[t]->Ldxu(idx_col, idx_row);
676  idx++;
677  }
678  for (std::size_t idx_col = 0; idx_col < nui; ++idx_col) {
679  if (idx_col > idx_row) {
680  break;
681  }
682  values[idx] = obj_factor * data->Luu(idx_row, idx_col);
683  idx++;
684  }
685  }
686  }
687 
688  // Terminal costs
689  const boost::shared_ptr<ActionModelAbstract>& model =
690  problem_->get_terminalModel();
691  const boost::shared_ptr<ActionDataAbstract>& data =
692  problem_->get_terminalData();
693  const std::size_t ndxi = model->get_state()->get_ndx();
694  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
695 
696  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
697  model->calc(data, datas_[T]->x);
698  model->calcDiff(data, datas_[T]->x);
699  model->get_state()->Jintegrate(xs_[T], datas_[T]->dx, datas_[T]->Jint_dx,
700  datas_[T]->Jint_dx, second, setto);
701  datas_[T]->Ldxdx.noalias() =
702  datas_[T]->Jint_dx.transpose() * data->Lxx * datas_[T]->Jint_dx;
703  for (std::size_t idx_row = 0; idx_row < ndxi; idx_row++) {
704  for (std::size_t idx_col = 0; idx_col < ndxi; idx_col++) {
705  // We need the lower triangular matrix
706  if (idx_col > idx_row) {
707  break;
708  }
709  values[idx] = datas_[T]->Ldxdx(idx_row, idx_col);
710  idx++;
711  }
712  }
713  }
714 
715  return true;
716 }
717 
718 void IpoptInterface::finalize_solution(
719  Ipopt::SolverReturn /*status*/, Ipopt::Index /*n*/, const Ipopt::Number* x,
720  const Ipopt::Number* /*z_L*/, const Ipopt::Number* /*z_U*/,
721  Ipopt::Index /*m*/, const Ipopt::Number* /*g*/,
722  const Ipopt::Number* /*lambda*/, Ipopt::Number obj_value,
723  const Ipopt::IpoptData* /*ip_data*/,
724  Ipopt::IpoptCalculatedQuantities* /*ip_cq*/) {
725  // Copy the solution to vector once solver is finished
726  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
727  problem_->get_runningModels();
728  const std::size_t T = problem_->get_T();
729  for (std::size_t t = 0; t < T; ++t) {
730  const std::size_t ndxi = models[t]->get_state()->get_ndx();
731  const std::size_t nui = models[t]->get_nu();
732  datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
733  datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
734 
735  models[t]->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
736  xs_[t] = datas_[t]->x;
737  us_[t] = datas_[t]->u;
738  }
739  // Terminal node
740  const boost::shared_ptr<ActionModelAbstract>& model =
741  problem_->get_terminalModel();
742  const std::size_t ndxi = model->get_state()->get_ndx();
743  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
744  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
745  xs_[T] = datas_[T]->x;
746 
747  cost_ = obj_value;
748 }
749 
750 bool IpoptInterface::intermediate_callback(
751  Ipopt::AlgorithmMode /*mode*/, Ipopt::Index /*iter*/,
752  Ipopt::Number /*obj_value*/, Ipopt::Number /*inf_pr*/,
753  Ipopt::Number /*inf_du*/, Ipopt::Number /*mu*/, Ipopt::Number /*d_norm*/,
754  Ipopt::Number /*regularization_size*/, Ipopt::Number /*alpha_du*/,
755  Ipopt::Number /*alpha_pr*/, Ipopt::Index /*ls_trials*/,
756  const Ipopt::IpoptData* /*ip_data*/,
757  Ipopt::IpoptCalculatedQuantities* /*ip_cq*/) {
758  return true;
759 }
760 
761 boost::shared_ptr<IpoptInterfaceData> IpoptInterface::createData(
762  const std::size_t nx, const std::size_t ndx, const std::size_t nu) {
763  return boost::allocate_shared<IpoptInterfaceData>(
764  Eigen::aligned_allocator<IpoptInterfaceData>(), nx, ndx, nu);
765 }
766 
767 void IpoptInterface::set_xs(const std::vector<Eigen::VectorXd>& xs) {
768  xs_ = xs;
769 }
770 
771 void IpoptInterface::set_us(const std::vector<Eigen::VectorXd>& us) {
772  us_ = us;
773 }
774 
775 std::size_t IpoptInterface::get_nvar() const { return nvar_; }
776 
777 std::size_t IpoptInterface::get_nconst() const { return nconst_; }
778 
779 const std::vector<Eigen::VectorXd>& IpoptInterface::get_xs() const {
780  return xs_;
781 }
782 
783 const std::vector<Eigen::VectorXd>& IpoptInterface::get_us() const {
784  return us_;
785 }
786 
787 const boost::shared_ptr<ShootingProblem>& IpoptInterface::get_problem() const {
788  return problem_;
789 }
790 
791 double IpoptInterface::get_cost() const { return cost_; }
792 
793 } // namespace crocoddyl
EIGEN_MAKE_ALIGNED_OPERATOR_NEW IpoptInterface(const boost::shared_ptr< crocoddyl::ShootingProblem > &problem)
Initialize the Ipopt interface.