10#include "crocoddyl/core/solvers/ddp.hpp"
14#include "crocoddyl/core/utils/exception.hpp"
18SolverDDP::SolverDDP(std::shared_ptr<ShootingProblem> problem)
19 : SolverAbstract(problem),
30 const std::size_t n_alphas = 10;
31 alphas_.resize(n_alphas);
32 for (std::size_t n = 0; n < n_alphas; ++n) {
33 alphas_[n] = 1. / pow(2.,
static_cast<double>(n));
35 if (th_stepinc_ < alphas_[n_alphas - 1]) {
36 th_stepinc_ = alphas_[n_alphas - 1];
37 std::cerr <<
"Warning: th_stepinc has higher value than lowest alpha "
39 << std::to_string(alphas_[n_alphas - 1]) << std::endl;
43SolverDDP::~SolverDDP() {}
45bool SolverDDP::solve(
const std::vector<Eigen::VectorXd>& init_xs,
46 const std::vector<Eigen::VectorXd>& init_us,
47 const std::size_t maxiter,
const bool is_feasible,
48 const double init_reg) {
49 START_PROFILER(
"SolverDDP::solve");
50 if (problem_->is_updated()) {
55 setCandidate(init_xs, init_us, is_feasible);
57 if (std::isnan(init_reg)) {
64 was_feasible_ =
false;
66 bool recalcDiff =
true;
67 for (iter_ = 0; iter_ < maxiter; ++iter_) {
70 computeDirection(recalcDiff);
71 }
catch (std::exception& e) {
73 increaseRegularization();
74 if (preg_ == reg_max_) {
82 expectedImprovement();
86 for (std::vector<double>::const_iterator it = alphas_.begin();
87 it != alphas_.end(); ++it) {
91 dV_ = tryStep(steplength_);
92 }
catch (std::exception& e) {
95 dVexp_ = steplength_ * (d_[0] + 0.5 * steplength_ * d_[1]);
98 if (std::abs(d_[0]) < th_grad_ || !is_feasible_ ||
99 dV_ > th_acceptstep_ * dVexp_) {
100 was_feasible_ = is_feasible_;
101 setCandidate(xs_try_, us_try_,
true);
109 if (steplength_ > th_stepdec_) {
110 decreaseRegularization();
112 if (steplength_ <= th_stepinc_) {
113 increaseRegularization();
114 if (preg_ == reg_max_) {
115 STOP_PROFILER(
"SolverDDP::solve");
121 const std::size_t n_callbacks = callbacks_.size();
122 for (std::size_t c = 0; c < n_callbacks; ++c) {
123 CallbackAbstract& callback = *callbacks_[c];
127 if (was_feasible_ && stop_ < th_stop_) {
128 STOP_PROFILER(
"SolverDDP::solve");
132 STOP_PROFILER(
"SolverDDP::solve");
136void SolverDDP::computeDirection(
const bool recalcDiff) {
137 START_PROFILER(
"SolverDDP::computeDirection");
142 STOP_PROFILER(
"SolverDDP::computeDirection");
145double SolverDDP::tryStep(
const double steplength) {
146 START_PROFILER(
"SolverDDP::tryStep");
147 forwardPass(steplength);
148 STOP_PROFILER(
"SolverDDP::tryStep");
149 return cost_ - cost_try_;
152double SolverDDP::stoppingCriteria() {
157 stop_ = std::abs(d_[0] + 0.5 * d_[1]);
161const Eigen::Vector2d& SolverDDP::expectedImprovement() {
163 const std::size_t T = this->problem_->get_T();
164 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
165 problem_->get_runningModels();
166 for (std::size_t t = 0; t < T; ++t) {
167 const std::size_t nu = models[t]->get_nu();
169 d_[0] += Qu_[t].dot(k_[t]);
170 d_[1] -= k_[t].dot(Quuk_[t]);
176void SolverDDP::resizeData() {
177 START_PROFILER(
"SolverDDP::resizeData");
178 SolverAbstract::resizeData();
180 const std::size_t T = problem_->get_T();
181 const std::size_t ndx = problem_->get_ndx();
182 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
183 problem_->get_runningModels();
184 for (std::size_t t = 0; t < T; ++t) {
185 const std::shared_ptr<ActionModelAbstract>& model = models[t];
186 const std::size_t nu = model->get_nu();
187 Qxu_[t].conservativeResize(ndx, nu);
188 Quu_[t].conservativeResize(nu, nu);
189 Qu_[t].conservativeResize(nu);
190 K_[t].conservativeResize(nu, ndx);
191 k_[t].conservativeResize(nu);
192 us_try_[t].conservativeResize(nu);
193 FuTVxx_p_[t].conservativeResize(nu, ndx);
194 Quuk_[t].conservativeResize(nu);
196 FuTVxx_p_[t].setZero();
199 STOP_PROFILER(
"SolverDDP::resizeData");
202double SolverDDP::calcDiff() {
203 START_PROFILER(
"SolverDDP::calcDiff");
205 problem_->calc(xs_, us_);
207 cost_ = problem_->calcDiff(xs_, us_);
209 ffeas_ = computeDynamicFeasibility();
210 gfeas_ = computeInequalityFeasibility();
211 hfeas_ = computeEqualityFeasibility();
212 STOP_PROFILER(
"SolverDDP::calcDiff");
216void SolverDDP::backwardPass() {
217 START_PROFILER(
"SolverDDP::backwardPass");
218 const std::shared_ptr<ActionDataAbstract>& d_T = problem_->get_terminalData();
219 Vxx_.back() = d_T->Lxx;
220 Vx_.back() = d_T->Lx;
222 if (!std::isnan(preg_)) {
223 Vxx_.back().diagonal().array() += preg_;
227 Vx_.back().noalias() += Vxx_.back() * fs_.back();
229 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
230 problem_->get_runningModels();
231 const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
232 problem_->get_runningDatas();
233 for (
int t =
static_cast<int>(problem_->get_T()) - 1; t >= 0; --t) {
234 const std::shared_ptr<ActionModelAbstract>& m = models[t];
235 const std::shared_ptr<ActionDataAbstract>& d = datas[t];
239 computeActionValueFunction(t, m, d);
245 computeValueFunction(t, m);
247 if (raiseIfNaN(Vx_[t].lpNorm<Eigen::Infinity>())) {
248 throw_pretty(
"backward_error");
250 if (raiseIfNaN(Vxx_[t].lpNorm<Eigen::Infinity>())) {
251 throw_pretty(
"backward_error");
254 STOP_PROFILER(
"SolverDDP::backwardPass");
257void SolverDDP::forwardPass(
const double steplength) {
258 if (steplength > 1. || steplength < 0.) {
259 throw_pretty(
"Invalid argument: "
260 <<
"invalid step length, value is between 0. to 1.");
262 START_PROFILER(
"SolverDDP::forwardPass");
264 const std::size_t T = problem_->get_T();
265 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
266 problem_->get_runningModels();
267 const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
268 problem_->get_runningDatas();
269 for (std::size_t t = 0; t < T; ++t) {
270 const std::shared_ptr<ActionModelAbstract>& m = models[t];
271 const std::shared_ptr<ActionDataAbstract>& d = datas[t];
273 m->get_state()->diff(xs_[t], xs_try_[t], dx_[t]);
274 if (m->get_nu() != 0) {
275 us_try_[t].noalias() = us_[t];
276 us_try_[t].noalias() -= k_[t] * steplength;
277 us_try_[t].noalias() -= K_[t] * dx_[t];
278 m->calc(d, xs_try_[t], us_try_[t]);
280 m->calc(d, xs_try_[t]);
282 xs_try_[t + 1] = d->xnext;
283 cost_try_ += d->cost;
285 if (raiseIfNaN(cost_try_)) {
286 STOP_PROFILER(
"SolverDDP::forwardPass");
287 throw_pretty(
"forward_error");
289 if (raiseIfNaN(xs_try_[t + 1].lpNorm<Eigen::Infinity>())) {
290 STOP_PROFILER(
"SolverDDP::forwardPass");
291 throw_pretty(
"forward_error");
295 const std::shared_ptr<ActionModelAbstract>& m = problem_->get_terminalModel();
296 const std::shared_ptr<ActionDataAbstract>& d = problem_->get_terminalData();
297 m->calc(d, xs_try_.back());
298 cost_try_ += d->cost;
300 if (raiseIfNaN(cost_try_)) {
301 STOP_PROFILER(
"SolverDDP::forwardPass");
302 throw_pretty(
"forward_error");
304 STOP_PROFILER(
"SolverDDP::forwardPass");
307void SolverDDP::computeActionValueFunction(
308 const std::size_t t,
const std::shared_ptr<ActionModelAbstract>& model,
309 const std::shared_ptr<ActionDataAbstract>& data) {
310 assert_pretty(t < problem_->get_T(),
311 "Invalid argument: t should be between 0 and " +
312 std::to_string(problem_->get_T()););
313 const std::size_t nu = model->get_nu();
314 const Eigen::MatrixXd& Vxx_p = Vxx_[t + 1];
315 const Eigen::VectorXd& Vx_p = Vx_[t + 1];
317 FxTVxx_p_.noalias() = data->Fx.transpose() * Vxx_p;
318 START_PROFILER(
"SolverDDP::Qx");
320 Qx_[t].noalias() += data->Fx.transpose() * Vx_p;
321 STOP_PROFILER(
"SolverDDP::Qx");
322 START_PROFILER(
"SolverDDP::Qxx");
324 Qxx_[t].noalias() += FxTVxx_p_ * data->Fx;
325 STOP_PROFILER(
"SolverDDP::Qxx");
327 FuTVxx_p_[t].noalias() = data->Fu.transpose() * Vxx_p;
328 START_PROFILER(
"SolverDDP::Qu");
330 Qu_[t].noalias() += data->Fu.transpose() * Vx_p;
331 STOP_PROFILER(
"SolverDDP::Qu");
332 START_PROFILER(
"SolverDDP::Quu");
334 Quu_[t].noalias() += FuTVxx_p_[t] * data->Fu;
335 STOP_PROFILER(
"SolverDDP::Quu");
336 START_PROFILER(
"SolverDDP::Qxu");
338 Qxu_[t].noalias() += FxTVxx_p_ * data->Fu;
339 STOP_PROFILER(
"SolverDDP::Qxu");
340 if (!std::isnan(preg_)) {
341 Quu_[t].diagonal().array() += preg_;
346void SolverDDP::computeValueFunction(
347 const std::size_t t,
const std::shared_ptr<ActionModelAbstract>& model) {
348 assert_pretty(t < problem_->get_T(),
349 "Invalid argument: t should be between 0 and " +
350 std::to_string(problem_->get_T()););
351 const std::size_t nu = model->get_nu();
355 START_PROFILER(
"SolverDDP::Vx");
356 Quuk_[t].noalias() = Quu_[t] * k_[t];
357 Vx_[t].noalias() -= K_[t].transpose() * Qu_[t];
358 STOP_PROFILER(
"SolverDDP::Vx");
359 START_PROFILER(
"SolverDDP::Vxx");
360 Vxx_[t].noalias() -= Qxu_[t] * K_[t];
361 STOP_PROFILER(
"SolverDDP::Vxx");
363 Vxx_tmp_ = 0.5 * (Vxx_[t] + Vxx_[t].transpose());
366 if (!std::isnan(preg_)) {
367 Vxx_[t].diagonal().array() += preg_;
372 Vx_[t].noalias() += Vxx_[t] * fs_[t];
376void SolverDDP::computeGains(
const std::size_t t) {
377 assert_pretty(t < problem_->get_T(),
378 "Invalid argument: t should be between 0 and " +
379 std::to_string(problem_->get_T()));
380 START_PROFILER(
"SolverDDP::computeGains");
381 const std::size_t nu = problem_->get_runningModels()[t]->get_nu();
383 START_PROFILER(
"SolverDDP::Quu_inv");
384 Quu_llt_[t].compute(Quu_[t]);
385 STOP_PROFILER(
"SolverDDP::Quu_inv");
386 const Eigen::ComputationInfo& info = Quu_llt_[t].info();
387 if (info != Eigen::Success) {
388 STOP_PROFILER(
"SolverDDP::computeGains");
389 throw_pretty(
"backward_error");
391 K_[t] = Qxu_[t].transpose();
393 START_PROFILER(
"SolverDDP::Quu_inv_Qux");
394 Quu_llt_[t].solveInPlace(K_[t]);
395 STOP_PROFILER(
"SolverDDP::Quu_inv_Qux");
397 Quu_llt_[t].solveInPlace(k_[t]);
399 STOP_PROFILER(
"SolverDDP::computeGains");
402void SolverDDP::increaseRegularization() {
403 preg_ *= reg_incfactor_;
404 if (preg_ > reg_max_) {
410void SolverDDP::decreaseRegularization() {
411 preg_ /= reg_decfactor_;
412 if (preg_ < reg_min_) {
418void SolverDDP::allocateData() {
419 const std::size_t T = problem_->get_T();
430 xs_try_.resize(T + 1);
438 const std::size_t ndx = problem_->get_ndx();
439 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
440 problem_->get_runningModels();
441 for (std::size_t t = 0; t < T; ++t) {
442 const std::shared_ptr<ActionModelAbstract>& model = models[t];
443 const std::size_t nu = model->get_nu();
444 Vxx_[t] = Eigen::MatrixXd::Zero(ndx, ndx);
445 Vx_[t] = Eigen::VectorXd::Zero(ndx);
446 Qxx_[t] = Eigen::MatrixXd::Zero(ndx, ndx);
447 Qxu_[t] = Eigen::MatrixXd::Zero(ndx, nu);
448 Quu_[t] = Eigen::MatrixXd::Zero(nu, nu);
449 Qx_[t] = Eigen::VectorXd::Zero(ndx);
450 Qu_[t] = Eigen::VectorXd::Zero(nu);
451 K_[t] = MatrixXdRowMajor::Zero(nu, ndx);
452 k_[t] = Eigen::VectorXd::Zero(nu);
455 xs_try_[t] = problem_->get_x0();
457 xs_try_[t] = model->get_state()->zero();
459 us_try_[t] = Eigen::VectorXd::Zero(nu);
460 dx_[t] = Eigen::VectorXd::Zero(ndx);
462 FuTVxx_p_[t] = MatrixXdRowMajor::Zero(nu, ndx);
463 Quu_llt_[t] = Eigen::LLT<Eigen::MatrixXd>(nu);
464 Quuk_[t] = Eigen::VectorXd(nu);
466 Vxx_.back() = Eigen::MatrixXd::Zero(ndx, ndx);
467 Vxx_tmp_ = Eigen::MatrixXd::Zero(ndx, ndx);
468 Vx_.back() = Eigen::VectorXd::Zero(ndx);
469 xs_try_.back() = problem_->get_terminalModel()->get_state()->zero();
471 FxTVxx_p_ = MatrixXdRowMajor::Zero(ndx, ndx);
472 fTVxx_p_ = Eigen::VectorXd::Zero(ndx);
475double SolverDDP::get_reg_incfactor()
const {
return reg_incfactor_; }
477double SolverDDP::get_reg_decfactor()
const {
return reg_decfactor_; }
479double SolverDDP::get_regfactor()
const {
return reg_incfactor_; }
481double SolverDDP::get_reg_min()
const {
return reg_min_; }
483double SolverDDP::get_regmin()
const {
return reg_min_; }
485double SolverDDP::get_reg_max()
const {
return reg_max_; }
487double SolverDDP::get_regmax()
const {
return reg_max_; }
489const std::vector<double>& SolverDDP::get_alphas()
const {
return alphas_; }
491double SolverDDP::get_th_stepdec()
const {
return th_stepdec_; }
493double SolverDDP::get_th_stepinc()
const {
return th_stepinc_; }
495double SolverDDP::get_th_grad()
const {
return th_grad_; }
497const std::vector<Eigen::MatrixXd>& SolverDDP::get_Vxx()
const {
return Vxx_; }
499const std::vector<Eigen::VectorXd>& SolverDDP::get_Vx()
const {
return Vx_; }
501const std::vector<Eigen::MatrixXd>& SolverDDP::get_Qxx()
const {
return Qxx_; }
503const std::vector<Eigen::MatrixXd>& SolverDDP::get_Qxu()
const {
return Qxu_; }
505const std::vector<Eigen::MatrixXd>& SolverDDP::get_Quu()
const {
return Quu_; }
507const std::vector<Eigen::VectorXd>& SolverDDP::get_Qx()
const {
return Qx_; }
509const std::vector<Eigen::VectorXd>& SolverDDP::get_Qu()
const {
return Qu_; }
511const std::vector<typename MathBaseTpl<double>::MatrixXsRowMajor>&
512SolverDDP::get_K()
const {
516const std::vector<Eigen::VectorXd>& SolverDDP::get_k()
const {
return k_; }
518void SolverDDP::set_reg_incfactor(
const double regfactor) {
519 if (regfactor <= 1.) {
521 "Invalid argument: " <<
"reg_incfactor value is higher than 1.");
523 reg_incfactor_ = regfactor;
526void SolverDDP::set_reg_decfactor(
const double regfactor) {
527 if (regfactor <= 1.) {
529 "Invalid argument: " <<
"reg_decfactor value is higher than 1.");
531 reg_decfactor_ = regfactor;
534void SolverDDP::set_regfactor(
const double regfactor) {
535 if (regfactor <= 1.) {
536 throw_pretty(
"Invalid argument: " <<
"regfactor value is higher than 1.");
538 set_reg_incfactor(regfactor);
539 set_reg_decfactor(regfactor);
542void SolverDDP::set_reg_min(
const double regmin) {
544 throw_pretty(
"Invalid argument: " <<
"regmin value has to be positive.");
549void SolverDDP::set_regmin(
const double regmin) {
551 throw_pretty(
"Invalid argument: " <<
"regmin value has to be positive.");
556void SolverDDP::set_reg_max(
const double regmax) {
558 throw_pretty(
"Invalid argument: " <<
"regmax value has to be positive.");
563void SolverDDP::set_regmax(
const double regmax) {
565 throw_pretty(
"Invalid argument: " <<
"regmax value has to be positive.");
570void SolverDDP::set_alphas(
const std::vector<double>& alphas) {
571 double prev_alpha = alphas[0];
572 if (prev_alpha != 1.) {
573 std::cerr <<
"Warning: alpha[0] should be 1" << std::endl;
575 for (std::size_t i = 1; i < alphas.size(); ++i) {
576 double alpha = alphas[i];
578 throw_pretty(
"Invalid argument: " <<
"alpha values has to be positive.");
580 if (alpha >= prev_alpha) {
582 "Invalid argument: " <<
"alpha values are monotonously decreasing.");
589void SolverDDP::set_th_stepdec(
const double th_stepdec) {
590 if (0. >= th_stepdec || th_stepdec > 1.) {
592 "Invalid argument: " <<
"th_stepdec value should between 0 and 1.");
594 th_stepdec_ = th_stepdec;
597void SolverDDP::set_th_stepinc(
const double th_stepinc) {
598 if (0. >= th_stepinc || th_stepinc > 1.) {
600 "Invalid argument: " <<
"th_stepinc value should between 0 and 1.");
602 th_stepinc_ = th_stepinc;
605void SolverDDP::set_th_grad(
const double th_grad) {
607 throw_pretty(
"Invalid argument: " <<
"th_grad value has to be positive.");