GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/core/solvers/ipopt/ipopt-iface.cpp Lines: 409 435 94.0 %
Date: 2024-02-13 11:12:33 Branches: 306 622 49.2 %

Line Branch Exec Source
1
///////////////////////////////////////////////////////////////////////////////
2
// 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.
7
///////////////////////////////////////////////////////////////////////////////
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
19
4
IpoptInterface::IpoptInterface(
20
4
    const boost::shared_ptr<ShootingProblem>& problem)
21
4
    : problem_(problem) {
22
4
  const std::size_t T = problem_->get_T();
23
4
  xs_.resize(T + 1);
24
4
  us_.resize(T);
25
4
  datas_.resize(T + 1);
26
4
  ixu_.resize(T + 1);
27
28
4
  nconst_ = 0;
29
4
  nvar_ = 0;
30
  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
31
4
      problem_->get_runningModels();
32
44
  for (std::size_t t = 0; t < T; ++t) {
33
40
    const std::size_t nxi = models[t]->get_state()->get_nx();
34
40
    const std::size_t ndxi = models[t]->get_state()->get_ndx();
35
40
    const std::size_t nui = models[t]->get_nu();
36
37
40
    xs_[t] = models[t]->get_state()->zero();
38

40
    us_[t] = Eigen::VectorXd::Zero(nui);
39
40
    datas_[t] = createData(nxi, ndxi, nui);
40
40
    ixu_[t] = nvar_;
41
40
    nconst_ += ndxi;      // T*ndx eq. constraints for dynamics
42
40
    nvar_ += ndxi + nui;  // Multiple shooting, states and controls
43
  }
44
4
  ixu_[T] = nvar_;
45
46
  // Initial condition
47
4
  nconst_ += models[0]->get_state()->get_ndx();
48
49
  const boost::shared_ptr<ActionModelAbstract>& model =
50
4
      problem_->get_terminalModel();
51
4
  const std::size_t nxi = model->get_state()->get_nx();
52
4
  const std::size_t ndxi = model->get_state()->get_ndx();
53
4
  nvar_ += ndxi;  // final node
54
4
  xs_[T] = model->get_state()->zero();
55
4
  datas_[T] = createData(nxi, ndxi, 0);
56
4
}
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
16
IpoptInterface::~IpoptInterface() {}
90
91
3
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
3
  n = static_cast<Ipopt::Index>(nvar_);    // number of variables
96
3
  m = static_cast<Ipopt::Index>(nconst_);  // number of constraints
97
98
3
  nnz_jac_g = 0;  // Jacobian nonzeros for dynamic constraints
99
3
  nnz_h_lag = 0;  // Hessian nonzeros (only lower triangular part)
100
  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
101
3
      problem_->get_runningModels();
102
3
  const std::size_t T = problem_->get_T();
103
33
  for (std::size_t t = 0; t < T; ++t) {
104
30
    const std::size_t ndxi = models[t]->get_state()->get_ndx();
105
    const std::size_t ndxi_next =
106
30
        t + 1 == T ? problem_->get_terminalModel()->get_state()->get_ndx()
107
27
                   : models[t + 1]->get_state()->get_ndx();
108
30
    const std::size_t nui = models[t]->get_nu();
109
30
    nnz_jac_g += ndxi * (ndxi + ndxi_next + nui);
110
111
    // Hessian
112
30
    std::size_t nonzero = 0;
113
300
    for (std::size_t i = 1; i <= (ndxi + nui); ++i) {
114
270
      nonzero += i;
115
    }
116
30
    nnz_h_lag += nonzero;
117
  }
118
119
  // Initial condition
120
3
  nnz_jac_g +=
121
3
      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
3
      problem_->get_terminalModel()->get_state()->get_ndx();
126
3
  std::size_t nonzero = 0;
127
22
  for (std::size_t i = 1; i <= ndxi; ++i) {
128
19
    nonzero += i;
129
  }
130
3
  nnz_h_lag += nonzero;
131
132
  // use the C style indexing (0-based)
133
3
  index_style = Ipopt::TNLP::C_STYLE;
134
135
3
  return true;
136
}
137
138
#ifndef NDEBUG
139
6
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


6
  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
148
                "Inconsistent number of decision variables");
149


6
  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
6
      problem_->get_runningModels();
155
66
  for (std::size_t t = 0; t < problem_->get_T(); ++t) {
156
    // Running state bounds
157
60
    const std::size_t ndxi = models[t]->get_state()->get_ndx();
158
60
    const std::size_t nui = models[t]->get_nu();
159
160
440
    for (std::size_t j = 0; j < ndxi; ++j) {
161
380
      x_l[ixu_[t] + j] = std::numeric_limits<double>::lowest();
162
380
      x_u[ixu_[t] + j] = std::numeric_limits<double>::max();
163
    }
164
220
    for (std::size_t j = 0; j < nui; ++j) {
165
320
      x_l[ixu_[t] + ndxi + j] = models[t]->get_has_control_limits()
166
160
                                    ? models[t]->get_u_lb()(j)
167
160
                                    : std::numeric_limits<double>::lowest();
168
320
      x_u[ixu_[t] + ndxi + j] = models[t]->get_has_control_limits()
169
160
                                    ? models[t]->get_u_ub()(j)
170
160
                                    : std::numeric_limits<double>::max();
171
    }
172
  }
173
174
  // Final state bounds
175
  const std::size_t ndxi =
176
6
      problem_->get_terminalModel()->get_state()->get_ndx();
177
44
  for (std::size_t j = 0; j < ndxi; j++) {
178
38
    x_l[ixu_.back() + j] = std::numeric_limits<double>::lowest();
179
38
    x_u[ixu_.back() + j] = std::numeric_limits<double>::max();
180
  }
181
182
  // Dynamics & Initial conditions (all equal to zero)
183
424
  for (Ipopt::Index i = 0; i < static_cast<Ipopt::Index>(nconst_); ++i) {
184
418
    g_l[i] = 0;
185
418
    g_u[i] = 0;
186
  }
187
188
6
  return true;
189
}
190
191
#ifndef NDEBUG
192
6
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


6
  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
205
                "Inconsistent number of decision variables");
206


6
  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
207
                "Inconsistent number of constraints");
208


6
  assert_pretty(init_x == true,
209
                "Make sure to provide initial value for primal variables");
210


6
  assert_pretty(init_z == false,
211
                "Cannot provide initial value for bound multipliers");
212


6
  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
6
      problem_->get_runningModels();
220
66
  for (std::size_t t = 0; t < problem_->get_T(); ++t) {
221
60
    const std::size_t ndxi = models[t]->get_state()->get_ndx();
222
60
    const std::size_t nui = models[t]->get_nu();
223
440
    for (std::size_t j = 0; j < ndxi; ++j) {
224
380
      x[ixu_[t] + j] = 0;
225
    }
226
220
    for (std::size_t j = 0; j < nui; ++j) {
227
160
      x[ixu_[t] + ndxi + j] = us_[t](j);
228
    }
229
  }
230
  const std::size_t ndxi =
231
6
      problem_->get_terminalModel()->get_state()->get_ndx();
232
44
  for (std::size_t j = 0; j < ndxi; j++) {
233
38
    x[ixu_.back() + j] = 0;
234
  }
235
236
6
  return true;
237
}
238
239
#ifndef NDEBUG
240
8
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


8
  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
8
      problem_->get_runningModels();
252
  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
253
8
      problem_->get_runningDatas();
254
8
  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
88
  for (std::size_t t = 0; t < T; ++t) {
259
80
    const boost::shared_ptr<ActionModelAbstract>& model = models[t];
260
80
    const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
261
80
    const std::size_t ndxi = model->get_state()->get_ndx();
262
80
    const std::size_t nui = model->get_nu();
263
264
80
    datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
265
80
    datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
266

80
    model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
267

80
    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
88
  for (std::size_t t = 0; t < T; ++t) {
274
80
    const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
275
80
    obj_value += data->cost;
276
  }
277
278
  // Terminal costs
279
  const boost::shared_ptr<ActionModelAbstract>& model =
280
8
      problem_->get_terminalModel();
281
  const boost::shared_ptr<ActionDataAbstract>& data =
282
8
      problem_->get_terminalData();
283
8
  const std::size_t ndxi = model->get_state()->get_ndx();
284
285
8
  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
286

8
  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
287
8
  model->calc(data, datas_[T]->x);
288
8
  obj_value += data->cost;
289
290
8
  return true;
291
}
292
293
#ifndef NDEBUG
294
11
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


11
  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
11
      problem_->get_runningModels();
305
  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
306
11
      problem_->get_runningDatas();
307
11
  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
121
  for (std::size_t t = 0; t < T; ++t) {
312
110
    const boost::shared_ptr<ActionModelAbstract>& model = models[t];
313
110
    const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
314
110
    const std::size_t ndxi = model->get_state()->get_ndx();
315
110
    const std::size_t nui = model->get_nu();
316
317
110
    datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
318
110
    datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
319

110
    model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
320


220
    model->get_state()->Jintegrate(xs_[t], datas_[t]->dx, datas_[t]->Jint_dx,
321
110
                                   datas_[t]->Jint_dx, second, setto);
322

110
    model->calc(data, datas_[t]->x, datas_[t]->u);
323

110
    model->calcDiff(data, datas_[t]->x, datas_[t]->u);
324

110
    datas_[t]->Ldx.noalias() = datas_[t]->Jint_dx.transpose() * data->Lx;
325
  }
326
121
  for (std::size_t t = 0; t < T; ++t) {
327
110
    const boost::shared_ptr<ActionModelAbstract>& model = models[t];
328
110
    const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
329
110
    const std::size_t ndxi = model->get_state()->get_ndx();
330
110
    const std::size_t nui = model->get_nu();
331
740
    for (std::size_t j = 0; j < ndxi; ++j) {
332
630
      grad_f[ixu_[t] + j] = datas_[t]->Ldx(j);
333
    }
334
390
    for (std::size_t j = 0; j < nui; ++j) {
335
280
      grad_f[ixu_[t] + ndxi + j] = data->Lu(j);
336
    }
337
  }
338
339
  // Terminal model
340
  const boost::shared_ptr<ActionModelAbstract>& model =
341
11
      problem_->get_terminalModel();
342
  const boost::shared_ptr<ActionDataAbstract>& data =
343
11
      problem_->get_terminalData();
344
11
  const std::size_t ndxi = model->get_state()->get_ndx();
345
346
11
  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
347

11
  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
348


22
  model->get_state()->Jintegrate(xs_[T], datas_[T]->dx, datas_[T]->Jint_dx,
349
11
                                 datas_[T]->Jint_dx, second, setto);
350
11
  model->calc(data, datas_[T]->x);
351
11
  model->calcDiff(data, datas_[T]->x);
352

11
  datas_[T]->Ldx.noalias() = datas_[T]->Jint_dx.transpose() * data->Lx;
353
74
  for (std::size_t j = 0; j < ndxi; ++j) {
354
63
    grad_f[ixu_.back() + j] = datas_[T]->Ldx(j);
355
  }
356
357
11
  return true;
358
}
359
360
#ifndef NDEBUG
361
8
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


8
  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
368
                "Inconsistent number of decision variables");
369


8
  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
8
      problem_->get_runningModels();
375
  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
376
8
      problem_->get_runningDatas();
377
8
  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
88
  for (std::size_t t = 0; t < T; ++t) {
382
80
    const boost::shared_ptr<ActionModelAbstract>& model = models[t];
383
80
    const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
384
80
    const std::size_t ndxi = model->get_state()->get_ndx();
385
80
    const std::size_t nui = model->get_nu();
386
    const boost::shared_ptr<ActionModelAbstract>& model_next =
387
80
        t + 1 == T ? problem_->get_terminalModel() : models[t + 1];
388
80
    const std::size_t ndxi_next = model_next->get_state()->get_ndx();
389
390
80
    datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
391
80
    datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
392
80
    datas_[t]->dxnext =
393
160
        Eigen::VectorXd::Map(x + ixu_[t] + ndxi + nui, ndxi_next);
394

80
    model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
395

160
    model_next->get_state()->integrate(xs_[t + 1], datas_[t]->dxnext,
396
80
                                       datas_[t]->xnext);
397

80
    model->calc(data, datas_[t]->x, datas_[t]->u);
398

80
    model->get_state()->diff(data->xnext, datas_[t]->xnext, datas_[t]->x_diff);
399
  }
400
401
8
  std::size_t ix = 0;
402
88
  for (std::size_t t = 0; t < T; ++t) {
403
80
    const boost::shared_ptr<ActionModelAbstract>& model = models[t];
404
80
    const std::size_t ndxi = model->get_state()->get_ndx();
405
520
    for (std::size_t j = 0; j < ndxi; ++j) {
406
440
      g[ix + j] = datas_[t]->x_diff[j];
407
    }
408
80
    ix += ndxi;
409
  }
410
411
  // Initial conditions
412
8
  const boost::shared_ptr<ActionModelAbstract>& model = models[0];
413
8
  const std::size_t ndxi = model->get_state()->get_ndx();
414
8
  datas_[0]->dx = Eigen::VectorXd::Map(x, ndxi);
415

8
  model->get_state()->integrate(xs_[0], datas_[0]->dx, datas_[0]->x);
416

16
  model->get_state()->diff(datas_[0]->x, problem_->get_x0(),
417
8
                           datas_[0]->x_diff);  // x(0) - x_0
418
52
  for (std::size_t j = 0; j < ndxi; j++) {
419
44
    g[ix + j] = datas_[0]->x_diff[j];
420
  }
421
422
8
  return true;
423
}
424
425
#ifndef NDEBUG
426
14
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


14
  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
436
                "Inconsistent number of decision variables");
437


14
  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
438
                "Inconsistent number of constraints");
439
440
  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
441
14
      problem_->get_runningModels();
442
14
  if (values == NULL) {
443
    // Dynamic constraints
444
3
    std::size_t idx = 0;
445
3
    std::size_t ix = 0;
446
3
    const std::size_t T = problem_->get_T();
447
33
    for (std::size_t t = 0; t < T; ++t) {
448
30
      const boost::shared_ptr<ActionModelAbstract>& model = models[t];
449
30
      const std::size_t ndxi = model->get_state()->get_ndx();
450
30
      const std::size_t nui = model->get_nu();
451
      const std::size_t ndxi_next =
452
30
          t + 1 == T ? problem_->get_terminalModel()->get_state()->get_ndx()
453
27
                     : models[t + 1]->get_state()->get_ndx();
454
220
      for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
455
3470
        for (std::size_t idx_col = 0; idx_col < (ndxi + nui + ndxi_next);
456
             ++idx_col) {
457
3280
          iRow[idx] = static_cast<Ipopt::Index>(ix + idx_row);
458
3280
          jCol[idx] = static_cast<Ipopt::Index>(ixu_[t] + idx_col);
459
3280
          idx++;
460
        }
461
      }
462
30
      ix += ndxi;
463
    }
464
465
    // Initial condition
466
3
    const std::size_t ndxi = models[0]->get_state()->get_ndx();
467
22
    for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
468
156
      for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
469
137
        iRow[idx] = static_cast<Ipopt::Index>(ix + idx_row);
470
137
        jCol[idx] = static_cast<Ipopt::Index>(idx_col);
471
137
        idx++;
472
      }
473
    }
474
475


3
    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
11
        problem_->get_runningDatas();
481
    // Dynamic constraints
482
11
    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
121
    for (std::size_t t = 0; t < T; ++t) {
487
110
      const boost::shared_ptr<ActionModelAbstract>& model = models[t];
488
110
      const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
489
      const boost::shared_ptr<ActionModelAbstract>& model_next =
490
110
          t + 1 == T ? problem_->get_terminalModel() : models[t + 1];
491
110
      const std::size_t ndxi = model->get_state()->get_ndx();
492
110
      const std::size_t ndxi_next = model_next->get_state()->get_ndx();
493
110
      const std::size_t nui = model->get_nu();
494
110
      datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
495
110
      datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
496
110
      datas_[t]->dxnext =
497
220
          Eigen::VectorXd::Map(x + ixu_[t] + ndxi + nui, ndxi_next);
498
499

110
      model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
500

220
      model_next->get_state()->integrate(xs_[t + 1], datas_[t]->dxnext,
501
110
                                         datas_[t]->xnext);
502

110
      model->calcDiff(data, datas_[t]->x, datas_[t]->u);
503

440
      model_next->get_state()->Jintegrate(
504
220
          xs_[t + 1], datas_[t]->dxnext, datas_[t]->Jint_dxnext,
505
110
          datas_[t]->Jint_dxnext, second,
506
110
          setto);  // datas_[t]->Jsum_dxnext == eq. 81
507

440
      model->get_state()->Jdiff(
508

220
          data->xnext, datas_[t]->xnext, datas_[t]->Jdiff_x,
509
110
          datas_[t]->Jdiff_xnext,
510
110
          both);  // datas_[t+1]->Jdiff_x == eq. 83, datas_[t]->Jdiff_x == eq.82
511


220
      model->get_state()->Jintegrate(xs_[t], datas_[t]->dx, datas_[t]->Jint_dx,
512
110
                                     datas_[t]->Jint_dx, second,
513
110
                                     setto);  // datas_[t]->Jsum_dx == eq. 81
514
110
      datas_[t]->Jg_dxnext.noalias() =
515
220
          datas_[t]->Jdiff_xnext * datas_[t]->Jint_dxnext;  // chain rule
516

110
      datas_[t]->FxJint_dx.noalias() = data->Fx * datas_[t]->Jint_dx;
517

110
      datas_[t]->Jg_dx.noalias() = datas_[t]->Jdiff_x * datas_[t]->FxJint_dx;
518

110
      datas_[t]->Jg_u.noalias() = datas_[t]->Jdiff_x * data->Fu;
519
    }
520
11
    std::size_t idx = 0;
521
121
    for (std::size_t t = 0; t < T; ++t) {
522
110
      const boost::shared_ptr<ActionModelAbstract>& model = models[t];
523
      const boost::shared_ptr<ActionModelAbstract>& model_next =
524
110
          t + 1 == T ? problem_->get_terminalModel() : models[t + 1];
525
110
      const std::size_t ndxi = model->get_state()->get_ndx();
526
110
      const std::size_t nui = model->get_nu();
527
110
      const std::size_t ndxi_next = model_next->get_state()->get_ndx();
528
740
      for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
529
4920
        for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
530
4290
          values[idx] = datas_[t]->Jg_dx(idx_row, idx_col);
531
4290
          idx++;
532
        }
533
2370
        for (std::size_t idx_col = 0; idx_col < nui; ++idx_col) {
534
1740
          values[idx] = datas_[t]->Jg_u(idx_row, idx_col);
535
1740
          idx++;
536
        }
537
        // This could be more optimized since there are a lot of zeros!
538
4920
        for (std::size_t idx_col = 0; idx_col < ndxi_next; ++idx_col) {
539
4290
          values[idx] = datas_[t]->Jg_dxnext(idx_row, idx_col);
540
4290
          idx++;
541
        }
542
      }
543
    }
544
545
    // Initial condition
546
11
    const boost::shared_ptr<ActionModelAbstract>& model = models[0];
547
11
    const std::size_t ndxi = model->get_state()->get_ndx();
548
11
    datas_[0]->dx = Eigen::VectorXd::Map(x, ndxi);
549
550

11
    model->get_state()->integrate(xs_[0], datas_[0]->dx, datas_[0]->x);
551


33
    model->get_state()->Jdiff(datas_[0]->x, problem_->get_x0(),
552
22
                              datas_[0]->Jdiff_x, datas_[0]->Jdiff_x, first);
553


22
    model->get_state()->Jintegrate(xs_[0], datas_[0]->dx, datas_[0]->Jint_dx,
554
11
                                   datas_[0]->Jint_dx, second, setto);
555

11
    datas_[0]->Jg_ic.noalias() = datas_[0]->Jdiff_x * datas_[0]->Jint_dx;
556
74
    for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
557
492
      for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
558
429
        values[idx] = datas_[0]->Jg_ic(idx_row, idx_col);
559
429
        idx++;
560
      }
561
    }
562
  }
563
564
14
  return true;
565
}
566
567
#ifndef NDEBUG
568
8
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


8
  assert_pretty(n == static_cast<Ipopt::Index>(nvar_),
582
                "Inconsistent number of decision variables");
583


8
  assert_pretty(m == static_cast<Ipopt::Index>(nconst_),
584
                "Inconsistent number of constraints");
585
586
  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
587
8
      problem_->get_runningModels();
588
8
  const std::size_t T = problem_->get_T();
589
8
  if (values == NULL) {
590
    // return the structure. This is a symmetric matrix, fill the lower left
591
    // triangle only
592
593
    // Running Costs
594
3
    std::size_t idx = 0;
595
33
    for (std::size_t t = 0; t < problem_->get_T(); ++t) {
596
60
      const boost::shared_ptr<ActionModelAbstract> model = models[t];
597
30
      const std::size_t ndxi = model->get_state()->get_ndx();
598
30
      const std::size_t nui = model->get_nu();
599
300
      for (std::size_t idx_row = 0; idx_row < ndxi + nui; ++idx_row) {
600
1750
        for (std::size_t idx_col = 0; idx_col < ndxi + nui; ++idx_col) {
601
          // We need the lower triangular matrix
602
1720
          if (idx_col > idx_row) {
603
240
            break;
604
          }
605
1480
          iRow[idx] = static_cast<Ipopt::Index>(ixu_[t] + idx_row);
606
1480
          jCol[idx] = static_cast<Ipopt::Index>(ixu_[t] + idx_col);
607
1480
          idx++;
608
        }
609
      }
610
    }
611
612
    // Terminal costs
613
    const std::size_t ndxi =
614
3
        problem_->get_terminalModel()->get_state()->get_ndx();
615
22
    for (std::size_t idx_row = 0; idx_row < ndxi; idx_row++) {
616
97
      for (std::size_t idx_col = 0; idx_col < ndxi; idx_col++) {
617
        // We need the lower triangular matrix
618
94
        if (idx_col > idx_row) {
619
16
          break;
620
        }
621
78
        iRow[idx] = static_cast<Ipopt::Index>(ixu_.back() + idx_row);
622
78
        jCol[idx] = static_cast<Ipopt::Index>(ixu_.back() + idx_col);
623
78
        idx++;
624
      }
625
    }
626
627


3
    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
5
        problem_->get_runningDatas();
636
#ifdef CROCODDYL_WITH_MULTITHREADING
637
#pragma omp parallel for num_threads(problem_->get_nthreads())
638
#endif
639
55
    for (std::size_t t = 0; t < T; ++t) {
640
50
      const boost::shared_ptr<ActionModelAbstract>& model = models[t];
641
50
      const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
642
50
      const std::size_t ndxi = model->get_state()->get_ndx();
643
50
      const std::size_t nui = model->get_nu();
644
50
      datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
645
50
      datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
646
647

50
      model->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
648

100
      model->calcDiff(data, datas_[t]->x,
649
50
                      datas_[t]->u);  // this might be removed
650


100
      model->get_state()->Jintegrate(xs_[t], datas_[t]->dx, datas_[t]->Jint_dx,
651
50
                                     datas_[t]->Jint_dx, second, setto);
652
50
      datas_[t]->Ldxdx.noalias() =
653

100
          datas_[t]->Jint_dx.transpose() * data->Lxx * datas_[t]->Jint_dx;
654

50
      datas_[t]->Ldxu.noalias() = datas_[t]->Jint_dx.transpose() * data->Lxu;
655
    }
656
657
5
    std::size_t idx = 0;
658
55
    for (std::size_t t = 0; t < T; ++t) {
659
50
      const boost::shared_ptr<ActionModelAbstract>& model = models[t];
660
50
      const boost::shared_ptr<ActionDataAbstract>& data = datas[t];
661
50
      const std::size_t ndxi = model->get_state()->get_ndx();
662
50
      const std::size_t nui = model->get_nu();
663
300
      for (std::size_t idx_row = 0; idx_row < ndxi; ++idx_row) {
664
1150
        for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
665
          // We need the lower triangular matrix
666
1100
          if (idx_col > idx_row) {
667
200
            break;
668
          }
669
900
          values[idx] = obj_factor * datas_[t]->Ldxdx(idx_row, idx_col);
670
900
          idx++;
671
        }
672
      }
673
170
      for (std::size_t idx_row = 0; idx_row < nui; ++idx_row) {
674
780
        for (std::size_t idx_col = 0; idx_col < ndxi; ++idx_col) {
675
660
          values[idx] = obj_factor * datas_[t]->Ldxu(idx_col, idx_row);
676
660
          idx++;
677
        }
678
340
        for (std::size_t idx_col = 0; idx_col < nui; ++idx_col) {
679
290
          if (idx_col > idx_row) {
680
70
            break;
681
          }
682
220
          values[idx] = obj_factor * data->Luu(idx_row, idx_col);
683
220
          idx++;
684
        }
685
      }
686
    }
687
688
    // Terminal costs
689
    const boost::shared_ptr<ActionModelAbstract>& model =
690
5
        problem_->get_terminalModel();
691
    const boost::shared_ptr<ActionDataAbstract>& data =
692
5
        problem_->get_terminalData();
693
5
    const std::size_t ndxi = model->get_state()->get_ndx();
694
5
    datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
695
696

5
    model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
697
5
    model->calc(data, datas_[T]->x);
698
5
    model->calcDiff(data, datas_[T]->x);
699


10
    model->get_state()->Jintegrate(xs_[T], datas_[T]->dx, datas_[T]->Jint_dx,
700
5
                                   datas_[T]->Jint_dx, second, setto);
701
5
    datas_[T]->Ldxdx.noalias() =
702

10
        datas_[T]->Jint_dx.transpose() * data->Lxx * datas_[T]->Jint_dx;
703
30
    for (std::size_t idx_row = 0; idx_row < ndxi; idx_row++) {
704
115
      for (std::size_t idx_col = 0; idx_col < ndxi; idx_col++) {
705
        // We need the lower triangular matrix
706
110
        if (idx_col > idx_row) {
707
20
          break;
708
        }
709
90
        values[idx] = datas_[T]->Ldxdx(idx_row, idx_col);
710
90
        idx++;
711
      }
712
    }
713
  }
714
715
8
  return true;
716
}
717
718
3
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
3
      problem_->get_runningModels();
728
3
  const std::size_t T = problem_->get_T();
729
33
  for (std::size_t t = 0; t < T; ++t) {
730
30
    const std::size_t ndxi = models[t]->get_state()->get_ndx();
731
30
    const std::size_t nui = models[t]->get_nu();
732
30
    datas_[t]->dx = Eigen::VectorXd::Map(x + ixu_[t], ndxi);
733
30
    datas_[t]->u = Eigen::VectorXd::Map(x + ixu_[t] + ndxi, nui);
734
735

30
    models[t]->get_state()->integrate(xs_[t], datas_[t]->dx, datas_[t]->x);
736
30
    xs_[t] = datas_[t]->x;
737
30
    us_[t] = datas_[t]->u;
738
  }
739
  // Terminal node
740
  const boost::shared_ptr<ActionModelAbstract>& model =
741
3
      problem_->get_terminalModel();
742
3
  const std::size_t ndxi = model->get_state()->get_ndx();
743
3
  datas_[T]->dx = Eigen::VectorXd::Map(x + ixu_.back(), ndxi);
744

3
  model->get_state()->integrate(xs_[T], datas_[T]->dx, datas_[T]->x);
745
3
  xs_[T] = datas_[T]->x;
746
747
3
  cost_ = obj_value;
748
3
}
749
750
8
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
8
  return true;
759
}
760
761
44
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
88
      Eigen::aligned_allocator<IpoptInterfaceData>(), nx, ndx, nu);
765
}
766
767
3
void IpoptInterface::set_xs(const std::vector<Eigen::VectorXd>& xs) {
768
3
  xs_ = xs;
769
3
}
770
771
3
void IpoptInterface::set_us(const std::vector<Eigen::VectorXd>& us) {
772
3
  us_ = us;
773
3
}
774
775
std::size_t IpoptInterface::get_nvar() const { return nvar_; }
776
777
std::size_t IpoptInterface::get_nconst() const { return nconst_; }
778
779
6
const std::vector<Eigen::VectorXd>& IpoptInterface::get_xs() const {
780
6
  return xs_;
781
}
782
783
6
const std::vector<Eigen::VectorXd>& IpoptInterface::get_us() const {
784
6
  return us_;
785
}
786
787
const boost::shared_ptr<ShootingProblem>& IpoptInterface::get_problem() const {
788
  return problem_;
789
}
790
791
3
double IpoptInterface::get_cost() const { return cost_; }
792
793
}  // namespace crocoddyl