Crocoddyl
solver-base.cpp
1 // BSD 3-Clause License
3 //
4 // Copyright (C) 2019-2024, LAAS-CNRS, University of Edinburgh,
5 // Heriot-Watt University, University of Oxford
6 // Copyright note valid unless otherwise stated in individual files.
7 // All rights reserved.
9 
10 #ifdef CROCODDYL_WITH_MULTITHREADING
11 #include <omp.h>
12 #endif // CROCODDYL_WITH_MULTITHREADING
13 
14 #include "crocoddyl/core/solver-base.hpp"
15 
16 namespace crocoddyl {
17 
18 SolverAbstract::SolverAbstract(std::shared_ptr<ShootingProblem> problem)
19  : problem_(problem),
20  is_feasible_(false),
21  was_feasible_(false),
22  cost_(0.),
23  merit_(0.),
24  stop_(0.),
25  dV_(0.),
26  dPhi_(0.),
27  dVexp_(0.),
28  dPhiexp_(0.),
29  dfeas_(0.),
30  feas_(0.),
31  ffeas_(0.),
32  gfeas_(0.),
33  hfeas_(0.),
34  ffeas_try_(0.),
35  gfeas_try_(0.),
36  hfeas_try_(0.),
37  preg_(0.),
38  dreg_(0.),
39  steplength_(1.),
40  th_acceptstep_(0.1),
41  th_stop_(1e-9),
42  th_gaptol_(1e-16),
43  feasnorm_(LInf),
44  iter_(0),
45  tmp_feas_(0.) {
46  // Allocate common data
47  const std::size_t ndx = problem_->get_ndx();
48  const std::size_t T = problem_->get_T();
49  const std::size_t ng_T = problem_->get_terminalModel()->get_ng_T();
50  xs_.resize(T + 1);
51  us_.resize(T);
52  fs_.resize(T + 1);
53  g_adj_.resize(T + 1);
54  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
55  problem_->get_runningModels();
56  for (std::size_t t = 0; t < T; ++t) {
57  const std::shared_ptr<ActionModelAbstract>& model = models[t];
58  const std::size_t nu = model->get_nu();
59  const std::size_t ng = model->get_ng();
60  xs_[t] = model->get_state()->zero();
61  us_[t] = Eigen::VectorXd::Zero(nu);
62  fs_[t] = Eigen::VectorXd::Zero(ndx);
63  g_adj_[t] = Eigen::VectorXd::Zero(ng);
64  }
65  xs_.back() = problem_->get_terminalModel()->get_state()->zero();
66  fs_.back() = Eigen::VectorXd::Zero(ndx);
67  g_adj_.back() = Eigen::VectorXd::Zero(ng_T);
68 }
69 
70 SolverAbstract::~SolverAbstract() {}
71 
73  START_PROFILER("SolverAbstract::resizeData");
74  const std::size_t T = problem_->get_T();
75  const std::size_t ng_T = problem_->get_terminalModel()->get_ng_T();
76  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
77  problem_->get_runningModels();
78  for (std::size_t t = 0; t < T; ++t) {
79  const std::shared_ptr<ActionModelAbstract>& model = models[t];
80  const std::size_t nu = model->get_nu();
81  const std::size_t ng = model->get_ng();
82  us_[t].conservativeResize(nu);
83  g_adj_[t].conservativeResize(ng);
84  }
85 
86  g_adj_.back().conservativeResize(ng_T);
87 
88  STOP_PROFILER("SolverAbstract::resizeData");
89 }
90 
92  tmp_feas_ = 0.;
93  const std::size_t T = problem_->get_T();
94  const Eigen::VectorXd& x0 = problem_->get_x0();
95  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
96  problem_->get_runningModels();
97  const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
98  problem_->get_runningDatas();
99 
100  models[0]->get_state()->diff(xs_[0], x0, fs_[0]);
101 #ifdef CROCODDYL_WITH_MULTITHREADING
102 #pragma omp parallel for num_threads(problem_->get_nthreads())
103 #endif
104  for (std::size_t t = 0; t < T; ++t) {
105  const std::shared_ptr<ActionModelAbstract>& m = models[t];
106  const std::shared_ptr<ActionDataAbstract>& d = datas[t];
107  m->get_state()->diff(xs_[t + 1], d->xnext, fs_[t + 1]);
108  }
109  switch (feasnorm_) {
110  case LInf:
111  tmp_feas_ = std::max(tmp_feas_, fs_[0].lpNorm<Eigen::Infinity>());
112  for (std::size_t t = 0; t < T; ++t) {
113  tmp_feas_ = std::max(tmp_feas_, fs_[t + 1].lpNorm<Eigen::Infinity>());
114  }
115  break;
116  case L1:
117  tmp_feas_ = fs_[0].lpNorm<1>();
118  for (std::size_t t = 0; t < T; ++t) {
119  tmp_feas_ += fs_[t + 1].lpNorm<1>();
120  }
121  break;
122  }
123  return tmp_feas_;
124 }
125 
127  tmp_feas_ = 0.;
128  const std::size_t T = problem_->get_T();
129  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
130  problem_->get_runningModels();
131  const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
132  problem_->get_runningDatas();
133 
134  switch (feasnorm_) {
135  case LInf:
136  for (std::size_t t = 0; t < T; ++t) {
137  if (models[t]->get_ng() > 0) {
138  g_adj_[t] = datas[t]
139  ->g.cwiseMax(models[t]->get_g_lb())
140  .cwiseMin(models[t]->get_g_ub());
141  tmp_feas_ = std::max(
142  tmp_feas_, (datas[t]->g - g_adj_[t]).lpNorm<Eigen::Infinity>());
143  }
144  }
145  if (problem_->get_terminalModel()->get_ng_T() > 0) {
146  g_adj_.back() =
147  problem_->get_terminalData()
148  ->g.cwiseMax(problem_->get_terminalModel()->get_g_lb())
149  .cwiseMin(problem_->get_terminalModel()->get_g_ub());
150  tmp_feas_ += (problem_->get_terminalData()->g - g_adj_.back())
151  .lpNorm<Eigen::Infinity>();
152  }
153  break;
154  case L1:
155  for (std::size_t t = 0; t < T; ++t) {
156  if (models[t]->get_ng() > 0) {
157  g_adj_[t] = datas[t]
158  ->g.cwiseMax(models[t]->get_g_lb())
159  .cwiseMin(models[t]->get_g_ub());
160  tmp_feas_ =
161  std::max(tmp_feas_, (datas[t]->g - g_adj_[t]).lpNorm<1>());
162  }
163  }
164  if (problem_->get_terminalModel()->get_ng_T() > 0) {
165  g_adj_.back() =
166  problem_->get_terminalData()
167  ->g.cwiseMax(problem_->get_terminalModel()->get_g_lb())
168  .cwiseMin(problem_->get_terminalModel()->get_g_ub());
169  tmp_feas_ +=
170  (problem_->get_terminalData()->g - g_adj_.back()).lpNorm<1>();
171  }
172  break;
173  }
174  return tmp_feas_;
175 }
176 
178  tmp_feas_ = 0.;
179  const std::size_t T = problem_->get_T();
180  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
181  problem_->get_runningModels();
182  const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
183  problem_->get_runningDatas();
184  switch (feasnorm_) {
185  case LInf:
186  for (std::size_t t = 0; t < T; ++t) {
187  if (models[t]->get_nh() > 0) {
188  tmp_feas_ =
189  std::max(tmp_feas_, datas[t]->h.lpNorm<Eigen::Infinity>());
190  }
191  }
192  if (problem_->get_terminalModel()->get_nh_T() > 0) {
193  tmp_feas_ =
194  std::max(tmp_feas_,
195  problem_->get_terminalData()->h.lpNorm<Eigen::Infinity>());
196  }
197  break;
198  case L1:
199  for (std::size_t t = 0; t < T; ++t) {
200  if (models[t]->get_nh() > 0) {
201  tmp_feas_ += datas[t]->h.lpNorm<1>();
202  }
203  }
204  if (problem_->get_terminalModel()->get_nh_T() > 0) {
205  tmp_feas_ += problem_->get_terminalData()->h.lpNorm<1>();
206  }
207  break;
208  }
209  return tmp_feas_;
210 }
211 
212 void SolverAbstract::setCandidate(const std::vector<Eigen::VectorXd>& xs_warm,
213  const std::vector<Eigen::VectorXd>& us_warm,
214  bool is_feasible) {
215  const std::size_t T = problem_->get_T();
216 
217  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
218  problem_->get_runningModels();
219  if (xs_warm.size() == 0) {
220  for (std::size_t t = 0; t < T; ++t) {
221  const std::shared_ptr<ActionModelAbstract>& model = models[t];
222  xs_[t] = model->get_state()->zero();
223  }
224  xs_.back() = problem_->get_terminalModel()->get_state()->zero();
225  } else {
226  if (xs_warm.size() != T + 1) {
227  throw_pretty("Warm start state vector has wrong dimension, got "
228  << xs_warm.size() << " expecting " << (T + 1));
229  }
230  for (std::size_t t = 0; t < T; ++t) {
231  const std::size_t nx = models[t]->get_state()->get_nx();
232  if (static_cast<std::size_t>(xs_warm[t].size()) != nx) {
233  throw_pretty("Invalid argument: "
234  << "xs_init[" + std::to_string(t) +
235  "] has wrong dimension ("
236  << xs_warm[t].size()
237  << " provided - it should be equal to " +
238  std::to_string(nx) + "). ActionModel: "
239  << *models[t]);
240  }
241  }
242  const std::size_t nx = problem_->get_terminalModel()->get_state()->get_nx();
243  if (static_cast<std::size_t>(xs_warm[T].size()) != nx) {
244  throw_pretty("Invalid argument: "
245  << "xs_init[" + std::to_string(T) +
246  "] (terminal state) has wrong dimension ("
247  << xs_warm[T].size()
248  << " provided - it should be equal to " +
249  std::to_string(nx) + "). ActionModel: "
250  << *problem_->get_terminalModel());
251  }
252  std::copy(xs_warm.begin(), xs_warm.end(), xs_.begin());
253  }
254 
255  if (us_warm.size() == 0) {
256  for (std::size_t t = 0; t < T; ++t) {
257  const std::shared_ptr<ActionModelAbstract>& model = models[t];
258  const std::size_t nu = model->get_nu();
259  us_[t] = Eigen::VectorXd::Zero(nu);
260  }
261  } else {
262  if (us_warm.size() != T) {
263  throw_pretty("Warm start control has wrong dimension, got "
264  << us_warm.size() << " expecting " << T);
265  }
266  for (std::size_t t = 0; t < T; ++t) {
267  const std::shared_ptr<ActionModelAbstract>& model = models[t];
268  const std::size_t nu = model->get_nu();
269  if (static_cast<std::size_t>(us_warm[t].size()) != nu) {
270  throw_pretty("Invalid argument: "
271  << "us_init[" + std::to_string(t) +
272  "] has wrong dimension ("
273  << us_warm[t].size()
274  << " provided - it should be equal to " +
275  std::to_string(nu) + "). ActionModel: "
276  << *model);
277  }
278  }
279  std::copy(us_warm.begin(), us_warm.end(), us_.begin());
280  }
281  is_feasible_ = is_feasible;
282 }
283 
285  const std::vector<std::shared_ptr<CallbackAbstract> >& callbacks) {
286  callbacks_ = callbacks;
287 }
288 
289 const std::vector<std::shared_ptr<CallbackAbstract> >&
291  return callbacks_;
292 }
293 
294 const std::shared_ptr<ShootingProblem>& SolverAbstract::get_problem() const {
295  return problem_;
296 }
297 
298 const std::vector<Eigen::VectorXd>& SolverAbstract::get_xs() const {
299  return xs_;
300 }
301 
302 const std::vector<Eigen::VectorXd>& SolverAbstract::get_us() const {
303  return us_;
304 }
305 
306 const std::vector<Eigen::VectorXd>& SolverAbstract::get_fs() const {
307  return fs_;
308 }
309 
311 
312 double SolverAbstract::get_cost() const { return cost_; }
313 
314 double SolverAbstract::get_merit() const { return merit_; }
315 
316 double SolverAbstract::get_stop() const { return stop_; }
317 
318 const Eigen::Vector2d& SolverAbstract::get_d() const { return d_; }
319 
320 double SolverAbstract::get_dV() const { return dV_; }
321 
322 double SolverAbstract::get_dPhi() const { return dPhi_; }
323 
324 double SolverAbstract::get_dVexp() const { return dVexp_; }
325 
326 double SolverAbstract::get_dPhiexp() const { return dPhiexp_; }
327 
328 double SolverAbstract::get_dfeas() const { return dfeas_; }
329 
330 double SolverAbstract::get_feas() const { return feas_; }
331 
332 double SolverAbstract::get_ffeas() const { return ffeas_; }
333 
334 double SolverAbstract::get_gfeas() const { return gfeas_; }
335 
336 double SolverAbstract::get_hfeas() const { return hfeas_; }
337 
338 double SolverAbstract::get_ffeas_try() const { return ffeas_try_; }
339 
340 double SolverAbstract::get_gfeas_try() const { return gfeas_try_; }
341 
342 double SolverAbstract::get_hfeas_try() const { return hfeas_try_; }
343 
344 double SolverAbstract::get_preg() const { return preg_; }
345 
346 double SolverAbstract::get_dreg() const { return dreg_; }
347 
348 DEPRECATED(
349  "Use get_preg for gettting the primal-dual regularization",
350  double SolverAbstract::get_xreg() const { return preg_; })
351 
352 DEPRECATED(
353  "Use get_preg for gettting the primal-dual regularization",
354  double SolverAbstract::get_ureg() const { return preg_; })
355 
356 double SolverAbstract::get_steplength() const { return steplength_; }
357 
359 
360 double SolverAbstract::get_th_stop() const { return th_stop_; }
361 
362 double SolverAbstract::get_th_gaptol() const { return th_gaptol_; }
363 
364 FeasibilityNorm SolverAbstract::get_feasnorm() const { return feasnorm_; }
365 
366 std::size_t SolverAbstract::get_iter() const { return iter_; }
367 
368 void SolverAbstract::set_xs(const std::vector<Eigen::VectorXd>& xs) {
369  const std::size_t T = problem_->get_T();
370  if (xs.size() != T + 1) {
371  throw_pretty("Invalid argument: " << "xs list has to be of length " +
372  std::to_string(T + 1));
373  }
374 
375  const std::size_t nx = problem_->get_nx();
376  for (std::size_t t = 0; t < T; ++t) {
377  if (static_cast<std::size_t>(xs[t].size()) != nx) {
378  throw_pretty("Invalid argument: "
379  << "xs[" + std::to_string(t) + "] has wrong dimension ("
380  << xs[t].size()
381  << " provided - it should be " + std::to_string(nx) + ")")
382  }
383  }
384  if (static_cast<std::size_t>(xs[T].size()) != nx) {
385  throw_pretty("Invalid argument: "
386  << "xs[" + std::to_string(T) +
387  "] (terminal state) has wrong dimension ("
388  << xs[T].size()
389  << " provided - it should be " + std::to_string(nx) + ")")
390  }
391  xs_ = xs;
392 }
393 
394 void SolverAbstract::set_us(const std::vector<Eigen::VectorXd>& us) {
395  const std::size_t T = problem_->get_T();
396  if (us.size() != T) {
397  throw_pretty("Invalid argument: " << "us list has to be of length " +
398  std::to_string(T));
399  }
400 
401  const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
402  problem_->get_runningModels();
403  for (std::size_t t = 0; t < T; ++t) {
404  const std::shared_ptr<ActionModelAbstract>& model = models[t];
405  const std::size_t nu = model->get_nu();
406  if (static_cast<std::size_t>(us[t].size()) != nu) {
407  throw_pretty("Invalid argument: "
408  << "us[" + std::to_string(t) + "] has wrong dimension ("
409  << us[t].size()
410  << " provided - it should be " + std::to_string(nu) + ")")
411  }
412  }
413  us_ = us;
414 }
415 
416 void SolverAbstract::set_preg(const double preg) {
417  if (preg < 0.) {
418  throw_pretty("Invalid argument: " << "preg value has to be positive.");
419  }
420  preg_ = preg;
421 }
422 
423 void SolverAbstract::set_dreg(const double dreg) {
424  if (dreg < 0.) {
425  throw_pretty("Invalid argument: " << "dreg value has to be positive.");
426  }
427  dreg_ = dreg;
428 }
429 
430 DEPRECATED(
431  "Use set_preg for gettting the primal-variable regularization",
432  void SolverAbstract::set_xreg(const double xreg) {
433  if (xreg < 0.) {
434  throw_pretty("Invalid argument: " << "xreg value has to be positive.");
435  }
436  xreg_ = xreg;
437  preg_ = xreg;
438  })
439 
440 DEPRECATED(
441  "Use set_preg for gettting the primal-variable regularization",
442  void SolverAbstract::set_ureg(const double ureg) {
443  if (ureg < 0.) {
444  throw_pretty("Invalid argument: " << "ureg value has to be positive.");
445  }
446  ureg_ = ureg;
447  preg_ = ureg;
448  })
449 
450 void SolverAbstract::set_th_acceptstep(const double th_acceptstep) {
451  if (0. >= th_acceptstep || th_acceptstep > 1) {
452  throw_pretty(
453  "Invalid argument: " << "th_acceptstep value should between 0 and 1.");
454  }
455  th_acceptstep_ = th_acceptstep;
456 }
457 
458 void SolverAbstract::set_th_stop(const double th_stop) {
459  if (th_stop <= 0.) {
460  throw_pretty("Invalid argument: " << "th_stop value has to higher than 0.");
461  }
462  th_stop_ = th_stop;
463 }
464 
465 void SolverAbstract::set_th_gaptol(const double th_gaptol) {
466  if (0. > th_gaptol) {
467  throw_pretty("Invalid argument: " << "th_gaptol value has to be positive.");
468  }
469  th_gaptol_ = th_gaptol;
470 }
471 
472 void SolverAbstract::set_feasnorm(const FeasibilityNorm feasnorm) {
473  feasnorm_ = feasnorm;
474 }
475 
476 bool raiseIfNaN(const double value) {
477  if (std::isnan(value) || std::isinf(value) || value >= 1e30) {
478  return true;
479  } else {
480  return false;
481  }
482 }
483 
484 } // namespace crocoddyl
double get_cost() const
Return the cost for the current guess.
std::vector< Eigen::VectorXd > g_adj_
Adjusted inequality bound.
double get_dPhi() const
Return the reduction in the merit function .
double get_th_gaptol() const
Return the threshold for accepting a gap as non-zero.
double dVexp_
Expected reduction in the cost function.
std::vector< Eigen::VectorXd > xs_
State trajectory.
std::size_t get_iter() const
Return the number of iterations performed by the solver.
double get_hfeas() const
Return the equality feasibility for the current guess.
void set_th_stop(const double th_stop)
Modify the tolerance for stopping the algorithm.
double stop_
Value computed by stoppingCriteria()
void set_xs(const std::vector< Eigen::VectorXd > &xs)
Modify the state trajectory .
double get_dVexp() const
Return the expected reduction in the cost function .
double dreg_
Current dual-variable regularization value.
double feas_
Total feasibility for the current guess.
bool is_feasible_
Label that indicates is the iteration is feasible.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW SolverAbstract(std::shared_ptr< ShootingProblem > problem)
Initialize the solver.
Definition: solver-base.cpp:18
std::shared_ptr< ShootingProblem > problem_
optimal control problem
std::vector< Eigen::VectorXd > us_
Control trajectory.
double get_dPhiexp() const
Return the expected reduction in the merit function .
double th_acceptstep_
Threshold used for accepting step.
double get_steplength() const
Return the step length .
void set_th_gaptol(const double th_gaptol)
Modify the threshold for accepting a gap as non-zero.
double get_merit() const
Return the merit for the current guess.
double dPhi_
Reduction in the merit function computed by tryStep()
const std::vector< std::shared_ptr< CallbackAbstract > > & getCallbacks() const
Return the list of callback functions using for diagnostic.
double computeInequalityFeasibility()
Compute the feasibility of the inequality constraints for the current guess.
double get_preg() const
Return the primal-variable regularization.
double get_hfeas_try() const
Return the equality feasibility for the current step length.
double th_stop_
Tolerance for stopping the algorithm.
double computeDynamicFeasibility()
Compute the dynamic feasibility for the current guess .
Definition: solver-base.cpp:91
const Eigen::Vector2d & get_d() const
Return the linear and quadratic terms of the expected improvement.
double dPhiexp_
Expected reduction in the merit function.
enum FeasibilityNorm feasnorm_
void setCandidate(const std::vector< Eigen::VectorXd > &xs_warm=DEFAULT_VECTOR, const std::vector< Eigen::VectorXd > &us_warm=DEFAULT_VECTOR, const bool is_feasible=false)
Set the solver candidate trajectories .
double get_th_stop() const
Return the tolerance for stopping the algorithm.
double dfeas_
Reduction in the feasibility.
void set_feasnorm(const FeasibilityNorm feas_norm)
Modify the current norm used for computed the dynamic and constraint feasibility.
double get_ffeas() const
Return the dynamic feasibility for the current guess.
double get_gfeas() const
Return the inequality feasibility for the current guess.
double cost_
Cost for the current guess.
std::vector< std::shared_ptr< CallbackAbstract > > callbacks_
Callback functions.
void set_us(const std::vector< Eigen::VectorXd > &us)
Modify the control trajectory .
void setCallbacks(const std::vector< std::shared_ptr< CallbackAbstract > > &callbacks)
Set a list of callback functions using for the solver diagnostic.
double th_gaptol_
Threshold limit to check non-zero gaps.
std::size_t iter_
Number of iteration performed by the solver.
double get_feas() const
Return the total feasibility for the current guess.
double dV_
Reduction in the cost function computed by tryStep()
double get_stop() const
Return the stopping-criteria value computed by stoppingCriteria()
double get_ffeas_try() const
Return the dynamic feasibility for the current step length.
void set_dreg(const double dreg)
Modify the dual-variable regularization value.
Eigen::Vector2d d_
LQ approximation of the expected improvement.
double get_dV() const
Return the reduction in the cost function .
const std::vector< Eigen::VectorXd > & get_fs() const
Return the dynamic infeasibility .
const std::vector< Eigen::VectorXd > & get_xs() const
Return the state trajectory .
double get_dfeas() const
Return the reduction in the feasibility.
void set_preg(const double preg)
Modify the primal-variable regularization value.
double ffeas_
Feasibility of the dynamic constraints for the current guess.
double get_gfeas_try() const
Return the inequality feasibility for the current step length.
bool get_is_feasible() const
Return the feasibility status of the trajectory.
double preg_
Current primal-variable regularization value.
const std::vector< Eigen::VectorXd > & get_us() const
Return the control trajectory .
double merit_
Merit for the current guess.
virtual void resizeData()
Resizing the solver data.
Definition: solver-base.cpp:72
std::vector< Eigen::VectorXd > fs_
Gaps/defects between shooting nodes.
void set_th_acceptstep(const double th_acceptstep)
Modify the threshold used for accepting step.
const std::shared_ptr< ShootingProblem > & get_problem() const
Return the shooting problem.
double tmp_feas_
Temporal variables used for computed the feasibility.
double get_th_acceptstep() const
Return the threshold used for accepting a step.
FeasibilityNorm get_feasnorm() const
Return the type of norm used to evaluate the dynamic and constraints feasibility.
double get_dreg() const
Return the dual-variable regularization.
double computeEqualityFeasibility()
Compute the feasibility of the equality constraints for the current guess.