multiple_shooting_edges.h
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
25 #ifndef SRC_OPTIMAL_CONTROL_INCLUDE_CORBO_OPTIMAL_CONTROL_STRUCTURED_OCP_EDGES_MULTIPLE_SHOOTING_EDGES_H_
26 #define SRC_OPTIMAL_CONTROL_INCLUDE_CORBO_OPTIMAL_CONTROL_STRUCTURED_OCP_EDGES_MULTIPLE_SHOOTING_EDGES_H_
27 
29 
35 
36 #include <functional>
37 #include <memory>
38 
39 namespace corbo {
40 
41 // Refer to MSVariableDynamicsOnlyEdge for the variable dt case.
42 // we could always use the variabledt edge, since dt is fixed in that case.
43 // However, we need some benchmarks how much time it requires to always iterate the fixed vertex.. (probably not much?)
44 class MSDynamicsOnlyEdge : public Edge<VectorVertex, VectorVertex, VectorVertex>
45 {
46  public:
47  using Ptr = std::shared_ptr<MSDynamicsOnlyEdge>;
48  using UPtr = std::unique_ptr<MSDynamicsOnlyEdge>;
49 
51  : Edge<VectorVertex, VectorVertex, VectorVertex>(x1, u1, x2), _dt(dt), _dynamics(dynamics)
52  {
53  _x1 = static_cast<const VectorVertex*>(_vertices[0]);
54  _u1 = static_cast<const VectorVertex*>(_vertices[1]);
55  _x2 = static_cast<const VectorVertex*>(_vertices[2]);
56  }
57 
58  // implements interface method
59  int getDimension() const override
60  {
61  assert(_dynamics);
62  return _dynamics->getStateDimension();
63  }
64  // implement in child class:
65  bool isLinear() const override { return false; }
66 
67  // bool providesJacobian() const override { return false; }
68 
69  bool isLeastSquaresForm() const override { return false; }
70 
72  {
73  assert(_integrator);
74  assert(_dynamics);
75  assert(_x1->getDimension() == _dynamics->getStateDimension());
76  assert(_u1->getDimension() == _dynamics->getInputDimension());
77  assert(_x2->getDimension() == _dynamics->getStateDimension());
78 
79  _integrator->computeEqualityConstraint(_x1->values(), _u1->values(), _x2->values(), _dt, *_dynamics, values);
80  }
81 
83 
84  private:
85  double _dt;
88 
89  const VectorVertex* _x1 = nullptr;
90  const VectorVertex* _u1 = nullptr;
91  const VectorVertex* _x2 = nullptr;
92 
93  public:
95 };
96 
97 class MSVariableDynamicsOnlyEdge : public Edge<VectorVertex, VectorVertex, VectorVertex, ScalarVertex>
98 {
99  public:
100  using Ptr = std::shared_ptr<MSVariableDynamicsOnlyEdge>;
101  using UPtr = std::unique_ptr<MSVariableDynamicsOnlyEdge>;
102 
104  : Edge<VectorVertex, VectorVertex, VectorVertex, ScalarVertex>(x1, u1, x2, dt), _dynamics(dynamics)
105  {
106  _x1 = static_cast<const VectorVertex*>(_vertices[0]);
107  _u1 = static_cast<const VectorVertex*>(_vertices[1]);
108  _x2 = static_cast<const VectorVertex*>(_vertices[2]);
109  _dt = static_cast<const ScalarVertex*>(_vertices[3]);
110  }
111 
112  // implements interface method
113  int getDimension() const override
114  {
115  assert(_dynamics);
116  return _dynamics->getStateDimension();
117  }
118  // implement in child class:
119  bool isLinear() const override { return false; }
120 
121  // bool providesJacobian() const override { return false; }
122 
123  bool isLeastSquaresForm() const override { return false; }
124 
126  {
127  assert(_integrator);
128  assert(_dynamics);
129  assert(_x1->getDimension() == _dynamics->getStateDimension());
130  assert(_u1->getDimension() == _dynamics->getInputDimension());
131  assert(_x2->getDimension() == _dynamics->getStateDimension());
132 
133  _integrator->computeEqualityConstraint(_x1->values(), _u1->values(), _x2->values(), _dt->value(), *_dynamics, values);
134  }
135 
137 
138  private:
141 
142  const VectorVertex* _x1 = nullptr;
143  const VectorVertex* _u1 = nullptr;
144  const VectorVertex* _x2 = nullptr;
145  const ScalarVertex* _dt = nullptr;
146 
147  public:
149 };
150 
151 class MultipleShootingEdgeSingleControl : public MixedEdge<VectorVertex, VectorVertex, ScalarVertex, VectorVertex>
152 {
153  public:
154  using Ptr = std::shared_ptr<MultipleShootingEdgeSingleControl>;
155  using UPtr = std::unique_ptr<MultipleShootingEdgeSingleControl>;
156 
159  VectorVertex& x_k, VectorVertex& u_k, ScalarVertex& dt_k, VectorVertex& x_kp1)
160  : MixedEdge<VectorVertex, VectorVertex, ScalarVertex, VectorVertex>(x_k, u_k, dt_k, x_kp1),
161  _stage_cost(stage_cost),
162  _stage_eq(stage_eq),
163  _stage_ineq(stage_ineq),
164  _dynamics(dynamics),
165  _k(k)
166  {
167  assert(_dynamics);
168 
169  _obj_dim = 1; // we have only a single obejtive value
170  _dyn_dim = _dynamics->getStateDimension();
171  _other_eq_dim = _stage_eq ? _stage_eq->getIntegralStateControlTermDimension(_k) : 0;
172  _ineq_dim = _stage_ineq ? _stage_ineq->getIntegralStateControlTermDimension(_k) : 0;
173 
174  _current.resize(_obj_dim + _dyn_dim + _other_eq_dim + _ineq_dim);
175 
176  // TODO(roesmann) see notes at member declaration;
177  _values.resize(_current.size());
178 
179  _xkvert = static_cast<const VectorVertex*>(_vertices[0]);
180  _ukvert = static_cast<const VectorVertex*>(_vertices[1]);
181  _dtvert = static_cast<const ScalarVertex*>(_vertices[2]);
182  _xnextvert = static_cast<const VectorVertex*>(_vertices[3]);
183 
184  configureIntegrand();
185  }
186 
187  // implements interface method
188  int getObjectiveDimension() const override { return _obj_dim; }
189  // implements interface method
190  int getEqualityDimension() const override { return _dyn_dim + _other_eq_dim; }
191  // implements interface method
192  int getInequalityDimension() const override { return _ineq_dim; }
193 
194  // implement in child class:
195  // TODO(roesmann) implement if appropriate
196  bool isObjectiveLinear() const override { return false; }
197  bool isEqualityLinear() const override { return false; }
198  bool isInequalityLinear() const override { return false; }
199 
200  // bool providesJacobian() const override { return false; }
201 
202  // TODO(roesmann): integral of squared functions is not always the same as the squared integral of functions
203  bool isObjectiveLeastSquaresForm() const override { return false; }
204 
205  void precompute() override
206  {
207  assert(_integrator);
208  assert(_dynamics);
209 
210  assert(_xkvert->getDimension() == _dynamics->getStateDimension());
211  assert(_ukvert->getDimension() == _dynamics->getInputDimension());
212 
213  // get current values (we must avoid alias, hence we need to copy)
214  if (_obj_dim > 0) _current[0] = 0;
215  _current.segment(_obj_dim, _dyn_dim) = _xkvert->values();
216  int tail = _other_eq_dim + _ineq_dim;
217  if (tail > 0) _current.tail(tail).setZero();
218 
219  // TODO(roesmann) check if we can ensure to avoid alias in the integrator part to speed up simple integrators
220  // first simple tests with replacing _values by _current led to different (wrong) results
221  // _integrator->integrate(_current, _dtvert->value(), _integrand, _values);
222  _integrator->solveIVP(_current, _dtvert->value(), _integrand, _values);
223  }
225  {
226  if (_obj_dim > 0) obj_values[0] = _values[0];
227  }
229  {
230  // system dynamics result must comply with subsequent state
231  assert(_xnextvert->values().size() == _dyn_dim);
232 
233  // _values.segment(_obj_dim, _dyn_dim) -= x_next->values();
234  eq_values.head(_dyn_dim).noalias() = _values.segment(_obj_dim, _dyn_dim) - _xnextvert->values();
235  eq_values.tail(_other_eq_dim) = _values.segment(_obj_dim + _dyn_dim, _other_eq_dim);
236  }
238  {
239  if (_ineq_dim > 0) ineq_values = _values.tail(_ineq_dim);
240  }
241 
243 
244  protected:
246  {
247  _integrand = [&](const Eigen::VectorXd& current, Eigen::Ref<Eigen::VectorXd> result) {
248  assert(_ukvert);
249  assert(_dtvert);
250 
251  int idx = 0;
252  _dynamics->dynamics(current.segment(_obj_dim, _dyn_dim), _ukvert->values(), result.segment(_obj_dim, _dyn_dim));
253  if (_obj_dim > 0)
254  {
255  assert(_obj_dim == 1);
256  _stage_cost->computeIntegralStateControlTerm(_k, current.segment(_obj_dim, _dyn_dim), _ukvert->values(), result.head(1));
257  idx += 1;
258  }
259  idx += _dyn_dim;
260  if (_other_eq_dim > 0)
261  {
262  _stage_eq->computeIntegralStateControlTerm(_k, current.segment(_obj_dim, _dyn_dim), _ukvert->values(),
263  result.segment(idx, _other_eq_dim));
264  idx += _other_eq_dim;
265  }
266  if (_ineq_dim)
267  {
268  _stage_ineq->computeIntegralStateControlTerm(_k, current.segment(_obj_dim, _dyn_dim), _ukvert->values(),
269  result.segment(idx, _ineq_dim));
270  idx += _ineq_dim;
271  }
272  assert(idx == result.size());
273  };
274  }
275 
276  private:
277  const VectorVertex* _xkvert = nullptr;
278  const VectorVertex* _ukvert = nullptr;
279  const ScalarVertex* _dtvert = nullptr;
280  const VectorVertex* _xnextvert = nullptr;
281 
282  int _dyn_dim = 0;
283  int _obj_dim = 0;
284  int _other_eq_dim = 0;
285  int _ineq_dim = 0;
286 
287  Eigen::VectorXd _current;
288  Eigen::VectorXd _values; // TODO(roesmann): if integrators are alias safe we can remove _values in favor of just _current
289 
290  std::function<void(const Eigen::VectorXd&, Eigen::Ref<Eigen::VectorXd>)> _integrand;
291 
295 
298 
299  int _k = 0;
300 
301  public:
303 };
304 
305 class MultipleShootingEdgeSingleControlTimeScaling : public MixedEdge<VectorVertex, VectorVertex, ScalarVertex, VectorVertex>
306 {
307  public:
308  using Ptr = std::shared_ptr<MultipleShootingEdgeSingleControlTimeScaling>;
309  using UPtr = std::unique_ptr<MultipleShootingEdgeSingleControlTimeScaling>;
310 
313  int k, VectorVertex& x_k, VectorVertex& u_k, ScalarVertex& time, VectorVertex& x_kp1,
314  double dt)
315  : MixedEdge<VectorVertex, VectorVertex, ScalarVertex, VectorVertex>(x_k, u_k, time, x_kp1),
316  _stage_cost(stage_cost),
317  _stage_eq(stage_eq),
318  _stage_ineq(stage_ineq),
319  _dynamics(dynamics),
320  _dt(dt),
321  _k(k)
322  {
323  assert(_dynamics);
324 
325  _obj_dim = 1; // we have only a single obejtive value
326  _dyn_dim = _dynamics->getStateDimension();
327  _other_eq_dim = _stage_eq ? _stage_eq->getIntegralStateControlTermDimension(_k) : 0;
328  _ineq_dim = _stage_ineq ? _stage_ineq->getIntegralStateControlTermDimension(_k) : 0;
329 
330  _current.resize(_obj_dim + _dyn_dim + _other_eq_dim + _ineq_dim);
331 
332  // TODO(roesmann) see notes at member declaration;
333  _values.resize(_current.size());
334 
335  _xkvert = static_cast<const VectorVertex*>(_vertices[0]);
336  _ukvert = static_cast<const VectorVertex*>(_vertices[1]);
337  _timevert = static_cast<const ScalarVertex*>(_vertices[2]);
338  _xnextvert = static_cast<const VectorVertex*>(_vertices[3]);
339 
340  configureIntegrand();
341  }
342 
343  // implements interface method
344  int getObjectiveDimension() const override { return _obj_dim; }
345  // implements interface method
346  int getEqualityDimension() const override { return _dyn_dim + _other_eq_dim; }
347  // implements interface method
348  int getInequalityDimension() const override { return _ineq_dim; }
349 
350  // implement in child class:
351  // TODO(roesmann) implement if appropriate
352  bool isObjectiveLinear() const override { return false; }
353  bool isEqualityLinear() const override { return false; }
354  bool isInequalityLinear() const override { return false; }
355 
356  // bool providesJacobian() const override { return false; }
357 
358  // TODO(roesmann): integral of squared functions is not always the same as the squared integral of functions
359  bool isObjectiveLeastSquaresForm() const override { return false; }
360 
361  void precompute() override
362  {
363  assert(_integrator);
364  assert(_dynamics);
365 
366  assert(_xkvert->getDimension() == _dynamics->getStateDimension());
367  assert(_ukvert->getDimension() == _dynamics->getInputDimension());
368 
369  // get current values (we must avoid alias, hence we need to copy)
370  if (_obj_dim > 0) _current[0] = 0;
371  _current.segment(_obj_dim, _dyn_dim) = _xkvert->values();
372  int tail = _other_eq_dim + _ineq_dim;
373  if (tail > 0) _current.tail(tail).setZero();
374 
375  // TODO(roesmann) check if we can ensure to avoid alias in the integrator part to speed up simple integrators
376  // first simple tests with replacing _values by _current led to different (wrong) results
377  // _integrator->integrate(_current, _dtvert->value(), _integrand, _values);
378  _integrator->solveIVP(_current, _dt, _integrand, _values);
379  }
381  {
382  if (_obj_dim > 0) obj_values[0] = _values[0];
383  }
385  {
386  // system dynamics result must comply with subsequent state
387  assert(_xnextvert->values().size() == _dyn_dim);
388 
389  // _values.segment(_obj_dim, _dyn_dim) -= x_next->values();
390  eq_values.head(_dyn_dim).noalias() = _values.segment(_obj_dim, _dyn_dim) - _xnextvert->values();
391  eq_values.tail(_other_eq_dim) = _values.segment(_obj_dim + _dyn_dim, _other_eq_dim);
392  }
394  {
395  if (_ineq_dim > 0) ineq_values = _values.tail(_ineq_dim);
396  }
397 
399 
400  protected:
402  {
403  _integrand = [&](const Eigen::VectorXd& current, Eigen::Ref<Eigen::VectorXd> result) {
404  assert(_ukvert);
405  assert(_timevert);
406 
407  int idx = 0;
408  _dynamics->dynamics(current.segment(_obj_dim, _dyn_dim), _ukvert->values(), result.segment(_obj_dim, _dyn_dim));
409  // time scaling:
410  result.segment(_obj_dim, _dyn_dim) *= _timevert->value();
411 
412  if (_obj_dim > 0)
413  {
414  assert(_obj_dim == 1);
415  _stage_cost->computeIntegralStateControlTerm(_k, current.segment(_obj_dim, _dyn_dim), _ukvert->values(), result.head(1));
416  idx += 1;
417  }
418  idx += _dyn_dim;
419  if (_other_eq_dim > 0)
420  {
421  _stage_eq->computeIntegralStateControlTerm(_k, current.segment(_obj_dim, _dyn_dim), _ukvert->values(),
422  result.segment(idx, _other_eq_dim));
423  idx += _other_eq_dim;
424  }
425  if (_ineq_dim)
426  {
427  _stage_ineq->computeIntegralStateControlTerm(_k, current.segment(_obj_dim, _dyn_dim), _ukvert->values(),
428  result.segment(idx, _ineq_dim));
429  idx += _ineq_dim;
430  }
431  assert(idx == result.size());
432  };
433  }
434 
435  private:
436  const VectorVertex* _xkvert = nullptr;
437  const VectorVertex* _ukvert = nullptr;
438  const ScalarVertex* _timevert = nullptr;
439  const VectorVertex* _xnextvert = nullptr;
440 
441  int _dyn_dim = 0;
442  int _obj_dim = 0;
443  int _other_eq_dim = 0;
444  int _ineq_dim = 0;
445 
446  Eigen::VectorXd _current;
447  Eigen::VectorXd _values; // TODO(roesmann): if integrators are alias safe we can remove _values in favor of just _current
448 
449  std::function<void(const Eigen::VectorXd&, Eigen::Ref<Eigen::VectorXd>)> _integrand;
450 
454 
457 
458  double _dt = 0.1;
459 
460  int _k = 0;
461 
462  public:
464 };
465 
466 // vertex order:
467 // vertex: sk
468 // vertex: sk+1
469 // vertex: u[0]
470 // vertex: u[1]
471 // vertex: u[...]
472 // vertex: u[nc-1]
473 // vertex: dt[0]
474 // vertex: dt[1]
475 // vertex: dt[...]
476 // vertex: dt[nt-1]
478 {
479  public:
480  using Ptr = std::shared_ptr<MultipleShootingEdge>;
481  using UPtr = std::unique_ptr<MultipleShootingEdge>;
482 
484  StageInequalityConstraint::ConstPtr stage_ineq, int k, int n_interval, int nc_interval, int nt_interval,
485  bool eval_intermediate_constr)
486  : MixedEdge<>(2 + nc_interval + nt_interval),
487  _stage_cost(stage_cost),
488  _stage_eq(stage_eq),
489  _stage_ineq(stage_ineq),
490  _n_interval(n_interval),
491  _nc_interval(nc_interval),
492  _nt_interval(nt_interval),
493  _eval_intermediate_constr(eval_intermediate_constr),
494  _dynamics(dynamics),
495  _k(k)
496  {
497  }
498 
499  void finalize()
500  {
501  assert(_dynamics);
502  assert((int)_vertices.size() == 2 + _nc_interval + _nt_interval);
503 
504  _shooting_node = static_cast<const VectorVertex*>(_vertices[0]);
505 
506  _int_obj_dim = (_stage_cost && _stage_cost->getIntegralStateControlTermDimension(_k) > 0) ? 1 : 0; // we have only a single obejtive value
507  _dyn_dim = _dynamics->getStateDimension();
508 
509  _other_int_eq_dim = _stage_eq ? _stage_eq->getIntegralStateControlTermDimension(_k) : 0;
510  _int_ineq_dim = _stage_ineq ? _stage_ineq->getIntegralStateControlTermDimension(_k) : 0;
511 
512  _current.resize(_int_obj_dim + _dyn_dim + _other_int_eq_dim + _int_ineq_dim);
513 
514  // TODO(roesmann) see notes at member declaration;
515  _values.resize(_current.size());
516 
517  // chache total inequality dimension
518  _total_dim_obj = _int_obj_dim;
519  _total_dim_eq = _dyn_dim + _other_int_eq_dim;
520  _total_dim_ineq = _int_ineq_dim;
521 
522  activateIntermediateStateCostAndConstraints();
523 
524  configureIntegrand();
525  }
526 
527  // implements interface method
528  int getObjectiveDimension() const override { return _total_dim_obj; }
529  // implements interface method
530  int getEqualityDimension() const override { return _total_dim_eq; }
531  // implements interface method
532  int getInequalityDimension() const override { return _total_dim_ineq; }
533 
534  // implement in child class:
535  bool isObjectiveLinear() const override { return false; }
536  bool isEqualityLinear() const override { return false; }
537  bool isInequalityLinear() const override { return false; }
538 
539  // bool providesJacobian() const override { return false; }
540 
541  // TODO(roesmann): integral of squared functions is not always the same as the squared integral of functions
542  bool isObjectiveLeastSquaresForm() const override { return false; }
543 
544  void precompute() override
545  {
546  assert(_integrator);
547  assert(_dynamics);
548  assert((int)_vertices.size() == 2 + _nc_interval + _nt_interval);
549 
550  assert(_shooting_node->getDimension() == _dynamics->getStateDimension());
551 
552  // get current values (we must avoid alias, hence we need to copy)
553  if (_int_obj_dim > 0) _current[0] = 0;
554  _current.segment(_int_obj_dim, _dyn_dim) = _shooting_node->values();
555  int tail = _other_int_eq_dim + _int_ineq_dim;
556  if (tail > 0) _current.tail(tail).setZero();
557 
558  _current_idx = 0;
559  for (; _current_idx < _n_interval - 1; ++_current_idx)
560  {
561  if (_current_idx < _nc_interval) _ukvert = static_cast<VectorVertex*>(_vertices[_current_idx + 2]);
562  if (_current_idx < _nt_interval) _dtvert = static_cast<ScalarVertex*>(_vertices[_current_idx + _nc_interval + 2]);
563 
564  assert(_ukvert->getDimension() == _dynamics->getInputDimension());
565 
566  // compute integral stuff
567  // _integrator->integrate(_current, _dtvert->value(), _integrand, _values);
568  _integrator->solveIVP(_current, _dtvert->value(), _integrand, _values);
569 
570  // TODO(roesmann) check if we can ensure to avoid alias in the integrator part to speed up simple integrators
571  // first simple tests with replacing _values by _current led to different (wrong) results
572  if (_current_idx < _n_interval - 2)
573  {
574  _current = _values;
575 
576  // also compute intermediate state dependend stuff
577  if (_eval_intermediate_constr)
578  {
579  const int k = _k + _current_idx;
580  Eigen::Ref<const Eigen::VectorXd> xk = _values.segment(_int_obj_dim, _dyn_dim);
581 
582  if (_nonint_obj_dim > 0)
583  {
584  Eigen::Ref<Eigen::VectorXd> cost_segment = _nonint_obj_values.segment(_current_idx * _nonint_obj_dim, _nonint_obj_dim);
585  _stage_cost->computeConcatenatedNonIntegralStateTerms(k, xk, _ukvert->values(), _dtvert->value(), cost_segment, true);
586  }
587 
588  if (_nonint_eq_dim > 0)
589  {
590  Eigen::Ref<Eigen::VectorXd> eq_segment = _nonint_eq_values.segment(_current_idx * _nonint_eq_dim, _nonint_eq_dim);
591  _stage_eq->computeConcatenatedNonIntegralStateTerms(k, xk, _ukvert->values(), _dtvert->value(), eq_segment, false);
592  }
593 
594  if (_nonint_ineq_dim > 0)
595  {
596  Eigen::Ref<Eigen::VectorXd> ineq_segment = _nonint_eq_values.segment(_current_idx * _nonint_ineq_dim, _nonint_ineq_dim);
597  _stage_ineq->computeConcatenatedNonIntegralStateTerms(k, xk, _ukvert->values(), _dtvert->value(), ineq_segment, false);
598  }
599 
600  // lower bounds
601  if (_dim_lb_x > 0)
602  {
603  int lb_idx = 0;
604  for (int j = 0; j < _shooting_node->getDimension(); ++j)
605  {
606  if (_shooting_node->hasFiniteLowerBound(j))
607  {
608  _lb_ineq_values(_current_idx * _dim_lb_x + lb_idx) = _shooting_node->lowerBound()[j] - xk[j]; // x_min - x <= 0
609  ++lb_idx;
610  }
611  }
612  }
613 
614  // upper bounds
615  if (_dim_ub_x > 0)
616  {
617  int ub_idx = 0;
618  for (int j = 0; j < _shooting_node->getDimension(); ++j)
619  {
620  if (_shooting_node->hasFiniteUpperBound(j))
621  {
622  _ub_ineq_values(_current_idx * _dim_ub_x + ub_idx) = xk[j] - _shooting_node->upperBound()[j]; // x - x_max <= 0
623  ++ub_idx;
624  }
625  }
626  }
627  }
628  }
629  }
630  }
631 
633  {
634  if (_int_obj_dim > 0 && _nonint_obj_dim > 0)
635  {
636  obj_values[0] = _values[0] + _nonint_obj_values.sum();
637  }
638  else if (_int_obj_dim > 0)
639  {
640  obj_values[0] = _values[0];
641  }
642  else if (_nonint_obj_dim > 0)
643  {
644  obj_values[0] = _nonint_obj_values.sum();
645  }
646  }
648  {
649  // system dynamics result must comply with subsequent state
650  const VectorVertex* x_next = static_cast<const VectorVertex*>(_vertices[1]);
651  assert(x_next->values().size() == _dyn_dim);
652 
653  eq_values.head(_dyn_dim).noalias() = _values.segment(_int_obj_dim, _dyn_dim) - x_next->values();
654 
655  if (_other_int_eq_dim > 0) eq_values.segment(_dyn_dim, _other_int_eq_dim) = _values.segment(_int_obj_dim + _dyn_dim, _other_int_eq_dim);
656 
657  if (_eval_intermediate_constr)
658  {
659  assert(_num_intermediate_states > 0);
660  if (_nonint_eq_dim > 0) eq_values.segment(_dyn_dim + _other_int_eq_dim, _num_intermediate_states * _nonint_eq_dim) = _nonint_eq_values;
661  }
662 
663  assert(eq_values.size() == _dyn_dim + _other_int_eq_dim + _num_intermediate_states * _nonint_eq_dim);
664  }
666  {
667  if (_int_ineq_dim > 0) ineq_values.head(_int_ineq_dim) = _values.tail(_int_ineq_dim);
668 
669  if (_eval_intermediate_constr)
670  {
671  assert(_num_intermediate_states > 0);
672  int idx = _int_ineq_dim;
673  if (_nonint_ineq_dim > 0)
674  {
675  ineq_values.segment(idx, _num_intermediate_states * _nonint_ineq_dim) = _nonint_ineq_values;
676  idx += _num_intermediate_states * _nonint_ineq_dim;
677  }
678 
679  // bounds
680  if (_dim_lb_x > 0)
681  {
682  ineq_values.segment(idx, _num_intermediate_states * _dim_lb_x) = _lb_ineq_values;
683  // idx += _num_intermediate_states * _nonint_ineq_dim;
684  }
685  if (_dim_ub_x > 0)
686  {
687  ineq_values.tail(_num_intermediate_states * _dim_ub_x) = _ub_ineq_values;
688  // idx += _num_intermediate_states * _nonint_ineq_dim;
689  }
690  }
691 
692  assert(ineq_values.size() == _int_ineq_dim + _num_intermediate_states * (_nonint_ineq_dim + _dim_lb_x + _dim_ub_x));
693  }
694 
696 
697  protected:
699  {
700  if (!_eval_intermediate_constr) return;
701  _num_intermediate_states = _n_interval - 2; // no start and state
702  if (_num_intermediate_states < 1)
703  {
704  _eval_intermediate_constr = false;
705  return;
706  }
707 
708  // TODO(roesmann): we assume for now that all terms have the same dimension _k!!!
709  _nonint_obj_dim = _stage_cost ? _stage_cost->getConcatenatedNonIntegralStateTermDimension(_k, true) : 0;
710  _nonint_eq_dim = _stage_eq ? _stage_eq->getConcatenatedNonIntegralStateTermDimension(_k, false) : 0;
711  _nonint_ineq_dim = _stage_ineq ? _stage_ineq->getConcatenatedNonIntegralStateTermDimension(_k, false) : 0;
712 
713  // consider number of finite bounds for all vertices
714  // we apply the bounds of vertex x1
715  const VectorVertex* x1 = static_cast<const VectorVertex*>(_vertices[0]);
716  _dim_lb_x = x1->getNumberFiniteLowerBounds(false);
717  _dim_ub_x = x1->getNumberFiniteUpperBounds(false);
718 
719  // update overall dimensions
720  _total_dim_obj = (_int_obj_dim > 0 || _nonint_obj_dim > 0) ? 1 : 0;
721  _total_dim_eq = _other_int_eq_dim + _dyn_dim + _num_intermediate_states * _nonint_eq_dim;
722  _total_dim_ineq = _int_ineq_dim + _num_intermediate_states * (_nonint_ineq_dim + _dim_lb_x + _dim_ub_x);
723 
724  // allocate caches
725  _nonint_obj_values.resize(_num_intermediate_states * _nonint_obj_dim);
726  _nonint_eq_values.resize(_num_intermediate_states * _nonint_eq_dim);
727  _nonint_ineq_values.resize(_num_intermediate_states * _nonint_ineq_dim);
728  _lb_ineq_values.resize(_num_intermediate_states * _dim_lb_x);
729  _ub_ineq_values.resize(_num_intermediate_states * _dim_ub_x);
730  }
731 
733  {
734  _integrand = [&](const Eigen::VectorXd& current, Eigen::Ref<Eigen::VectorXd> result) {
735  assert(_ukvert);
736  assert(_dtvert);
737 
738  const int k_integrand = _k + _current_idx;
739 
740  int idx = 0;
741  _dynamics->dynamics(current.segment(_int_obj_dim, _dyn_dim), _ukvert->values(), result.segment(_int_obj_dim, _dyn_dim));
742  if (_int_obj_dim > 0)
743  {
744  assert(_int_obj_dim == 1);
745  _stage_cost->computeIntegralStateControlTerm(k_integrand, current.segment(_int_obj_dim, _dyn_dim), _ukvert->values(), result.head(1));
746  idx += 1;
747  }
748  idx += _dyn_dim;
749  if (_other_int_eq_dim > 0)
750  {
751  _stage_eq->computeIntegralStateControlTerm(k_integrand, current.segment(_int_obj_dim, _dyn_dim), _ukvert->values(),
752  result.segment(idx, _other_int_eq_dim));
753  idx += _other_int_eq_dim;
754  }
755  if (_int_ineq_dim)
756  {
757  _stage_ineq->computeIntegralStateControlTerm(k_integrand, current.segment(_int_obj_dim, _dyn_dim), _ukvert->values(),
758  result.segment(idx, _int_ineq_dim));
759  idx += _int_ineq_dim;
760  }
761  assert(idx == result.size());
762  };
763  }
764 
765  private:
766  const VectorVertex* _shooting_node = nullptr;
767  VectorVertex* _ukvert = nullptr;
768  ScalarVertex* _dtvert = nullptr;
769 
770  int _dyn_dim = 0;
771 
772  int _int_obj_dim = 0;
773  int _other_int_eq_dim = 0;
774  int _int_ineq_dim = 0;
775 
776  int _nonint_obj_dim = 0;
777  int _nonint_eq_dim = 0;
778  int _nonint_ineq_dim = 0;
779 
780  int _dim_lb_x = 0;
781  int _dim_ub_x = 0;
782 
783  int _total_dim_obj = 0;
784  int _total_dim_eq = 0;
785  int _total_dim_ineq = 0;
786 
787  int _n_interval = 0;
788  int _nc_interval = 0;
789  int _nt_interval = 0;
790  int _num_intermediate_states = 0;
791  int _current_idx = 0;
792 
793  bool _eval_intermediate_constr = false;
794 
795  Eigen::VectorXd _current;
796  Eigen::VectorXd _values; // TODO(roesmann): if integrators are alias safe we can remove _values in favor of just _current
797 
798  Eigen::VectorXd _nonint_obj_values;
799  Eigen::VectorXd _nonint_ineq_values;
800  Eigen::VectorXd _nonint_eq_values;
801  Eigen::VectorXd _lb_ineq_values;
802  Eigen::VectorXd _ub_ineq_values;
803 
804  std::function<void(const Eigen::VectorXd&, Eigen::Ref<Eigen::VectorXd>)> _integrand;
805 
809 
812 
813  int _k = 0;
814 
815  public:
817 };
818 
820 {
821  public:
822  using Ptr = std::shared_ptr<MSDynamicsOnlyMultiControlsEdge>;
823  using UPtr = std::unique_ptr<MSDynamicsOnlyMultiControlsEdge>;
824 
825  explicit MSDynamicsOnlyMultiControlsEdge(SystemDynamicsInterface::Ptr dynamics, int num_controls) : Edge<>(3 + num_controls), _dynamics(dynamics)
826  {
827  }
828 
829  void finalize()
830  {
831  _x1 = static_cast<const VectorVertex*>(_vertices[0]);
832  _x2 = static_cast<const VectorVertex*>(_vertices[1]);
833  _dt = static_cast<const ScalarVertex*>(_vertices[2]);
834 
835  _f.resize(_dynamics->getStateDimension());
836  }
837 
838  // implements interface method
839  int getDimension() const override
840  {
841  assert(_dynamics);
842  return _dynamics->getStateDimension();
843  }
844  // implement in child class:
845  bool isLinear() const override { return false; }
846 
847  // bool providesJacobian() const override { return false; }
848 
849  bool isLeastSquaresForm() const override { return false; }
850 
852  {
853  assert(_integrator);
854  assert(_dynamics);
855  assert(_x1->getDimension() == _dynamics->getStateDimension());
856  assert(_x2->getDimension() == _dynamics->getStateDimension());
857  assert(_vertices.size() > 4);
858 
859  const VectorVertex* u = static_cast<const VectorVertex*>(_vertices[3]);
860  _integrator->solveIVP(_x1->values(), u->values(), _dt->value(), *_dynamics, _f);
861 
862  for (int i = 4; i < (int)_vertices.size(); ++i)
863  {
864  u = static_cast<const VectorVertex*>(_vertices[i]);
865  _integrator->solveIVP(_f, u->values(), _dt->value(), *_dynamics, values);
866  _f = values; // TODO(roesmann): avoid this by checking the indices maybe?!
867  }
868  values -= _x2->values();
869  }
870 
872 
873  private:
876 
877  Eigen::VectorXd _f;
878 
879  const VectorVertex* _x1 = nullptr;
880  const VectorVertex* _x2 = nullptr;
881  const ScalarVertex* _dt = nullptr;
882 
883  public:
885 };
886 
888 {
889  public:
890  using Ptr = std::shared_ptr<MSDynamicsOnlyMultiControlsMultiDtsEdge>;
891  using UPtr = std::unique_ptr<MSDynamicsOnlyMultiControlsMultiDtsEdge>;
892 
894  : Edge<>(2 + 2 * num_controls), _dynamics(dynamics)
895  {
896  setVertex(0, x1);
897  setVertex(1, x2);
898  _x1 = static_cast<const VectorVertex*>(_vertices[0]);
899  _x2 = static_cast<const VectorVertex*>(_vertices[1]);
900 
901  _f.resize(dynamics->getStateDimension());
902  }
903 
904  void finalize() {}
905 
906  // implements interface method
907  int getDimension() const override
908  {
909  assert(_dynamics);
910  return _dynamics->getStateDimension();
911  }
912  // implement in child class:
913  bool isLinear() const override { return false; }
914 
915  // bool providesJacobian() const override { return false; }
916 
917  bool isLeastSquaresForm() const override { return false; }
918 
920  {
921  assert(_integrator);
922  assert(_dynamics);
923  assert(_x1->getDimension() == _dynamics->getStateDimension());
924  assert(_x2->getDimension() == _dynamics->getStateDimension());
925  assert(_vertices.size() > 5);
926 
927  const VectorVertex* u = static_cast<const VectorVertex*>(_vertices[2]);
928  const ScalarVertex* dt = static_cast<const ScalarVertex*>(_vertices[3]);
929  _integrator->solveIVP(_x1->values(), u->values(), dt->value(), *_dynamics, values);
930 
931  for (int i = 4; i < (int)_vertices.size() - 2; i = i + 2)
932  {
933  u = static_cast<const VectorVertex*>(_vertices[i]);
934  dt = static_cast<const ScalarVertex*>(_vertices[i + 1]);
935  _integrator->solveIVP(values, u->values(), dt->value(), *_dynamics, _f);
936  }
937  u = static_cast<const VectorVertex*>(_vertices[(int)_vertices.size() - 2]);
938  dt = static_cast<const ScalarVertex*>(_vertices.back());
939  _integrator->solveIVP(_f, u->values(), dt->value(), *_dynamics, values);
940  values -= _x2->values();
941  }
942 
944 
945  private:
948 
949  Eigen::VectorXd _f;
950 
951  const VectorVertex* _x1 = nullptr;
952  const VectorVertex* _x2 = nullptr;
953 
954  public:
956 };
957 
958 } // namespace corbo
959 
960 #endif // SRC_OPTIMAL_CONTROL_INCLUDE_CORBO_OPTIMAL_CONTROL_STRUCTURED_OCP_EDGES_MULTIPLE_SHOOTING_EDGES_H_
StageEqualityConstraint::ConstPtr _stage_eq
int getEqualityDimension() const override
MultipleShootingEdgeSingleControl(SystemDynamicsInterface::Ptr dynamics, StageCost::ConstPtr stage_cost, StageEqualityConstraint::ConstPtr stage_eq, StageInequalityConstraint::ConstPtr stage_ineq, int k, VectorVertex &x_k, VectorVertex &u_k, ScalarVertex &dt_k, VectorVertex &x_kp1)
bool isLeastSquaresForm() const override
Defines if the edge is formulated as Least-Squares form.
void computeEqualityValues(Eigen::Ref< Eigen::VectorXd > eq_values) override
void computeValues(Eigen::Ref< Eigen::VectorXd > values) override
Compute function values.
std::shared_ptr< MSDynamicsOnlyMultiControlsEdge > Ptr
return int(ret)+1
bool isObjectiveLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
bool isObjectiveLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
std::unique_ptr< MultipleShootingEdge > UPtr
MSVariableDynamicsOnlyEdge(SystemDynamicsInterface::Ptr dynamics, VectorVertex &x1, VectorVertex &u1, VectorVertex &x2, ScalarVertex &dt)
void computeValues(Eigen::Ref< Eigen::VectorXd > values) override
Compute function values.
int getInequalityDimension() const override
NumericalIntegratorExplicitInterface::Ptr _integrator
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW
Definition: Memory.h:690
void computeEqualityValues(Eigen::Ref< Eigen::VectorXd > eq_values) override
std::unique_ptr< MultipleShootingEdgeSingleControlTimeScaling > UPtr
int getNumberFiniteLowerBounds(bool unfixed_only) const override
Get number of finite lower bounds.
Vertex implementation for scalar values.
Definition: scalar_vertex.h:50
bool isLeastSquaresForm() const override
Defines if the edge is formulated as Least-Squares form.
std::shared_ptr< MultipleShootingEdge > Ptr
MSDynamicsOnlyMultiControlsMultiDtsEdge(SystemDynamicsInterface::Ptr dynamics, VectorVertex &x1, VectorVertex &x2, int num_controls)
int getNumberFiniteUpperBounds(bool unfixed_only) const override
Get number of finite upper bounds.
NumericalIntegratorExplicitInterface::Ptr _integrator
MultipleShootingEdge(SystemDynamicsInterface::Ptr dynamics, StageCost::ConstPtr stage_cost, StageEqualityConstraint::ConstPtr stage_eq, StageInequalityConstraint::ConstPtr stage_ineq, int k, int n_interval, int nc_interval, int nt_interval, bool eval_intermediate_constr)
StageInequalityConstraint::ConstPtr _stage_ineq
MSDynamicsOnlyMultiControlsEdge(SystemDynamicsInterface::Ptr dynamics, int num_controls)
NumericalIntegratorExplicitInterface::Ptr _integrator
SystemDynamicsInterface::Ptr _dynamics
void computeInequalityValues(Eigen::Ref< Eigen::VectorXd > ineq_values) override
SystemDynamicsInterface::Ptr _dynamics
void computeObjectiveValues(Eigen::Ref< Eigen::VectorXd > obj_values) override
std::shared_ptr< MSVariableDynamicsOnlyEdge > Ptr
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
bool isObjectiveLeastSquaresForm() const override
std::shared_ptr< MultipleShootingEdgeSingleControl > Ptr
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
bool isInequalityLinear() const override
std::unique_ptr< MSDynamicsOnlyMultiControlsMultiDtsEdge > UPtr
std::shared_ptr< const StageInequalityConstraint > ConstPtr
void computeValues(Eigen::Ref< Eigen::VectorXd > values) override
Compute function values.
std::shared_ptr< const StageEqualityConstraint > ConstPtr
std::unique_ptr< MSDynamicsOnlyMultiControlsEdge > UPtr
int getDimension() const override
Get dimension of the edge (dimension of the cost-function/constraint value vector) ...
StageInequalityConstraint::ConstPtr _stage_ineq
NumericalIntegratorExplicitInterface::Ptr _integrator
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
MultipleShootingEdgeSingleControlTimeScaling(SystemDynamicsInterface::Ptr dynamics, StageCost::ConstPtr stage_cost, StageEqualityConstraint::ConstPtr stage_eq, StageInequalityConstraint::ConstPtr stage_ineq, int k, VectorVertex &x_k, VectorVertex &u_k, ScalarVertex &time, VectorVertex &x_kp1, double dt)
void computeInequalityValues(Eigen::Ref< Eigen::VectorXd > ineq_values) override
void computeObjectiveValues(Eigen::Ref< Eigen::VectorXd > obj_values) override
int getObjectiveDimension() const override
int getDimension() const override
Return number of elements/values/components stored in this vertex.
Definition: vector_vertex.h:96
std::unique_ptr< MultipleShootingEdgeSingleControl > UPtr
bool isLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
const double & value() const
Get underlying value.
std::shared_ptr< NumericalIntegratorExplicitInterface > Ptr
bool isEqualityLinear() const override
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:192
int getDimension() const override
Get dimension of the edge (dimension of the cost-function/constraint value vector) ...
NumericalIntegratorExplicitInterface::Ptr _integrator
std::shared_ptr< MSDynamicsOnlyEdge > Ptr
std::unique_ptr< MSDynamicsOnlyEdge > UPtr
bool isLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
MSDynamicsOnlyEdge(SystemDynamicsInterface::Ptr dynamics, VectorVertex &x1, VectorVertex &u1, VectorVertex &x2, double dt)
bool isLeastSquaresForm() const override
Defines if the edge is formulated as Least-Squares form.
EIGEN_DEVICE_FUNC SegmentReturnType tail(Index n)
This is the const version of tail(Index).
Definition: BlockMethods.h:949
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
void computeValues(Eigen::Ref< Eigen::VectorXd > values) override
Compute function values.
bool isObjectiveLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
Templated base edge class that stores an arbitary number of value.
Definition: edge.h:148
int getDimension() const override
Get dimension of the edge (dimension of the cost-function/constraint value vector) ...
std::unique_ptr< MSVariableDynamicsOnlyEdge > UPtr
std::shared_ptr< MultipleShootingEdgeSingleControlTimeScaling > Ptr
Vertex implementation that stores an Eigen::VectorXd (dynamic dimension)
Definition: vector_vertex.h:51
SystemDynamicsInterface::Ptr _dynamics
NumericalIntegratorExplicitInterface::Ptr _integrator
const Eigen::VectorXd & values() const
Read-access to the underlying value vector.
const VertexContainer _vertices
Vertex container.
Definition: edge.h:214
NumericalIntegratorExplicitInterface::Ptr _integrator
void computeEqualityValues(Eigen::Ref< Eigen::VectorXd > eq_values) override
bool isLeastSquaresForm() const override
Defines if the edge is formulated as Least-Squares form.
void computeObjectiveValues(Eigen::Ref< Eigen::VectorXd > obj_values) override
void computeInequalityValues(Eigen::Ref< Eigen::VectorXd > ineq_values) override
int getDimension() const override
Get dimension of the edge (dimension of the cost-function/constraint value vector) ...
bool isLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
bool isLinear() const override
Return true if the edge is linear (and hence its Hessian is always zero)
std::shared_ptr< MSDynamicsOnlyMultiControlsMultiDtsEdge > Ptr
void setIntegrator(NumericalIntegratorExplicitInterface::Ptr integrator)
std::shared_ptr< const StageCost > ConstPtr
std::shared_ptr< SystemDynamicsInterface > Ptr
StageEqualityConstraint::ConstPtr _stage_eq


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Mon Feb 28 2022 22:07:05