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