10 #include "crocoddyl/core/solvers/ddp.hpp"
14 SolverDDP::SolverDDP(std::shared_ptr<ShootingProblem> problem)
15 : SolverAbstract(problem),
26 const std::size_t n_alphas = 10;
27 alphas_.resize(n_alphas);
28 for (std::size_t n = 0; n < n_alphas; ++n) {
29 alphas_[n] = 1. / pow(2.,
static_cast<double>(n));
31 if (th_stepinc_ < alphas_[n_alphas - 1]) {
32 th_stepinc_ = alphas_[n_alphas - 1];
33 std::cerr <<
"Warning: th_stepinc has higher value than lowest alpha "
35 << std::to_string(alphas_[n_alphas - 1]) << std::endl;
39 SolverDDP::~SolverDDP() {}
41 bool SolverDDP::solve(
const std::vector<Eigen::VectorXd>& init_xs,
42 const std::vector<Eigen::VectorXd>& init_us,
43 const std::size_t maxiter,
const bool is_feasible,
44 const double init_reg) {
45 START_PROFILER(
"SolverDDP::solve");
46 if (problem_->is_updated()) {
51 setCandidate(init_xs, init_us, is_feasible);
53 if (std::isnan(init_reg)) {
60 was_feasible_ =
false;
62 bool recalcDiff =
true;
63 for (iter_ = 0; iter_ < maxiter; ++iter_) {
66 computeDirection(recalcDiff);
67 }
catch (std::exception& e) {
69 increaseRegularization();
70 if (preg_ == reg_max_) {
78 expectedImprovement();
82 for (std::vector<double>::const_iterator it = alphas_.begin();
83 it != alphas_.end(); ++it) {
87 dV_ = tryStep(steplength_);
88 }
catch (std::exception& e) {
91 dVexp_ = steplength_ * (d_[0] + 0.5 * steplength_ * d_[1]);
94 if (std::abs(d_[0]) < th_grad_ || !is_feasible_ ||
95 dV_ > th_acceptstep_ * dVexp_) {
96 was_feasible_ = is_feasible_;
97 setCandidate(xs_try_, us_try_,
true);
105 if (steplength_ > th_stepdec_) {
106 decreaseRegularization();
108 if (steplength_ <= th_stepinc_) {
109 increaseRegularization();
110 if (preg_ == reg_max_) {
111 STOP_PROFILER(
"SolverDDP::solve");
117 const std::size_t n_callbacks = callbacks_.size();
118 for (std::size_t c = 0; c < n_callbacks; ++c) {
119 CallbackAbstract& callback = *callbacks_[c];
123 if (was_feasible_ && stop_ < th_stop_) {
124 STOP_PROFILER(
"SolverDDP::solve");
128 STOP_PROFILER(
"SolverDDP::solve");
132 void SolverDDP::computeDirection(
const bool recalcDiff) {
133 START_PROFILER(
"SolverDDP::computeDirection");
138 STOP_PROFILER(
"SolverDDP::computeDirection");
141 double SolverDDP::tryStep(
const double steplength) {
142 START_PROFILER(
"SolverDDP::tryStep");
143 forwardPass(steplength);
144 STOP_PROFILER(
"SolverDDP::tryStep");
145 return cost_ - cost_try_;
148 double SolverDDP::stoppingCriteria() {
153 stop_ = std::abs(d_[0] + 0.5 * d_[1]);
157 const Eigen::Vector2d& SolverDDP::expectedImprovement() {
159 const std::size_t T = this->problem_->get_T();
160 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
161 problem_->get_runningModels();
162 for (std::size_t t = 0; t < T; ++t) {
163 const std::size_t nu = models[t]->get_nu();
165 d_[0] += Qu_[t].dot(k_[t]);
166 d_[1] -= k_[t].dot(Quuk_[t]);
172 void SolverDDP::resizeData() {
173 START_PROFILER(
"SolverDDP::resizeData");
174 SolverAbstract::resizeData();
176 const std::size_t T = problem_->get_T();
177 const std::size_t ndx = problem_->get_ndx();
178 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
179 problem_->get_runningModels();
180 for (std::size_t t = 0; t < T; ++t) {
181 const std::shared_ptr<ActionModelAbstract>& model = models[t];
182 const std::size_t nu = model->get_nu();
183 Qxu_[t].conservativeResize(ndx, nu);
184 Quu_[t].conservativeResize(nu, nu);
185 Qu_[t].conservativeResize(nu);
186 K_[t].conservativeResize(nu, ndx);
187 k_[t].conservativeResize(nu);
188 us_try_[t].conservativeResize(nu);
189 FuTVxx_p_[t].conservativeResize(nu, ndx);
190 Quuk_[t].conservativeResize(nu);
192 FuTVxx_p_[t].setZero();
195 STOP_PROFILER(
"SolverDDP::resizeData");
198 double SolverDDP::calcDiff() {
199 START_PROFILER(
"SolverDDP::calcDiff");
201 problem_->calc(xs_, us_);
203 cost_ = problem_->calcDiff(xs_, us_);
205 ffeas_ = computeDynamicFeasibility();
206 gfeas_ = computeInequalityFeasibility();
207 hfeas_ = computeEqualityFeasibility();
208 STOP_PROFILER(
"SolverDDP::calcDiff");
212 void SolverDDP::backwardPass() {
213 START_PROFILER(
"SolverDDP::backwardPass");
214 const std::shared_ptr<ActionDataAbstract>& d_T = problem_->get_terminalData();
215 Vxx_.back() = d_T->Lxx;
216 Vx_.back() = d_T->Lx;
218 if (!std::isnan(preg_)) {
219 Vxx_.back().diagonal().array() += preg_;
223 Vx_.back().noalias() += Vxx_.back() * fs_.back();
225 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
226 problem_->get_runningModels();
227 const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
228 problem_->get_runningDatas();
229 for (
int t =
static_cast<int>(problem_->get_T()) - 1; t >= 0; --t) {
230 const std::shared_ptr<ActionModelAbstract>& m = models[t];
231 const std::shared_ptr<ActionDataAbstract>& d = datas[t];
235 computeActionValueFunction(t, m, d);
241 computeValueFunction(t, m);
243 if (raiseIfNaN(Vx_[t].lpNorm<Eigen::Infinity>())) {
244 throw_pretty(
"backward_error");
246 if (raiseIfNaN(Vxx_[t].lpNorm<Eigen::Infinity>())) {
247 throw_pretty(
"backward_error");
250 STOP_PROFILER(
"SolverDDP::backwardPass");
253 void SolverDDP::forwardPass(
const double steplength) {
254 if (steplength > 1. || steplength < 0.) {
255 throw_pretty(
"Invalid argument: "
256 <<
"invalid step length, value is between 0. to 1.");
258 START_PROFILER(
"SolverDDP::forwardPass");
260 const std::size_t T = problem_->get_T();
261 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
262 problem_->get_runningModels();
263 const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
264 problem_->get_runningDatas();
265 for (std::size_t t = 0; t < T; ++t) {
266 const std::shared_ptr<ActionModelAbstract>& m = models[t];
267 const std::shared_ptr<ActionDataAbstract>& d = datas[t];
269 m->get_state()->diff(xs_[t], xs_try_[t], dx_[t]);
270 if (m->get_nu() != 0) {
271 us_try_[t].noalias() = us_[t];
272 us_try_[t].noalias() -= k_[t] * steplength;
273 us_try_[t].noalias() -= K_[t] * dx_[t];
274 m->calc(d, xs_try_[t], us_try_[t]);
276 m->calc(d, xs_try_[t]);
278 xs_try_[t + 1] = d->xnext;
279 cost_try_ += d->cost;
281 if (raiseIfNaN(cost_try_)) {
282 STOP_PROFILER(
"SolverDDP::forwardPass");
283 throw_pretty(
"forward_error");
285 if (raiseIfNaN(xs_try_[t + 1].lpNorm<Eigen::Infinity>())) {
286 STOP_PROFILER(
"SolverDDP::forwardPass");
287 throw_pretty(
"forward_error");
291 const std::shared_ptr<ActionModelAbstract>& m = problem_->get_terminalModel();
292 const std::shared_ptr<ActionDataAbstract>& d = problem_->get_terminalData();
293 m->calc(d, xs_try_.back());
294 cost_try_ += d->cost;
296 if (raiseIfNaN(cost_try_)) {
297 STOP_PROFILER(
"SolverDDP::forwardPass");
298 throw_pretty(
"forward_error");
300 STOP_PROFILER(
"SolverDDP::forwardPass");
303 void SolverDDP::computeActionValueFunction(
304 const std::size_t t,
const std::shared_ptr<ActionModelAbstract>& model,
305 const std::shared_ptr<ActionDataAbstract>& data) {
306 assert_pretty(t < problem_->get_T(),
307 "Invalid argument: t should be between 0 and " +
308 std::to_string(problem_->get_T()););
309 const std::size_t nu = model->get_nu();
310 const Eigen::MatrixXd& Vxx_p = Vxx_[t + 1];
311 const Eigen::VectorXd& Vx_p = Vx_[t + 1];
313 FxTVxx_p_.noalias() = data->Fx.transpose() * Vxx_p;
314 START_PROFILER(
"SolverDDP::Qx");
316 Qx_[t].noalias() += data->Fx.transpose() * Vx_p;
317 STOP_PROFILER(
"SolverDDP::Qx");
318 START_PROFILER(
"SolverDDP::Qxx");
320 Qxx_[t].noalias() += FxTVxx_p_ * data->Fx;
321 STOP_PROFILER(
"SolverDDP::Qxx");
323 FuTVxx_p_[t].noalias() = data->Fu.transpose() * Vxx_p;
324 START_PROFILER(
"SolverDDP::Qu");
326 Qu_[t].noalias() += data->Fu.transpose() * Vx_p;
327 STOP_PROFILER(
"SolverDDP::Qu");
328 START_PROFILER(
"SolverDDP::Quu");
330 Quu_[t].noalias() += FuTVxx_p_[t] * data->Fu;
331 STOP_PROFILER(
"SolverDDP::Quu");
332 START_PROFILER(
"SolverDDP::Qxu");
334 Qxu_[t].noalias() += FxTVxx_p_ * data->Fu;
335 STOP_PROFILER(
"SolverDDP::Qxu");
336 if (!std::isnan(preg_)) {
337 Quu_[t].diagonal().array() += preg_;
342 void SolverDDP::computeValueFunction(
343 const std::size_t t,
const std::shared_ptr<ActionModelAbstract>& model) {
344 assert_pretty(t < problem_->get_T(),
345 "Invalid argument: t should be between 0 and " +
346 std::to_string(problem_->get_T()););
347 const std::size_t nu = model->get_nu();
351 START_PROFILER(
"SolverDDP::Vx");
352 Quuk_[t].noalias() = Quu_[t] * k_[t];
353 Vx_[t].noalias() -= K_[t].transpose() * Qu_[t];
354 STOP_PROFILER(
"SolverDDP::Vx");
355 START_PROFILER(
"SolverDDP::Vxx");
356 Vxx_[t].noalias() -= Qxu_[t] * K_[t];
357 STOP_PROFILER(
"SolverDDP::Vxx");
359 Vxx_tmp_ = 0.5 * (Vxx_[t] + Vxx_[t].transpose());
362 if (!std::isnan(preg_)) {
363 Vxx_[t].diagonal().array() += preg_;
368 Vx_[t].noalias() += Vxx_[t] * fs_[t];
372 void SolverDDP::computeGains(
const std::size_t t) {
373 assert_pretty(t < problem_->get_T(),
374 "Invalid argument: t should be between 0 and " +
375 std::to_string(problem_->get_T()));
376 START_PROFILER(
"SolverDDP::computeGains");
377 const std::size_t nu = problem_->get_runningModels()[t]->get_nu();
379 START_PROFILER(
"SolverDDP::Quu_inv");
380 Quu_llt_[t].compute(Quu_[t]);
381 STOP_PROFILER(
"SolverDDP::Quu_inv");
382 const Eigen::ComputationInfo& info = Quu_llt_[t].info();
383 if (info != Eigen::Success) {
384 STOP_PROFILER(
"SolverDDP::computeGains");
385 throw_pretty(
"backward_error");
387 K_[t] = Qxu_[t].transpose();
389 START_PROFILER(
"SolverDDP::Quu_inv_Qux");
390 Quu_llt_[t].solveInPlace(K_[t]);
391 STOP_PROFILER(
"SolverDDP::Quu_inv_Qux");
393 Quu_llt_[t].solveInPlace(k_[t]);
395 STOP_PROFILER(
"SolverDDP::computeGains");
398 void SolverDDP::increaseRegularization() {
399 preg_ *= reg_incfactor_;
400 if (preg_ > reg_max_) {
406 void SolverDDP::decreaseRegularization() {
407 preg_ /= reg_decfactor_;
408 if (preg_ < reg_min_) {
414 void SolverDDP::allocateData() {
415 const std::size_t T = problem_->get_T();
426 xs_try_.resize(T + 1);
434 const std::size_t ndx = problem_->get_ndx();
435 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
436 problem_->get_runningModels();
437 for (std::size_t t = 0; t < T; ++t) {
438 const std::shared_ptr<ActionModelAbstract>& model = models[t];
439 const std::size_t nu = model->get_nu();
440 Vxx_[t] = Eigen::MatrixXd::Zero(ndx, ndx);
441 Vx_[t] = Eigen::VectorXd::Zero(ndx);
442 Qxx_[t] = Eigen::MatrixXd::Zero(ndx, ndx);
443 Qxu_[t] = Eigen::MatrixXd::Zero(ndx, nu);
444 Quu_[t] = Eigen::MatrixXd::Zero(nu, nu);
445 Qx_[t] = Eigen::VectorXd::Zero(ndx);
446 Qu_[t] = Eigen::VectorXd::Zero(nu);
447 K_[t] = MatrixXdRowMajor::Zero(nu, ndx);
448 k_[t] = Eigen::VectorXd::Zero(nu);
451 xs_try_[t] = problem_->get_x0();
453 xs_try_[t] = model->get_state()->zero();
455 us_try_[t] = Eigen::VectorXd::Zero(nu);
456 dx_[t] = Eigen::VectorXd::Zero(ndx);
458 FuTVxx_p_[t] = MatrixXdRowMajor::Zero(nu, ndx);
459 Quu_llt_[t] = Eigen::LLT<Eigen::MatrixXd>(nu);
460 Quuk_[t] = Eigen::VectorXd(nu);
462 Vxx_.back() = Eigen::MatrixXd::Zero(ndx, ndx);
463 Vxx_tmp_ = Eigen::MatrixXd::Zero(ndx, ndx);
464 Vx_.back() = Eigen::VectorXd::Zero(ndx);
465 xs_try_.back() = problem_->get_terminalModel()->get_state()->zero();
467 FxTVxx_p_ = MatrixXdRowMajor::Zero(ndx, ndx);
468 fTVxx_p_ = Eigen::VectorXd::Zero(ndx);
471 double SolverDDP::get_reg_incfactor()
const {
return reg_incfactor_; }
473 double SolverDDP::get_reg_decfactor()
const {
return reg_decfactor_; }
475 double SolverDDP::get_regfactor()
const {
return reg_incfactor_; }
477 double SolverDDP::get_reg_min()
const {
return reg_min_; }
479 double SolverDDP::get_regmin()
const {
return reg_min_; }
481 double SolverDDP::get_reg_max()
const {
return reg_max_; }
483 double SolverDDP::get_regmax()
const {
return reg_max_; }
485 const std::vector<double>& SolverDDP::get_alphas()
const {
return alphas_; }
487 double SolverDDP::get_th_stepdec()
const {
return th_stepdec_; }
489 double SolverDDP::get_th_stepinc()
const {
return th_stepinc_; }
491 double SolverDDP::get_th_grad()
const {
return th_grad_; }
493 const std::vector<Eigen::MatrixXd>& SolverDDP::get_Vxx()
const {
return Vxx_; }
495 const std::vector<Eigen::VectorXd>& SolverDDP::get_Vx()
const {
return Vx_; }
497 const std::vector<Eigen::MatrixXd>& SolverDDP::get_Qxx()
const {
return Qxx_; }
499 const std::vector<Eigen::MatrixXd>& SolverDDP::get_Qxu()
const {
return Qxu_; }
501 const std::vector<Eigen::MatrixXd>& SolverDDP::get_Quu()
const {
return Quu_; }
503 const std::vector<Eigen::VectorXd>& SolverDDP::get_Qx()
const {
return Qx_; }
505 const std::vector<Eigen::VectorXd>& SolverDDP::get_Qu()
const {
return Qu_; }
507 const std::vector<typename MathBaseTpl<double>::MatrixXsRowMajor>&
508 SolverDDP::get_K()
const {
512 const std::vector<Eigen::VectorXd>& SolverDDP::get_k()
const {
return k_; }
514 void SolverDDP::set_reg_incfactor(
const double regfactor) {
515 if (regfactor <= 1.) {
517 "Invalid argument: " <<
"reg_incfactor value is higher than 1.");
519 reg_incfactor_ = regfactor;
522 void SolverDDP::set_reg_decfactor(
const double regfactor) {
523 if (regfactor <= 1.) {
525 "Invalid argument: " <<
"reg_decfactor value is higher than 1.");
527 reg_decfactor_ = regfactor;
530 void SolverDDP::set_regfactor(
const double regfactor) {
531 if (regfactor <= 1.) {
532 throw_pretty(
"Invalid argument: " <<
"regfactor value is higher than 1.");
534 set_reg_incfactor(regfactor);
535 set_reg_decfactor(regfactor);
538 void SolverDDP::set_reg_min(
const double regmin) {
540 throw_pretty(
"Invalid argument: " <<
"regmin value has to be positive.");
545 void SolverDDP::set_regmin(
const double regmin) {
547 throw_pretty(
"Invalid argument: " <<
"regmin value has to be positive.");
552 void SolverDDP::set_reg_max(
const double regmax) {
554 throw_pretty(
"Invalid argument: " <<
"regmax value has to be positive.");
559 void SolverDDP::set_regmax(
const double regmax) {
561 throw_pretty(
"Invalid argument: " <<
"regmax value has to be positive.");
566 void SolverDDP::set_alphas(
const std::vector<double>& alphas) {
567 double prev_alpha = alphas[0];
568 if (prev_alpha != 1.) {
569 std::cerr <<
"Warning: alpha[0] should be 1" << std::endl;
571 for (std::size_t i = 1; i < alphas.size(); ++i) {
572 double alpha = alphas[i];
574 throw_pretty(
"Invalid argument: " <<
"alpha values has to be positive.");
576 if (alpha >= prev_alpha) {
578 "Invalid argument: " <<
"alpha values are monotonously decreasing.");
585 void SolverDDP::set_th_stepdec(
const double th_stepdec) {
586 if (0. >= th_stepdec || th_stepdec > 1.) {
588 "Invalid argument: " <<
"th_stepdec value should between 0 and 1.");
590 th_stepdec_ = th_stepdec;
593 void SolverDDP::set_th_stepinc(
const double th_stepinc) {
594 if (0. >= th_stepinc || th_stepinc > 1.) {
596 "Invalid argument: " <<
"th_stepinc value should between 0 and 1.");
598 th_stepinc_ = th_stepinc;
601 void SolverDDP::set_th_grad(
const double th_grad) {
603 throw_pretty(
"Invalid argument: " <<
"th_grad value has to be positive.");