| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /////////////////////////////////////////////////////////////////////////////// | ||
| 2 | // BSD 3-Clause License | ||
| 3 | // | ||
| 4 | // Copyright (C) 2019-2020, LAAS-CNRS, New York University, Max Planck | ||
| 5 | // Gesellschaft, | ||
| 6 | // University of Edinburgh | ||
| 7 | // Copyright note valid unless otherwise stated in individual files. | ||
| 8 | // All rights reserved. | ||
| 9 | /////////////////////////////////////////////////////////////////////////////// | ||
| 10 | |||
| 11 | #include "crocoddyl/core/solvers/kkt.hpp" | ||
| 12 | |||
| 13 | namespace crocoddyl { | ||
| 14 | |||
| 15 | ✗ | SolverKKT::SolverKKT(std::shared_ptr<ShootingProblem> problem) | |
| 16 | : SolverAbstract(problem), | ||
| 17 | ✗ | reg_incfactor_(10.), | |
| 18 | ✗ | reg_decfactor_(10.), | |
| 19 | ✗ | reg_min_(1e-9), | |
| 20 | ✗ | reg_max_(1e9), | |
| 21 | ✗ | cost_try_(0.), | |
| 22 | ✗ | th_grad_(1e-12), | |
| 23 | ✗ | was_feasible_(false) { | |
| 24 | ✗ | allocateData(); | |
| 25 | ✗ | const std::size_t n_alphas = 10; | |
| 26 | ✗ | preg_ = 0.; | |
| 27 | ✗ | dreg_ = 0.; | |
| 28 | ✗ | alphas_.resize(n_alphas); | |
| 29 | ✗ | for (std::size_t n = 0; n < n_alphas; ++n) { | |
| 30 | ✗ | alphas_[n] = 1. / pow(2., (double)n); | |
| 31 | } | ||
| 32 | ✗ | } | |
| 33 | |||
| 34 | ✗ | SolverKKT::~SolverKKT() {} | |
| 35 | |||
| 36 | ✗ | bool SolverKKT::solve(const std::vector<Eigen::VectorXd>& init_xs, | |
| 37 | const std::vector<Eigen::VectorXd>& init_us, | ||
| 38 | const std::size_t maxiter, const bool is_feasible, | ||
| 39 | const double) { | ||
| 40 | ✗ | setCandidate(init_xs, init_us, is_feasible); | |
| 41 | ✗ | bool recalc = true; | |
| 42 | ✗ | for (iter_ = 0; iter_ < maxiter; ++iter_) { | |
| 43 | while (true) { | ||
| 44 | try { | ||
| 45 | ✗ | computeDirection(recalc); | |
| 46 | ✗ | } catch (std::exception& e) { | |
| 47 | ✗ | recalc = false; | |
| 48 | ✗ | if (preg_ == reg_max_) { | |
| 49 | ✗ | return false; | |
| 50 | } else { | ||
| 51 | ✗ | continue; | |
| 52 | } | ||
| 53 | ✗ | } | |
| 54 | ✗ | break; | |
| 55 | ✗ | } | |
| 56 | |||
| 57 | ✗ | expectedImprovement(); | |
| 58 | ✗ | for (std::vector<double>::const_iterator it = alphas_.begin(); | |
| 59 | ✗ | it != alphas_.end(); ++it) { | |
| 60 | ✗ | steplength_ = *it; | |
| 61 | try { | ||
| 62 | ✗ | dV_ = tryStep(steplength_); | |
| 63 | ✗ | } catch (std::exception& e) { | |
| 64 | ✗ | continue; | |
| 65 | ✗ | } | |
| 66 | ✗ | dVexp_ = steplength_ * d_[0] + 0.5 * steplength_ * steplength_ * d_[1]; | |
| 67 | ✗ | if (d_[0] < th_grad_ || !is_feasible_ || dV_ > th_acceptstep_ * dVexp_) { | |
| 68 | ✗ | was_feasible_ = is_feasible_; | |
| 69 | ✗ | setCandidate(xs_try_, us_try_, true); | |
| 70 | ✗ | cost_ = cost_try_; | |
| 71 | ✗ | break; | |
| 72 | } | ||
| 73 | } | ||
| 74 | ✗ | stoppingCriteria(); | |
| 75 | ✗ | const std::size_t n_callbacks = callbacks_.size(); | |
| 76 | ✗ | if (n_callbacks != 0) { | |
| 77 | ✗ | for (std::size_t c = 0; c < n_callbacks; ++c) { | |
| 78 | ✗ | CallbackAbstract& callback = *callbacks_[c]; | |
| 79 | ✗ | callback(*this); | |
| 80 | } | ||
| 81 | } | ||
| 82 | ✗ | if (was_feasible_ && stop_ < th_stop_) { | |
| 83 | ✗ | return true; | |
| 84 | } | ||
| 85 | } | ||
| 86 | ✗ | return false; | |
| 87 | } | ||
| 88 | |||
| 89 | ✗ | void SolverKKT::computeDirection(const bool recalc) { | |
| 90 | ✗ | const std::size_t T = problem_->get_T(); | |
| 91 | ✗ | if (recalc) { | |
| 92 | ✗ | calcDiff(); | |
| 93 | } | ||
| 94 | ✗ | computePrimalDual(); | |
| 95 | const Eigen::VectorBlock<Eigen::VectorXd, Eigen::Dynamic> p_x = | ||
| 96 | ✗ | primal_.segment(0, ndx_); | |
| 97 | const Eigen::VectorBlock<Eigen::VectorXd, Eigen::Dynamic> p_u = | ||
| 98 | ✗ | primal_.segment(ndx_, nu_); | |
| 99 | |||
| 100 | ✗ | std::size_t ix = 0; | |
| 101 | ✗ | std::size_t iu = 0; | |
| 102 | const std::vector<std::shared_ptr<ActionModelAbstract> >& models = | ||
| 103 | ✗ | problem_->get_runningModels(); | |
| 104 | ✗ | for (std::size_t t = 0; t < T; ++t) { | |
| 105 | ✗ | const std::size_t ndxi = models[t]->get_state()->get_ndx(); | |
| 106 | ✗ | const std::size_t nui = models[t]->get_nu(); | |
| 107 | ✗ | dxs_[t] = p_x.segment(ix, ndxi); | |
| 108 | ✗ | dus_[t] = p_u.segment(iu, nui); | |
| 109 | ✗ | lambdas_[t] = dual_.segment(ix, ndxi); | |
| 110 | ✗ | ix += ndxi; | |
| 111 | ✗ | iu += nui; | |
| 112 | } | ||
| 113 | const std::size_t ndxi = | ||
| 114 | ✗ | problem_->get_terminalModel()->get_state()->get_ndx(); | |
| 115 | ✗ | dxs_.back() = p_x.segment(ix, ndxi); | |
| 116 | ✗ | lambdas_.back() = dual_.segment(ix, ndxi); | |
| 117 | ✗ | } | |
| 118 | |||
| 119 | ✗ | double SolverKKT::tryStep(const double steplength) { | |
| 120 | ✗ | const std::size_t T = problem_->get_T(); | |
| 121 | const std::vector<std::shared_ptr<ActionModelAbstract> >& models = | ||
| 122 | ✗ | problem_->get_runningModels(); | |
| 123 | ✗ | for (std::size_t t = 0; t < T; ++t) { | |
| 124 | ✗ | const std::shared_ptr<ActionModelAbstract>& m = models[t]; | |
| 125 | |||
| 126 | ✗ | m->get_state()->integrate(xs_[t], steplength * dxs_[t], xs_try_[t]); | |
| 127 | ✗ | if (m->get_nu() != 0) { | |
| 128 | ✗ | us_try_[t] = us_[t]; | |
| 129 | ✗ | us_try_[t] += steplength * dus_[t]; | |
| 130 | } | ||
| 131 | } | ||
| 132 | ✗ | const std::shared_ptr<ActionModelAbstract> m = problem_->get_terminalModel(); | |
| 133 | ✗ | m->get_state()->integrate(xs_[T], steplength * dxs_[T], xs_try_[T]); | |
| 134 | ✗ | cost_try_ = problem_->calc(xs_try_, us_try_); | |
| 135 | ✗ | return cost_ - cost_try_; | |
| 136 | ✗ | } | |
| 137 | |||
| 138 | ✗ | double SolverKKT::stoppingCriteria() { | |
| 139 | ✗ | const std::size_t T = problem_->get_T(); | |
| 140 | ✗ | std::size_t ix = 0; | |
| 141 | ✗ | std::size_t iu = 0; | |
| 142 | const std::vector<std::shared_ptr<ActionModelAbstract> >& models = | ||
| 143 | ✗ | problem_->get_runningModels(); | |
| 144 | const std::vector<std::shared_ptr<ActionDataAbstract> >& datas = | ||
| 145 | ✗ | problem_->get_runningDatas(); | |
| 146 | ✗ | for (std::size_t t = 0; t < T; ++t) { | |
| 147 | ✗ | const std::shared_ptr<ActionDataAbstract>& d = datas[t]; | |
| 148 | ✗ | const std::size_t ndxi = models[t]->get_state()->get_ndx(); | |
| 149 | ✗ | const std::size_t nui = models[t]->get_nu(); | |
| 150 | |||
| 151 | ✗ | dF.segment(ix, ndxi) = lambdas_[t]; | |
| 152 | ✗ | dF.segment(ix, ndxi).noalias() -= d->Fx.transpose() * lambdas_[t + 1]; | |
| 153 | ✗ | dF.segment(ndx_ + iu, nui).noalias() = -lambdas_[t + 1].transpose() * d->Fu; | |
| 154 | ✗ | ix += ndxi; | |
| 155 | ✗ | iu += nui; | |
| 156 | } | ||
| 157 | const std::size_t ndxi = | ||
| 158 | ✗ | problem_->get_terminalModel()->get_state()->get_ndx(); | |
| 159 | ✗ | dF.segment(ix, ndxi) = lambdas_.back(); | |
| 160 | ✗ | stop_ = (kktref_.segment(0, ndx_ + nu_) + dF).squaredNorm() + | |
| 161 | ✗ | kktref_.segment(ndx_ + nu_, ndx_).squaredNorm(); | |
| 162 | ✗ | return stop_; | |
| 163 | } | ||
| 164 | |||
| 165 | ✗ | const Eigen::Vector2d& SolverKKT::expectedImprovement() { | |
| 166 | ✗ | d_ = Eigen::Vector2d::Zero(); | |
| 167 | // -grad^T.primal | ||
| 168 | ✗ | d_(0) = -kktref_.segment(0, ndx_ + nu_).dot(primal_); | |
| 169 | // -(hessian.primal)^T.primal | ||
| 170 | ✗ | kkt_primal_.noalias() = kkt_.block(0, 0, ndx_ + nu_, ndx_ + nu_) * primal_; | |
| 171 | ✗ | d_(1) = -kkt_primal_.dot(primal_); | |
| 172 | ✗ | return d_; | |
| 173 | } | ||
| 174 | |||
| 175 | ✗ | const Eigen::MatrixXd& SolverKKT::get_kkt() const { return kkt_; } | |
| 176 | |||
| 177 | ✗ | const Eigen::VectorXd& SolverKKT::get_kktref() const { return kktref_; } | |
| 178 | |||
| 179 | ✗ | const Eigen::VectorXd& SolverKKT::get_primaldual() const { return primaldual_; } | |
| 180 | |||
| 181 | ✗ | const std::vector<Eigen::VectorXd>& SolverKKT::get_dxs() const { return dxs_; } | |
| 182 | |||
| 183 | ✗ | const std::vector<Eigen::VectorXd>& SolverKKT::get_dus() const { return dus_; } | |
| 184 | |||
| 185 | ✗ | const std::vector<Eigen::VectorXd>& SolverKKT::get_lambdas() const { | |
| 186 | ✗ | return lambdas_; | |
| 187 | } | ||
| 188 | |||
| 189 | ✗ | std::size_t SolverKKT::get_nx() const { return nx_; } | |
| 190 | |||
| 191 | ✗ | std::size_t SolverKKT::get_ndx() const { return ndx_; } | |
| 192 | |||
| 193 | ✗ | std::size_t SolverKKT::get_nu() const { return nu_; } | |
| 194 | |||
| 195 | ✗ | double SolverKKT::calcDiff() { | |
| 196 | ✗ | cost_ = problem_->calc(xs_, us_); | |
| 197 | ✗ | cost_ = problem_->calcDiff(xs_, us_); | |
| 198 | |||
| 199 | // offset on constraint xnext = f(x,u) due to x0 = ref. | ||
| 200 | const std::size_t cx0 = | ||
| 201 | ✗ | problem_->get_runningModels()[0]->get_state()->get_ndx(); | |
| 202 | |||
| 203 | ✗ | std::size_t ix = 0; | |
| 204 | ✗ | std::size_t iu = 0; | |
| 205 | ✗ | const std::size_t T = problem_->get_T(); | |
| 206 | ✗ | kkt_.block(ndx_ + nu_, 0, ndx_, ndx_).setIdentity(); | |
| 207 | ✗ | for (std::size_t t = 0; t < T; ++t) { | |
| 208 | const std::shared_ptr<ActionModelAbstract>& m = | ||
| 209 | ✗ | problem_->get_runningModels()[t]; | |
| 210 | const std::shared_ptr<ActionDataAbstract>& d = | ||
| 211 | ✗ | problem_->get_runningDatas()[t]; | |
| 212 | ✗ | const std::size_t ndxi = m->get_state()->get_ndx(); | |
| 213 | ✗ | const std::size_t nui = m->get_nu(); | |
| 214 | |||
| 215 | // Computing the gap at the initial state | ||
| 216 | ✗ | if (t == 0) { | |
| 217 | ✗ | m->get_state()->diff(problem_->get_x0(), xs_[0], | |
| 218 | ✗ | kktref_.segment(ndx_ + nu_, ndxi)); | |
| 219 | } | ||
| 220 | |||
| 221 | // Filling KKT matrix | ||
| 222 | ✗ | kkt_.block(ix, ix, ndxi, ndxi) = d->Lxx; | |
| 223 | ✗ | kkt_.block(ix, ndx_ + iu, ndxi, nui) = d->Lxu; | |
| 224 | ✗ | kkt_.block(ndx_ + iu, ix, nui, ndxi) = d->Lxu.transpose(); | |
| 225 | ✗ | kkt_.block(ndx_ + iu, ndx_ + iu, nui, nui) = d->Luu; | |
| 226 | ✗ | kkt_.block(ndx_ + nu_ + cx0 + ix, ix, ndxi, ndxi) = -d->Fx; | |
| 227 | ✗ | kkt_.block(ndx_ + nu_ + cx0 + ix, ndx_ + iu, ndxi, nui) = -d->Fu; | |
| 228 | |||
| 229 | // Filling KKT vector | ||
| 230 | ✗ | kktref_.segment(ix, ndxi) = d->Lx; | |
| 231 | ✗ | kktref_.segment(ndx_ + iu, nui) = d->Lu; | |
| 232 | ✗ | m->get_state()->diff(d->xnext, xs_[t + 1], | |
| 233 | ✗ | kktref_.segment(ndx_ + nu_ + cx0 + ix, ndxi)); | |
| 234 | |||
| 235 | ✗ | ix += ndxi; | |
| 236 | ✗ | iu += nui; | |
| 237 | } | ||
| 238 | ✗ | const std::shared_ptr<ActionDataAbstract>& df = problem_->get_terminalData(); | |
| 239 | const std::size_t ndxf = | ||
| 240 | ✗ | problem_->get_terminalModel()->get_state()->get_ndx(); | |
| 241 | ✗ | kkt_.block(ix, ix, ndxf, ndxf) = df->Lxx; | |
| 242 | ✗ | kktref_.segment(ix, ndxf) = df->Lx; | |
| 243 | ✗ | kkt_.block(0, ndx_ + nu_, ndx_ + nu_, ndx_) = | |
| 244 | ✗ | kkt_.block(ndx_ + nu_, 0, ndx_, ndx_ + nu_).transpose(); | |
| 245 | ✗ | return cost_; | |
| 246 | } | ||
| 247 | |||
| 248 | ✗ | void SolverKKT::computePrimalDual() { | |
| 249 | ✗ | primaldual_ = kkt_.lu().solve(-kktref_); | |
| 250 | ✗ | primal_ = primaldual_.segment(0, ndx_ + nu_); | |
| 251 | ✗ | dual_ = primaldual_.segment(ndx_ + nu_, ndx_); | |
| 252 | ✗ | } | |
| 253 | |||
| 254 | ✗ | void SolverKKT::increaseRegularization() { | |
| 255 | ✗ | preg_ *= reg_incfactor_; | |
| 256 | ✗ | if (preg_ > reg_max_) { | |
| 257 | ✗ | preg_ = reg_max_; | |
| 258 | } | ||
| 259 | ✗ | dreg_ = preg_; | |
| 260 | ✗ | } | |
| 261 | |||
| 262 | ✗ | void SolverKKT::decreaseRegularization() { | |
| 263 | ✗ | preg_ /= reg_decfactor_; | |
| 264 | ✗ | if (preg_ < reg_min_) { | |
| 265 | ✗ | preg_ = reg_min_; | |
| 266 | } | ||
| 267 | ✗ | dreg_ = preg_; | |
| 268 | ✗ | } | |
| 269 | |||
| 270 | ✗ | void SolverKKT::allocateData() { | |
| 271 | ✗ | const std::size_t T = problem_->get_T(); | |
| 272 | ✗ | dxs_.resize(T + 1); | |
| 273 | ✗ | dus_.resize(T); | |
| 274 | ✗ | lambdas_.resize(T + 1); | |
| 275 | ✗ | xs_try_.resize(T + 1); | |
| 276 | ✗ | us_try_.resize(T); | |
| 277 | |||
| 278 | ✗ | nx_ = 0; | |
| 279 | ✗ | ndx_ = 0; | |
| 280 | ✗ | nu_ = 0; | |
| 281 | ✗ | const std::size_t nx = problem_->get_nx(); | |
| 282 | ✗ | const std::size_t ndx = problem_->get_ndx(); | |
| 283 | const std::vector<std::shared_ptr<ActionModelAbstract> >& models = | ||
| 284 | ✗ | problem_->get_runningModels(); | |
| 285 | ✗ | for (std::size_t t = 0; t < T; ++t) { | |
| 286 | ✗ | const std::shared_ptr<ActionModelAbstract>& model = models[t]; | |
| 287 | ✗ | if (t == 0) { | |
| 288 | ✗ | xs_try_[t] = problem_->get_x0(); | |
| 289 | } else { | ||
| 290 | ✗ | xs_try_[t] = Eigen::VectorXd::Constant(nx, NAN); | |
| 291 | } | ||
| 292 | ✗ | const std::size_t nu = model->get_nu(); | |
| 293 | ✗ | us_try_[t] = Eigen::VectorXd::Constant(nu, NAN); | |
| 294 | ✗ | dxs_[t] = Eigen::VectorXd::Zero(ndx); | |
| 295 | ✗ | dus_[t] = Eigen::VectorXd::Zero(nu); | |
| 296 | ✗ | lambdas_[t] = Eigen::VectorXd::Zero(ndx); | |
| 297 | ✗ | nx_ += nx; | |
| 298 | ✗ | ndx_ += ndx; | |
| 299 | ✗ | nu_ += nu; | |
| 300 | } | ||
| 301 | ✗ | nx_ += nx; | |
| 302 | ✗ | ndx_ += ndx; | |
| 303 | ✗ | xs_try_.back() = problem_->get_terminalModel()->get_state()->zero(); | |
| 304 | ✗ | dxs_.back() = Eigen::VectorXd::Zero(ndx); | |
| 305 | ✗ | lambdas_.back() = Eigen::VectorXd::Zero(ndx); | |
| 306 | |||
| 307 | // Set dimensions for kkt matrix and kkt_ref vector | ||
| 308 | ✗ | kkt_.resize(2 * ndx_ + nu_, 2 * ndx_ + nu_); | |
| 309 | ✗ | kkt_.setZero(); | |
| 310 | ✗ | kktref_.resize(2 * ndx_ + nu_); | |
| 311 | ✗ | kktref_.setZero(); | |
| 312 | ✗ | primaldual_.resize(2 * ndx_ + nu_); | |
| 313 | ✗ | primaldual_.setZero(); | |
| 314 | ✗ | primal_.resize(ndx_ + nu_); | |
| 315 | ✗ | primal_.setZero(); | |
| 316 | ✗ | kkt_primal_.resize(ndx_ + nu_); | |
| 317 | ✗ | kkt_primal_.setZero(); | |
| 318 | ✗ | dual_.resize(ndx_); | |
| 319 | ✗ | dual_.setZero(); | |
| 320 | ✗ | dF.resize(ndx_ + nu_); | |
| 321 | ✗ | dF.setZero(); | |
| 322 | ✗ | } | |
| 323 | |||
| 324 | } // namespace crocoddyl | ||
| 325 |