Crocoddyl
kkt.cpp
1 // 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.
10 
11 #include "crocoddyl/core/solvers/kkt.hpp"
12 
13 namespace crocoddyl {
14 
15 SolverKKT::SolverKKT(boost::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<boost::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<boost::shared_ptr<ActionModelAbstract> >& models =
122  problem_->get_runningModels();
123  for (std::size_t t = 0; t < T; ++t) {
124  const boost::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 boost::shared_ptr<ActionModelAbstract> m =
133  problem_->get_terminalModel();
134  m->get_state()->integrate(xs_[T], steplength * dxs_[T], xs_try_[T]);
135  cost_try_ = problem_->calc(xs_try_, us_try_);
136  return cost_ - cost_try_;
137 }
138 
139 double SolverKKT::stoppingCriteria() {
140  const std::size_t T = problem_->get_T();
141  std::size_t ix = 0;
142  std::size_t iu = 0;
143  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
144  problem_->get_runningModels();
145  const std::vector<boost::shared_ptr<ActionDataAbstract> >& datas =
146  problem_->get_runningDatas();
147  for (std::size_t t = 0; t < T; ++t) {
148  const boost::shared_ptr<ActionDataAbstract>& d = datas[t];
149  const std::size_t ndxi = models[t]->get_state()->get_ndx();
150  const std::size_t nui = models[t]->get_nu();
151 
152  dF.segment(ix, ndxi) = lambdas_[t];
153  dF.segment(ix, ndxi).noalias() -= d->Fx.transpose() * lambdas_[t + 1];
154  dF.segment(ndx_ + iu, nui).noalias() = -lambdas_[t + 1].transpose() * d->Fu;
155  ix += ndxi;
156  iu += nui;
157  }
158  const std::size_t ndxi =
159  problem_->get_terminalModel()->get_state()->get_ndx();
160  dF.segment(ix, ndxi) = lambdas_.back();
161  stop_ = (kktref_.segment(0, ndx_ + nu_) + dF).squaredNorm() +
162  kktref_.segment(ndx_ + nu_, ndx_).squaredNorm();
163  return stop_;
164 }
165 
166 const Eigen::Vector2d& SolverKKT::expectedImprovement() {
167  d_ = Eigen::Vector2d::Zero();
168  // -grad^T.primal
169  d_(0) = -kktref_.segment(0, ndx_ + nu_).dot(primal_);
170  // -(hessian.primal)^T.primal
171  kkt_primal_.noalias() = kkt_.block(0, 0, ndx_ + nu_, ndx_ + nu_) * primal_;
172  d_(1) = -kkt_primal_.dot(primal_);
173  return d_;
174 }
175 
176 const Eigen::MatrixXd& SolverKKT::get_kkt() const { return kkt_; }
177 
178 const Eigen::VectorXd& SolverKKT::get_kktref() const { return kktref_; }
179 
180 const Eigen::VectorXd& SolverKKT::get_primaldual() const { return primaldual_; }
181 
182 const std::vector<Eigen::VectorXd>& SolverKKT::get_dxs() const { return dxs_; }
183 
184 const std::vector<Eigen::VectorXd>& SolverKKT::get_dus() const { return dus_; }
185 
186 const std::vector<Eigen::VectorXd>& SolverKKT::get_lambdas() const {
187  return lambdas_;
188 }
189 
190 std::size_t SolverKKT::get_nx() const { return nx_; }
191 
192 std::size_t SolverKKT::get_ndx() const { return ndx_; }
193 
194 std::size_t SolverKKT::get_nu() const { return nu_; }
195 
196 double SolverKKT::calcDiff() {
197  cost_ = problem_->calc(xs_, us_);
198  cost_ = problem_->calcDiff(xs_, us_);
199 
200  // offset on constraint xnext = f(x,u) due to x0 = ref.
201  const std::size_t cx0 =
202  problem_->get_runningModels()[0]->get_state()->get_ndx();
203 
204  std::size_t ix = 0;
205  std::size_t iu = 0;
206  const std::size_t T = problem_->get_T();
207  kkt_.block(ndx_ + nu_, 0, ndx_, ndx_).setIdentity();
208  for (std::size_t t = 0; t < T; ++t) {
209  const boost::shared_ptr<ActionModelAbstract>& m =
210  problem_->get_runningModels()[t];
211  const boost::shared_ptr<ActionDataAbstract>& d =
212  problem_->get_runningDatas()[t];
213  const std::size_t ndxi = m->get_state()->get_ndx();
214  const std::size_t nui = m->get_nu();
215 
216  // Computing the gap at the initial state
217  if (t == 0) {
218  m->get_state()->diff(problem_->get_x0(), xs_[0],
219  kktref_.segment(ndx_ + nu_, ndxi));
220  }
221 
222  // Filling KKT matrix
223  kkt_.block(ix, ix, ndxi, ndxi) = d->Lxx;
224  kkt_.block(ix, ndx_ + iu, ndxi, nui) = d->Lxu;
225  kkt_.block(ndx_ + iu, ix, nui, ndxi) = d->Lxu.transpose();
226  kkt_.block(ndx_ + iu, ndx_ + iu, nui, nui) = d->Luu;
227  kkt_.block(ndx_ + nu_ + cx0 + ix, ix, ndxi, ndxi) = -d->Fx;
228  kkt_.block(ndx_ + nu_ + cx0 + ix, ndx_ + iu, ndxi, nui) = -d->Fu;
229 
230  // Filling KKT vector
231  kktref_.segment(ix, ndxi) = d->Lx;
232  kktref_.segment(ndx_ + iu, nui) = d->Lu;
233  m->get_state()->diff(d->xnext, xs_[t + 1],
234  kktref_.segment(ndx_ + nu_ + cx0 + ix, ndxi));
235 
236  ix += ndxi;
237  iu += nui;
238  }
239  const boost::shared_ptr<ActionDataAbstract>& df =
240  problem_->get_terminalData();
241  const std::size_t ndxf =
242  problem_->get_terminalModel()->get_state()->get_ndx();
243  kkt_.block(ix, ix, ndxf, ndxf) = df->Lxx;
244  kktref_.segment(ix, ndxf) = df->Lx;
245  kkt_.block(0, ndx_ + nu_, ndx_ + nu_, ndx_) =
246  kkt_.block(ndx_ + nu_, 0, ndx_, ndx_ + nu_).transpose();
247  return cost_;
248 }
249 
250 void SolverKKT::computePrimalDual() {
251  primaldual_ = kkt_.lu().solve(-kktref_);
252  primal_ = primaldual_.segment(0, ndx_ + nu_);
253  dual_ = primaldual_.segment(ndx_ + nu_, ndx_);
254 }
255 
256 void SolverKKT::increaseRegularization() {
257  preg_ *= reg_incfactor_;
258  if (preg_ > reg_max_) {
259  preg_ = reg_max_;
260  }
261  dreg_ = preg_;
262 }
263 
264 void SolverKKT::decreaseRegularization() {
265  preg_ /= reg_decfactor_;
266  if (preg_ < reg_min_) {
267  preg_ = reg_min_;
268  }
269  dreg_ = preg_;
270 }
271 
272 void SolverKKT::allocateData() {
273  const std::size_t T = problem_->get_T();
274  dxs_.resize(T + 1);
275  dus_.resize(T);
276  lambdas_.resize(T + 1);
277  xs_try_.resize(T + 1);
278  us_try_.resize(T);
279 
280  nx_ = 0;
281  ndx_ = 0;
282  nu_ = 0;
283  const std::size_t nx = problem_->get_nx();
284  const std::size_t ndx = problem_->get_ndx();
285  const std::vector<boost::shared_ptr<ActionModelAbstract> >& models =
286  problem_->get_runningModels();
287  for (std::size_t t = 0; t < T; ++t) {
288  const boost::shared_ptr<ActionModelAbstract>& model = models[t];
289  if (t == 0) {
290  xs_try_[t] = problem_->get_x0();
291  } else {
292  xs_try_[t] = Eigen::VectorXd::Constant(nx, NAN);
293  }
294  const std::size_t nu = model->get_nu();
295  us_try_[t] = Eigen::VectorXd::Constant(nu, NAN);
296  dxs_[t] = Eigen::VectorXd::Zero(ndx);
297  dus_[t] = Eigen::VectorXd::Zero(nu);
298  lambdas_[t] = Eigen::VectorXd::Zero(ndx);
299  nx_ += nx;
300  ndx_ += ndx;
301  nu_ += nu;
302  }
303  nx_ += nx;
304  ndx_ += ndx;
305  xs_try_.back() = problem_->get_terminalModel()->get_state()->zero();
306  dxs_.back() = Eigen::VectorXd::Zero(ndx);
307  lambdas_.back() = Eigen::VectorXd::Zero(ndx);
308 
309  // Set dimensions for kkt matrix and kkt_ref vector
310  kkt_.resize(2 * ndx_ + nu_, 2 * ndx_ + nu_);
311  kkt_.setZero();
312  kktref_.resize(2 * ndx_ + nu_);
313  kktref_.setZero();
314  primaldual_.resize(2 * ndx_ + nu_);
315  primaldual_.setZero();
316  primal_.resize(ndx_ + nu_);
317  primal_.setZero();
318  kkt_primal_.resize(ndx_ + nu_);
319  kkt_primal_.setZero();
320  dual_.resize(ndx_);
321  dual_.setZero();
322  dF.resize(ndx_ + nu_);
323  dF.setZero();
324 }
325 
326 } // namespace crocoddyl
Abstract class for solver callbacks.