9#include "crocoddyl/core/solvers/intro.hpp"
13#include "crocoddyl/core/utils/exception.hpp"
14#include "crocoddyl/core/utils/stop-watch.hpp"
24 zero_upsilon_(false) {
25 const std::size_t T = problem_->get_T();
44 const std::size_t ndx = problem_->get_ndx();
45 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
46 problem_->get_runningModels();
47 for (std::size_t t = 0; t < T; ++t) {
48 const std::shared_ptr<ActionModelAbstract>& model = models[t];
49 const std::size_t nu = model->get_nu();
50 const std::size_t nh = model->get_nh();
52 KQuu_tmp_[t] = Eigen::MatrixXd::Zero(ndx, nu);
53 YZ_[t] = Eigen::MatrixXd::Zero(nu, nu);
54 Hy_[t] = Eigen::MatrixXd::Zero(nh, nh);
55 Qz_[t] = Eigen::VectorXd::Zero(nh);
56 Qzz_[t] = Eigen::MatrixXd::Zero(nh, nh);
57 Qxz_[t] = Eigen::MatrixXd::Zero(ndx, nh);
58 Quz_[t] = Eigen::MatrixXd::Zero(nu, nh);
59 kz_[t] = Eigen::VectorXd::Zero(nu);
60 Kz_[t] = Eigen::MatrixXd::Zero(nu, ndx);
61 ks_[t] = Eigen::VectorXd::Zero(nh);
62 Ks_[t] = Eigen::MatrixXd::Zero(nh, ndx);
63 QuuinvHuT_[t] = Eigen::MatrixXd::Zero(nu, nh);
64 Qzz_llt_[t] = Eigen::LLT<Eigen::MatrixXd>(nh);
65 Hu_lu_[t] = Eigen::FullPivLU<Eigen::MatrixXd>(nh, nu);
66 Hu_qr_[t] = Eigen::ColPivHouseholderQR<Eigen::MatrixXd>(nu, nh);
67 Hy_lu_[t] = Eigen::PartialPivLU<Eigen::MatrixXd>(nh);
71SolverIntro::~SolverIntro() {}
73bool SolverIntro::solve(
const std::vector<Eigen::VectorXd>& init_xs,
74 const std::vector<Eigen::VectorXd>& init_us,
75 const std::size_t maxiter,
const bool is_feasible,
76 const double init_reg) {
77 START_PROFILER(
"SolverIntro::solve");
78 if (problem_->is_updated()) {
83 setCandidate(init_xs, init_us, is_feasible);
85 if (std::isnan(init_reg)) {
92 was_feasible_ =
false;
97 bool recalcDiff =
true;
98 for (iter_ = 0; iter_ < maxiter; ++iter_) {
101 computeDirection(recalcDiff);
102 }
catch (std::exception& e) {
104 increaseRegularization();
105 if (preg_ == reg_max_) {
119 if (hfeas_ != 0 && iter_ != 0) {
121 std::max(
upsilon_, (d_[0] + .5 * d_[1]) / ((1 -
rho_) * hfeas_));
126 for (std::vector<double>::const_iterator it = alphas_.begin();
127 it != alphas_.end(); ++it) {
130 dV_ = tryStep(steplength_);
131 dfeas_ = hfeas_ - hfeas_try_;
133 }
catch (std::exception& e) {
137 dVexp_ = steplength_ * (d_[0] + 0.5 * steplength_ * d_[1]);
138 dPhiexp_ = dVexp_ + steplength_ *
upsilon_ * dfeas_;
140 if (std::abs(d_[0]) < th_grad_ || dPhi_ > th_acceptstep_ * dPhiexp_) {
141 was_feasible_ = is_feasible_;
142 setCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1));
152 was_feasible_ = is_feasible_;
153 setCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1));
164 const std::size_t n_callbacks = callbacks_.size();
165 for (std::size_t c = 0; c < n_callbacks; ++c) {
166 CallbackAbstract& callback = *callbacks_[c];
170 if (steplength_ > th_stepdec_ && dV_ >= 0.) {
171 decreaseRegularization();
173 if (steplength_ <= th_stepinc_ || std::abs(d_[1]) <=
th_feas_) {
174 if (preg_ == reg_max_) {
175 STOP_PROFILER(
"SolverIntro::solve");
178 increaseRegularization();
181 if (is_feasible_ && stop_ < th_stop_) {
182 STOP_PROFILER(
"SolverIntro::solve");
186 STOP_PROFILER(
"SolverIntro::solve");
190double SolverIntro::tryStep(
const double steplength) {
191 forwardPass(steplength);
192 hfeas_try_ = computeEqualityFeasibility();
193 return cost_ - cost_try_;
196double SolverIntro::stoppingCriteria() {
197 stop_ = std::max(hfeas_, std::abs(d_[0] + 0.5 * d_[1]));
201void SolverIntro::resizeData() {
202 START_PROFILER(
"SolverIntro::resizeData");
203 SolverFDDP::resizeData();
205 const std::size_t T = problem_->get_T();
206 const std::size_t ndx = problem_->get_ndx();
207 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
208 problem_->get_runningModels();
209 for (std::size_t t = 0; t < T; ++t) {
210 const std::shared_ptr<ActionModelAbstract>& model = models[t];
211 const std::size_t nu = model->get_nu();
212 const std::size_t nh = model->get_nh();
213 KQuu_tmp_[t].conservativeResize(ndx, nu);
214 YZ_[t].conservativeResize(nu, nu);
215 Hy_[t].conservativeResize(nh, nh);
216 Qz_[t].conservativeResize(nh);
217 Qzz_[t].conservativeResize(nh, nh);
218 Qxz_[t].conservativeResize(ndx, nh);
219 Quz_[t].conservativeResize(nu, nh);
220 kz_[t].conservativeResize(nu);
221 Kz_[t].conservativeResize(nu, ndx);
222 ks_[t].conservativeResize(nh);
223 Ks_[t].conservativeResize(nh, ndx);
224 QuuinvHuT_[t].conservativeResize(nu, nh);
226 STOP_PROFILER(
"SolverIntro::resizeData");
229double SolverIntro::calcDiff() {
230 START_PROFILER(
"SolverIntro::calcDiff");
231 SolverFDDP::calcDiff();
232 const std::size_t T = problem_->get_T();
233 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
234 problem_->get_runningModels();
235 const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
236 problem_->get_runningDatas();
239#ifdef CROCODDYL_WITH_MULTITHREADING
240#pragma omp parallel for num_threads(problem_->get_nthreads())
242 for (std::size_t t = 0; t < T; ++t) {
243 const std::shared_ptr<crocoddyl::ActionModelAbstract>& model =
245 const std::shared_ptr<crocoddyl::ActionDataAbstract>& data = datas[t];
246 if (model->get_nu() > 0 && model->get_nh() > 0) {
247 Hu_lu_[t].compute(data->Hu);
250 const Eigen::Block<Eigen::MatrixXd, Eigen::Dynamic, Eigen::Dynamic,
253 Hy_[t].noalias() = data->Hu * Y;
255 const Eigen::Inverse<Eigen::PartialPivLU<Eigen::MatrixXd> > Hy_inv =
257 ks_[t].noalias() = Hy_inv * data->h;
258 Ks_[t].noalias() = Hy_inv * data->Hx;
259 kz_[t].noalias() = Y *
ks_[t];
260 Kz_[t].noalias() = Y *
Ks_[t];
265#ifdef CROCODDYL_WITH_MULTITHREADING
266#pragma omp parallel for num_threads(problem_->get_nthreads())
268 for (std::size_t t = 0; t < T; ++t) {
269 const std::shared_ptr<crocoddyl::ActionModelAbstract>& model =
271 const std::shared_ptr<crocoddyl::ActionDataAbstract>& data = datas[t];
272 if (model->get_nu() > 0 && model->get_nh() > 0) {
273 Hu_qr_[t].compute(data->Hu.transpose());
276 const Eigen::Block<Eigen::MatrixXd, Eigen::Dynamic, Eigen::Dynamic,
279 Hy_[t].noalias() = data->Hu * Y;
281 const Eigen::Inverse<Eigen::PartialPivLU<Eigen::MatrixXd> > Hy_inv =
283 ks_[t].noalias() = Hy_inv * data->h;
284 Ks_[t].noalias() = Hy_inv * data->Hx;
285 kz_[t].noalias() = Y *
ks_[t];
286 Kz_[t].noalias() = Y *
Ks_[t];
294 STOP_PROFILER(
"SolverIntro::calcDiff");
298void SolverIntro::computeValueFunction(
299 const std::size_t t,
const std::shared_ptr<ActionModelAbstract>& model) {
300 const std::size_t nu = model->get_nu();
304 START_PROFILER(
"SolverIntro::Vx");
305 Quuk_[t].noalias() = Quu_[t] * k_[t];
306 Vx_[t].noalias() -= Qxu_[t] * k_[t];
308 Vx_[t].noalias() -= K_[t].transpose() * Qu_[t];
310 STOP_PROFILER(
"SolverIntro::Vx");
311 START_PROFILER(
"SolverIntro::Vxx");
312 KQuu_tmp_[t].noalias() = K_[t].transpose() * Quu_[t];
313 KQuu_tmp_[t].noalias() -= 2 * Qxu_[t];
314 Vxx_[t].noalias() += KQuu_tmp_[t] * K_[t];
315 STOP_PROFILER(
"SolverIntro::Vxx");
317 Vxx_tmp_ = 0.5 * (Vxx_[t] + Vxx_[t].transpose());
320 if (!std::isnan(preg_)) {
321 Vxx_[t].diagonal().array() += preg_;
326 Vx_[t].noalias() += Vxx_[t] * fs_[t];
330void SolverIntro::computeGains(
const std::size_t t) {
331 START_PROFILER(
"SolverIntro::computeGains");
332 const std::shared_ptr<crocoddyl::ActionModelAbstract>& model =
333 problem_->get_runningModels()[t];
334 const std::shared_ptr<crocoddyl::ActionDataAbstract>& data =
335 problem_->get_runningDatas()[t];
337 const std::size_t nu = model->get_nu();
338 const std::size_t nh = model->get_nh();
342 if (nu > 0 && nh > 0) {
343 START_PROFILER(
"SolverIntro::Qzz_inv");
344 const std::size_t rank =
Hu_rank_[t];
345 const std::size_t nullity = data->Hu.cols() - rank;
346 const Eigen::Block<Eigen::MatrixXd, Eigen::Dynamic, Eigen::Dynamic,
348 Z =
YZ_[t].rightCols(nullity);
349 Quz_[t].noalias() = Quu_[t] * Z;
350 Qzz_[t].noalias() = Z.transpose() *
Quz_[t];
352 STOP_PROFILER(
"SolverIntro::Qzz_inv");
353 const Eigen::ComputationInfo& info =
Qzz_llt_[t].info();
354 if (info != Eigen::Success) {
355 throw_pretty(
"backward error");
360 Eigen::Transpose<Eigen::MatrixXd> QzzinvQzu =
Quz_[t].transpose();
361 Qzz_llt_[t].solveInPlace(QzzinvQzu);
362 Qz_[t].noalias() = Z.transpose() * Qu_[t];
364 Qxz_[t].noalias() = Qxu_[t] * Z;
365 Eigen::Transpose<Eigen::MatrixXd> Qzx =
Qxz_[t].transpose();
367 Qz_[t].noalias() -= QzzinvQzu *
kz_[t];
368 Qzx.noalias() -= QzzinvQzu *
Kz_[t];
369 k_[t].noalias() += Z *
Qz_[t];
370 K_[t].noalias() += Z * Qzx;
372 SolverFDDP::computeGains(t);
376 SolverFDDP::computeGains(t);
377 if (nu > 0 && nh > 0) {
378 START_PROFILER(
"SolverIntro::Qzz_inv");
379 QuuinvHuT_[t] = data->Hu.transpose();
380 Quu_llt_[t].solveInPlace(QuuinvHuT_[t]);
381 Qzz_[t].noalias() = data->Hu * QuuinvHuT_[t];
383 STOP_PROFILER(
"SolverIntro::Qzz_inv");
384 const Eigen::ComputationInfo& info =
Qzz_llt_[t].info();
385 if (info != Eigen::Success) {
386 throw_pretty(
"backward error");
388 Eigen::Transpose<Eigen::MatrixXd> HuQuuinv = QuuinvHuT_[t].transpose();
391 ks_[t].noalias() -= data->Hu * k_[t];
393 Ks_[t].noalias() -= data->Hu * K_[t];
394 k_[t].noalias() += QuuinvHuT_[t] *
ks_[t];
395 K_[t] += QuuinvHuT_[t] *
Ks_[t];
399 STOP_PROFILER(
"SolverIntro::computeGains");
414const std::vector<std::size_t>& SolverIntro::get_Hu_rank()
const {
451 if (0. >= rho || rho > 1.) {
452 throw_pretty(
"Invalid argument: " <<
"rho value should between 0 and 1.");
Feasibility-driven Differential Dynamic Programming (FDDP) solver.
virtual const Eigen::Vector2d & expectedImprovement()
Return the expected improvement from a given current search direction .
void updateExpectedImprovement()
Update internal values for computing the expected improvement.
std::vector< Eigen::MatrixXd > Kz_
Feedback gain in the nullspace of .
std::vector< Eigen::FullPivLU< Eigen::MatrixXd > > Hu_lu_
std::vector< Eigen::MatrixXd > Hy_
double th_feas_
Threshold for switching to feasibility.
std::vector< std::size_t > Hu_rank_
Rank of the control Jacobian of the equality constraints.
std::vector< Eigen::MatrixXd > Qxz_
Hessian of the reduced Hamiltonian .
std::vector< Eigen::VectorXd > Qz_
Jacobian of the reduced Hamiltonian .
double get_th_feas() const
Return the threshold for switching to feasibility.
const std::vector< Eigen::VectorXd > & get_kz() const
Return feedforward term related to the nullspace of .
EqualitySolverType get_equality_solver() const
Return the type of solver used for handling the equality constraints.
const std::vector< Eigen::MatrixXd > & get_YZ() const
Return the rank of control-equality constraints \mathbf{H_u}\f.
const std::vector< Eigen::MatrixXd > & get_Kz() const
Return feedback gain related to the nullspace of .
void set_equality_solver(const EqualitySolverType type)
Modify the type of solver used for handling the equality constraints.
std::vector< Eigen::MatrixXd > Quz_
Hessian of the reduced Hamiltonian .
std::vector< Eigen::MatrixXd > Ks_
Feedback gain related to the equality constraints.
std::vector< Eigen::LLT< Eigen::MatrixXd > > Qzz_llt_
Cholesky LLT solver.
std::vector< Eigen::ColPivHouseholderQR< Eigen::MatrixXd > > Hu_qr_
std::vector< Eigen::MatrixXd > Qzz_
Hessian of the reduced Hamiltonian .
void set_zero_upsilon(const bool zero_upsilon)
Modify the zero-upsilon label.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW SolverIntro(std::shared_ptr< ShootingProblem > problem)
Initialize the INTRO solver.
const std::vector< Eigen::MatrixXd > & get_Hy() const
Return span-projected Jacobian of the equality-constraint with respect to the control.
double get_rho() const
Return the rho parameter used in the merit function.
const std::vector< Eigen::MatrixXd > & get_Qxz() const
Return Hessian of the reduced Hamiltonian .
const std::vector< Eigen::MatrixXd > & get_Ks() const
Return feedback gain related to the equality constraints.
void set_th_feas(const double th_feas)
Modify the threshold for switching to feasibility.
bool get_zero_upsilon() const
Return the zero-upsilon label.
double get_upsilon() const
Return the estimated penalty parameter that balances relative contribution of the cost function and e...
std::vector< Eigen::MatrixXd > YZ_
const std::vector< Eigen::MatrixXd > & get_Quz() const
Return Hessian of the reduced Hamiltonian .
enum EqualitySolverType eq_solver_
Strategy used for handling the equality constraints.
const std::vector< Eigen::MatrixXd > & get_Qzz() const
Return Hessian of the reduced Hamiltonian .
std::vector< Eigen::VectorXd > kz_
Feedforward term in the nullspace of .
const std::vector< Eigen::VectorXd > & get_ks() const
Return feedforward term related to the equality constraints.
const std::vector< Eigen::VectorXd > & get_Qz() const
Return Jacobian of the reduced Hamiltonian .
std::vector< Eigen::PartialPivLU< Eigen::MatrixXd > > Hy_lu_
std::vector< Eigen::VectorXd > ks_
Feedforward term related to the equality constraints.
void set_rho(const double rho)
Modify the rho parameter used in the merit function.