9#include "crocoddyl/core/solvers/intro.hpp"
11#include "crocoddyl/core/utils/stop-watch.hpp"
21 zero_upsilon_(false) {
22 const std::size_t T = problem_->get_T();
41 const std::size_t ndx = problem_->get_ndx();
42 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
43 problem_->get_runningModels();
44 for (std::size_t t = 0; t < T; ++t) {
45 const std::shared_ptr<ActionModelAbstract>& model = models[t];
46 const std::size_t nu = model->get_nu();
47 const std::size_t nh = model->get_nh();
49 KQuu_tmp_[t] = Eigen::MatrixXd::Zero(ndx, nu);
50 YZ_[t] = Eigen::MatrixXd::Zero(nu, nu);
51 Hy_[t] = Eigen::MatrixXd::Zero(nh, nh);
52 Qz_[t] = Eigen::VectorXd::Zero(nh);
53 Qzz_[t] = Eigen::MatrixXd::Zero(nh, nh);
54 Qxz_[t] = Eigen::MatrixXd::Zero(ndx, nh);
55 Quz_[t] = Eigen::MatrixXd::Zero(nu, nh);
56 kz_[t] = Eigen::VectorXd::Zero(nu);
57 Kz_[t] = Eigen::MatrixXd::Zero(nu, ndx);
58 ks_[t] = Eigen::VectorXd::Zero(nh);
59 Ks_[t] = Eigen::MatrixXd::Zero(nh, ndx);
60 QuuinvHuT_[t] = Eigen::MatrixXd::Zero(nu, nh);
61 Qzz_llt_[t] = Eigen::LLT<Eigen::MatrixXd>(nh);
62 Hu_lu_[t] = Eigen::FullPivLU<Eigen::MatrixXd>(nh, nu);
63 Hu_qr_[t] = Eigen::ColPivHouseholderQR<Eigen::MatrixXd>(nu, nh);
64 Hy_lu_[t] = Eigen::PartialPivLU<Eigen::MatrixXd>(nh);
68SolverIntro::~SolverIntro() {}
70bool SolverIntro::solve(
const std::vector<Eigen::VectorXd>& init_xs,
71 const std::vector<Eigen::VectorXd>& init_us,
72 const std::size_t maxiter,
const bool is_feasible,
73 const double init_reg) {
74 START_PROFILER(
"SolverIntro::solve");
75 if (problem_->is_updated()) {
80 setCandidate(init_xs, init_us, is_feasible);
82 if (std::isnan(init_reg)) {
89 was_feasible_ =
false;
94 bool recalcDiff =
true;
95 for (iter_ = 0; iter_ < maxiter; ++iter_) {
98 computeDirection(recalcDiff);
99 }
catch (std::exception& e) {
101 increaseRegularization();
102 if (preg_ == reg_max_) {
116 if (hfeas_ != 0 && iter_ != 0) {
118 std::max(
upsilon_, (d_[0] + .5 * d_[1]) / ((1 -
rho_) * hfeas_));
123 for (std::vector<double>::const_iterator it = alphas_.begin();
124 it != alphas_.end(); ++it) {
127 dV_ = tryStep(steplength_);
128 dfeas_ = hfeas_ - hfeas_try_;
130 }
catch (std::exception& e) {
134 dVexp_ = steplength_ * (d_[0] + 0.5 * steplength_ * d_[1]);
135 dPhiexp_ = dVexp_ + steplength_ *
upsilon_ * dfeas_;
137 if (std::abs(d_[0]) < th_grad_ || dPhi_ > th_acceptstep_ * dPhiexp_) {
138 was_feasible_ = is_feasible_;
139 setCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1));
149 was_feasible_ = is_feasible_;
150 setCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1));
161 const std::size_t n_callbacks = callbacks_.size();
162 for (std::size_t c = 0; c < n_callbacks; ++c) {
163 CallbackAbstract& callback = *callbacks_[c];
167 if (steplength_ > th_stepdec_ && dV_ >= 0.) {
168 decreaseRegularization();
170 if (steplength_ <= th_stepinc_ || std::abs(d_[1]) <=
th_feas_) {
171 if (preg_ == reg_max_) {
172 STOP_PROFILER(
"SolverIntro::solve");
175 increaseRegularization();
178 if (is_feasible_ && stop_ < th_stop_) {
179 STOP_PROFILER(
"SolverIntro::solve");
183 STOP_PROFILER(
"SolverIntro::solve");
187double SolverIntro::tryStep(
const double steplength) {
188 forwardPass(steplength);
189 hfeas_try_ = computeEqualityFeasibility();
190 return cost_ - cost_try_;
193double SolverIntro::stoppingCriteria() {
194 stop_ = std::max(hfeas_, std::abs(d_[0] + 0.5 * d_[1]));
198void SolverIntro::resizeData() {
199 START_PROFILER(
"SolverIntro::resizeData");
200 SolverFDDP::resizeData();
202 const std::size_t T = problem_->get_T();
203 const std::size_t ndx = problem_->get_ndx();
204 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
205 problem_->get_runningModels();
206 for (std::size_t t = 0; t < T; ++t) {
207 const std::shared_ptr<ActionModelAbstract>& model = models[t];
208 const std::size_t nu = model->get_nu();
209 const std::size_t nh = model->get_nh();
210 KQuu_tmp_[t].conservativeResize(ndx, nu);
211 YZ_[t].conservativeResize(nu, nu);
212 Hy_[t].conservativeResize(nh, nh);
213 Qz_[t].conservativeResize(nh);
214 Qzz_[t].conservativeResize(nh, nh);
215 Qxz_[t].conservativeResize(ndx, nh);
216 Quz_[t].conservativeResize(nu, nh);
217 kz_[t].conservativeResize(nu);
218 Kz_[t].conservativeResize(nu, ndx);
219 ks_[t].conservativeResize(nh);
220 Ks_[t].conservativeResize(nh, ndx);
221 QuuinvHuT_[t].conservativeResize(nu, nh);
223 STOP_PROFILER(
"SolverIntro::resizeData");
226double SolverIntro::calcDiff() {
227 START_PROFILER(
"SolverIntro::calcDiff");
228 SolverFDDP::calcDiff();
229 const std::size_t T = problem_->get_T();
230 const std::vector<std::shared_ptr<ActionModelAbstract> >& models =
231 problem_->get_runningModels();
232 const std::vector<std::shared_ptr<ActionDataAbstract> >& datas =
233 problem_->get_runningDatas();
236#ifdef CROCODDYL_WITH_MULTITHREADING
237#pragma omp parallel for num_threads(problem_->get_nthreads())
239 for (std::size_t t = 0; t < T; ++t) {
240 const std::shared_ptr<crocoddyl::ActionModelAbstract>& model =
242 const std::shared_ptr<crocoddyl::ActionDataAbstract>& data = datas[t];
243 if (model->get_nu() > 0 && model->get_nh() > 0) {
244 Hu_lu_[t].compute(data->Hu);
247 (
Hu_lu_[t].permutationP() * data->Hu).transpose();
249 const Eigen::Block<Eigen::MatrixXd, Eigen::Dynamic, Eigen::Dynamic,
252 Hy_[t].noalias() = data->Hu * Y;
254 const Eigen::Inverse<Eigen::PartialPivLU<Eigen::MatrixXd> > Hy_inv =
256 ks_[t].noalias() = Hy_inv * data->h;
257 Ks_[t].noalias() = Hy_inv * data->Hx;
258 kz_[t].noalias() = Y *
ks_[t];
259 Kz_[t].noalias() = Y *
Ks_[t];
264#ifdef CROCODDYL_WITH_MULTITHREADING
265#pragma omp parallel for num_threads(problem_->get_nthreads())
267 for (std::size_t t = 0; t < T; ++t) {
268 const std::shared_ptr<crocoddyl::ActionModelAbstract>& model =
270 const std::shared_ptr<crocoddyl::ActionDataAbstract>& data = datas[t];
271 if (model->get_nu() > 0 && model->get_nh() > 0) {
272 Hu_qr_[t].compute(data->Hu.transpose());
275 const Eigen::Block<Eigen::MatrixXd, Eigen::Dynamic, Eigen::Dynamic,
278 Hy_[t].noalias() = data->Hu * Y;
280 const Eigen::Inverse<Eigen::PartialPivLU<Eigen::MatrixXd> > Hy_inv =
282 ks_[t].noalias() = Hy_inv * data->h;
283 Ks_[t].noalias() = Hy_inv * data->Hx;
284 kz_[t].noalias() = Y *
ks_[t];
285 Kz_[t].noalias() = Y *
Ks_[t];
293 STOP_PROFILER(
"SolverIntro::calcDiff");
297void SolverIntro::computeValueFunction(
298 const std::size_t t,
const std::shared_ptr<ActionModelAbstract>& model) {
299 const std::size_t nu = model->get_nu();
303 START_PROFILER(
"SolverIntro::Vx");
304 Quuk_[t].noalias() = Quu_[t] * k_[t];
305 Vx_[t].noalias() -= Qxu_[t] * k_[t];
307 Vx_[t].noalias() -= K_[t].transpose() * Qu_[t];
309 STOP_PROFILER(
"SolverIntro::Vx");
310 START_PROFILER(
"SolverIntro::Vxx");
311 KQuu_tmp_[t].noalias() = K_[t].transpose() * Quu_[t];
312 KQuu_tmp_[t].noalias() -= 2 * Qxu_[t];
313 Vxx_[t].noalias() += KQuu_tmp_[t] * K_[t];
314 STOP_PROFILER(
"SolverIntro::Vxx");
316 Vxx_tmp_ = 0.5 * (Vxx_[t] + Vxx_[t].transpose());
319 if (!std::isnan(preg_)) {
320 Vxx_[t].diagonal().array() += preg_;
325 Vx_[t].noalias() += Vxx_[t] * fs_[t];
329void SolverIntro::computeGains(
const std::size_t t) {
330 START_PROFILER(
"SolverIntro::computeGains");
331 const std::shared_ptr<crocoddyl::ActionModelAbstract>& model =
332 problem_->get_runningModels()[t];
333 const std::shared_ptr<crocoddyl::ActionDataAbstract>& data =
334 problem_->get_runningDatas()[t];
336 const std::size_t nu = model->get_nu();
337 const std::size_t nh = model->get_nh();
341 if (nu > 0 && nh > 0) {
342 START_PROFILER(
"SolverIntro::Qzz_inv");
343 const std::size_t rank =
Hu_rank_[t];
344 const std::size_t nullity = data->Hu.cols() - rank;
345 const Eigen::Block<Eigen::MatrixXd, Eigen::Dynamic, Eigen::Dynamic,
347 Z =
YZ_[t].rightCols(nullity);
348 Quz_[t].noalias() = Quu_[t] * Z;
349 Qzz_[t].noalias() = Z.transpose() *
Quz_[t];
351 STOP_PROFILER(
"SolverIntro::Qzz_inv");
352 const Eigen::ComputationInfo& info =
Qzz_llt_[t].info();
353 if (info != Eigen::Success) {
354 throw_pretty(
"backward error");
359 Eigen::Transpose<Eigen::MatrixXd> QzzinvQzu =
Quz_[t].transpose();
360 Qzz_llt_[t].solveInPlace(QzzinvQzu);
361 Qz_[t].noalias() = Z.transpose() * Qu_[t];
363 Qxz_[t].noalias() = Qxu_[t] * Z;
364 Eigen::Transpose<Eigen::MatrixXd> Qzx =
Qxz_[t].transpose();
366 Qz_[t].noalias() -= QzzinvQzu *
kz_[t];
367 Qzx.noalias() -= QzzinvQzu *
Kz_[t];
368 k_[t].noalias() += Z *
Qz_[t];
369 K_[t].noalias() += Z * Qzx;
371 SolverFDDP::computeGains(t);
375 SolverFDDP::computeGains(t);
376 if (nu > 0 && nh > 0) {
377 START_PROFILER(
"SolverIntro::Qzz_inv");
378 QuuinvHuT_[t] = data->Hu.transpose();
379 Quu_llt_[t].solveInPlace(QuuinvHuT_[t]);
380 Qzz_[t].noalias() = data->Hu * QuuinvHuT_[t];
382 STOP_PROFILER(
"SolverIntro::Qzz_inv");
383 const Eigen::ComputationInfo& info =
Qzz_llt_[t].info();
384 if (info != Eigen::Success) {
385 throw_pretty(
"backward error");
387 Eigen::Transpose<Eigen::MatrixXd> HuQuuinv = QuuinvHuT_[t].transpose();
390 ks_[t].noalias() -= data->Hu * k_[t];
392 Ks_[t].noalias() -= data->Hu * K_[t];
393 k_[t].noalias() += QuuinvHuT_[t] *
ks_[t];
394 K_[t] += QuuinvHuT_[t] *
Ks_[t];
398 STOP_PROFILER(
"SolverIntro::computeGains");
413const std::vector<std::size_t>& SolverIntro::get_Hu_rank()
const {
450 if (0. >= rho || rho > 1.) {
451 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.