humoto
solver.h
Go to the documentation of this file.
1 /**
2  @file
3  @author Alexander Sherikov
4  @author Jan Michalczyk
5  @copyright 2014-2017 INRIA. Licensed under the Apache License, Version 2.0.
6  (see @ref LICENSE or http://www.apache.org/licenses/LICENSE-2.0)
7 
8  @brief
9 */
10 
11 #pragma once
12 
13 
14 namespace humoto
15 {
16  namespace qpoases
17  {
18  /**
19  * @brief Parameters of the solver
20  */
22  {
23  #define HUMOTO_CONFIG_SECTION_ID "SolverParameters"
24  #define HUMOTO_CONFIG_CONSTRUCTOR SolverParameters
25  #define HUMOTO_CONFIG_ENTRIES \
26  HUMOTO_CONFIG_PARENT_CLASS(humoto::SolverParametersBase) \
27  \
28  HUMOTO_CONFIG_SCALAR_(max_number_of_iterations) \
29  HUMOTO_CONFIG_SCALAR_(max_cpu_time) \
30  \
31  HUMOTO_CONFIG_MEMBER_CLASS(options_, "QPoasesParameters")
32  #include HUMOTO_CONFIG_DEFINE_ACCESSORS
33 
34 
35  private:
36  class HUMOTO_LOCAL QPoasesParameters : public qpOASES::Options, public humoto::config::ConfigurableBase
37  {
38  #define HUMOTO_CONFIG_SECTION_ID "QPoasesParameters"
39  #define HUMOTO_CONFIG_CONSTRUCTOR QPoasesParameters
40  #define HUMOTO_CONFIG_ENTRIES \
41  HUMOTO_CONFIG_ENUM(enableRamping)\
42  HUMOTO_CONFIG_ENUM(enableFarBounds)\
43  HUMOTO_CONFIG_ENUM(enableFlippingBounds)\
44  HUMOTO_CONFIG_ENUM(enableRegularisation)\
45  HUMOTO_CONFIG_ENUM(enableFullLITests)\
46  HUMOTO_CONFIG_ENUM(enableNZCTests)\
47  HUMOTO_CONFIG_SCALAR(enableDriftCorrection)\
48  HUMOTO_CONFIG_SCALAR(enableCholeskyRefactorisation)\
49  HUMOTO_CONFIG_ENUM(enableEqualities)\
50  HUMOTO_CONFIG_SCALAR(terminationTolerance)\
51  HUMOTO_CONFIG_SCALAR(boundTolerance)\
52  HUMOTO_CONFIG_SCALAR(boundRelaxation)\
53  HUMOTO_CONFIG_SCALAR(epsNum)\
54  HUMOTO_CONFIG_SCALAR(epsDen)\
55  HUMOTO_CONFIG_SCALAR(maxPrimalJump)\
56  HUMOTO_CONFIG_SCALAR(maxDualJump)\
57  HUMOTO_CONFIG_SCALAR(initialRamping)\
58  HUMOTO_CONFIG_SCALAR(finalRamping)\
59  HUMOTO_CONFIG_SCALAR(initialFarBounds)\
60  HUMOTO_CONFIG_SCALAR(growFarBounds)\
61  HUMOTO_CONFIG_ENUM(initialStatusBounds)\
62  HUMOTO_CONFIG_SCALAR(epsFlipping)\
63  HUMOTO_CONFIG_SCALAR(numRegularisationSteps)\
64  HUMOTO_CONFIG_SCALAR(epsRegularisation)\
65  HUMOTO_CONFIG_SCALAR(numRefinementSteps)\
66  HUMOTO_CONFIG_SCALAR(epsIterRef)\
67  HUMOTO_CONFIG_SCALAR(epsLITests)\
68  HUMOTO_CONFIG_SCALAR(epsNZCTests)\
69  HUMOTO_CONFIG_SCALAR(rcondSMin)\
70  HUMOTO_CONFIG_ENUM(enableInertiaCorrection)\
71  HUMOTO_CONFIG_ENUM(enableDropInfeasibles)\
72  HUMOTO_CONFIG_SCALAR(dropBoundPriority)\
73  HUMOTO_CONFIG_SCALAR(dropEqConPriority)\
74  HUMOTO_CONFIG_SCALAR(dropIneqConPriority)
75  #include HUMOTO_CONFIG_DEFINE_ACCESSORS
76 
77  public:
79  {
80  setDefaults();
81  }
82 
83  /**
84  * @brief Initialize to default parameters
85  */
86  void setDefaults()
87  {
88  setToMPC ();
89  }
90  };
91 
92 
93  public:
94  /// Parameters for qpOASES
96 
97  /// Limit on the number of iterations
99 
100  /// Limit on the computation time
102 
103 
104  public:
105  void setDefaults()
106  {
107  max_number_of_iterations_ = 1000;
108  max_cpu_time_ = 0.0;
109  }
110 
111 
112  /**
113  * @brief Default constructor
114  */
116  {
117  setDefaults();
118  }
119  };
120 
121 
122 
123  /**
124  * @brief QP solver.
125  *
126  * Solves hierarchy as an optimization problem of the following form:
127  * minimize 1/2* x' H x + x' g
128  * subject to lb <= x <= ub
129  * lbA <= A x <= ubA
130  * where 'x' is solution, 'H' -- Hessian matrix, 'g' -- gradient
131  * vector, 'lb' and 'ub' vectors of simple lower and upper bounds, 'Ax'
132  * -- matrix of general constraints with bounds 'lbA' and 'ubA'.
133  */
134  class HUMOTO_LOCAL Solver : public humoto::QPSolverMixin< humoto::SolverGuessSolutionActiveSet<SolverParameters> >
135  {
136  private:
137  /// If set the active set is returned
139 
140 
142 
143 
144  /// Guess of the solution
145  const double * solution_guess_;
146 
147 
148  ///@{
149  /// The active set guess for qpOASES
152  ///@}
153 
154  Eigen::VectorXd qpoases_active_set_bounds_;
156 
157 
158  /// Nonzero if constraints are specified
160 
161  qpOASES::QProblem * qp_;
162 
163 
164  private:
165  /// @copydoc humoto::Solver::initialize
166  void initialize( const humoto::OptimizationProblem &hierarchy,
167  const humoto::SolutionStructure &sol_structure)
168  {
169  reset();
170 
171  hierarchy.getQPProblem(qp_problem_, sol_structure);
172 
173  number_of_constraints_ = qp_problem_.getGeneralConstraints().getNumberOfConstraints();
174 
175  qp_ = new qpOASES::QProblem(sol_structure.getNumberOfVariables(),
176  number_of_constraints_,
177  qpOASES::HST_SEMIDEF);
178  }
179 
180 
181  /// @copydoc humoto::Solver::solveHierarchy
183  const humoto::OptimizationProblem &hierarchy)
184  {
185  qpOASES::real_t max_time = parameters_.max_cpu_time_;
186 
187  qpOASES::real_t *max_time_ptr = NULL;
188 
189  const qpOASES::real_t * lb_ptr = NULL;
190  const qpOASES::real_t * ub_ptr = NULL;
191 
192  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> A_rm;
193  const qpOASES::real_t *A_ptr = NULL;
194  const qpOASES::real_t *lbA_ptr = NULL;
195  const qpOASES::real_t *ubA_ptr = NULL;
196 
197  const qpOASES::real_t *solution_guess_ptr = NULL;
198 
199  qpOASES::Bounds *active_set_bounds_ptr = NULL;
200  qpOASES::Constraints *active_set_constraints_ptr = NULL;
201 
202  qpOASES::returnValue qpoases_return_value;
203 
204 
205  if (max_time != 0.0)
206  {
207  max_time_ptr = &max_time;
208  }
209 
210  if (qp_problem_.getSimpleBounds().getNumberOfConstraints() > 0)
211  {
212  lb_ptr = qp_problem_.getSolutionLowerBounds().data();
213  ub_ptr = qp_problem_.getSolutionUpperBounds().data();
214  }
215 
216  if (number_of_constraints_ > 0)
217  {
218  // Note: qpOASES expects rowMajor data, Eigen is colMajor by default
219  // only A needs to be handled since H is symmetric and the rest
220  // are vectors
221  A_rm = qp_problem_.getGeneralConstraints().getA();
222  A_ptr = A_rm.data();
223 
224  lbA_ptr = qp_problem_.getGeneralConstraints().getLowerBounds().data();
225  ubA_ptr = qp_problem_.getGeneralConstraints().getUpperBounds().data();
226  }
227 
228  if (NULL != solution_guess_)
229  {
230  solution_guess_ptr = solution_guess_;
231  }
232 
233 
234  if (active_set_guess_provided_)
235  {
236  active_set_bounds_ptr = &qpoases_active_set_guess_bounds_;
237  active_set_constraints_ptr = &qpoases_active_set_guess_constraints_;
238  }
239 
240 
241  int number_of_iterations = parameters_.max_number_of_iterations_;
242 
243  qp_->setOptions (parameters_.options_);
244  qpoases_return_value =
245  qp_->init( qp_problem_.getHessian().data(),
246  qp_problem_.getGradient().data(),
247  A_ptr,
248  lb_ptr,
249  ub_ptr,
250  lbA_ptr,
251  ubA_ptr,
252  number_of_iterations,
253  max_time_ptr,
254  solution_guess_ptr,
255  NULL, // Optimal dual solution vector
256  active_set_bounds_ptr,
257  active_set_constraints_ptr);
258 
259 
260  qp_->getPrimalSolution( solution.x_.data() );
261 
262 
263 
264  switch (qpoases_return_value)
265  {
266  case qpOASES::SUCCESSFUL_RETURN:
267  solution.return_status_ = SolverStatus::OK;
268  break;
269  case qpOASES::RET_MAX_NWSR_REACHED:
271  break;
272  default:
274  break;
275  }
276 
277  try
278  {
279  humoto::qpoases::Solution &qpoases_solution = dynamic_cast<humoto::qpoases::Solution &> (solution);
280 
281  qpoases_solution.qpoases_return_value_ = qpoases_return_value;
282  qpoases_solution.number_of_iterations_ = number_of_iterations;
283  }
284  catch(...)
285  {
286  // not critical
287  }
288 
289  solution_guess_ = NULL;
290  active_set_guess_provided_ = false;
291  }
292 
293 
294 
295  /// @copydoc humoto::SolverGuessActiveSetMixin::getActiveSet
296  void getActiveSet( humoto::ActiveSet &active_set,
297  const humoto::OptimizationProblem &hierarchy)
298  {
299  if (qp_problem_.getSimpleBounds().getNumberOfConstraints() > 0)
300  {
301  qpoases_active_set_bounds_.resize(qp_problem_.getSimpleBounds().getNumberOfVariables());
302  qp_->getWorkingSetBounds(qpoases_active_set_bounds_.data());
303  }
304 
305  if (number_of_constraints_ > 0)
306  {
307  qpoases_active_set_constraints_.resize(number_of_constraints_);
308  qp_->getWorkingSetConstraints(qpoases_active_set_constraints_.data());
309  }
310 
311 
312  hierarchy.initializeActiveSet(active_set);
313 
314  // Objective level includes only equalities by design.
315  if (0 == number_of_constraints_ + qp_problem_.getSimpleBounds().getNumberOfConstraints())
316  {
317  // no constraints -- objective on the first level
319  }
320  else
321  {
323  }
324 
325 
326  for (std::size_t i = 0; i < number_of_constraints_; ++i)
327  {
328  if (qpoases_active_set_constraints_[i] == -1.0)
329  {
330  active_set[0][i] = ConstraintActivationType::LOWER_BOUND;
331  }
332  else
333  {
334  if (qpoases_active_set_constraints_[i] == 0.0)
335  {
336  active_set[0][i] = ConstraintActivationType::INACTIVE;
337  }
338  else
339  {
340  if (qpoases_active_set_constraints_[i] == 1.0)
341  {
342  active_set[0][i] = ConstraintActivationType::UPPER_BOUND;
343  }
344  else
345  {
346  HUMOTO_THROW_MSG("Active set returned by qpOASES is incorrectly initialized.");
347  }
348  }
349  }
350  }
351 
352 
353  for (std::size_t i = 0; i < qp_problem_.getSimpleBounds().getNumberOfConstraints(); ++i)
354  {
355  std::size_t var_index = qp_problem_.getSimpleBounds().getIndices()[i];
356 
357  if (qpoases_active_set_bounds_[var_index] == -1.0)
358  {
359  if (qp_problem_.getSimpleBounds().getLowerBounds()[i] < qp_problem_.getSolutionLowerBounds()[var_index])
360  {
361  active_set[0][number_of_constraints_ + i] = ConstraintActivationType::INACTIVE;
362  }
363  else
364  {
365  active_set[0][number_of_constraints_ + i] = ConstraintActivationType::LOWER_BOUND;
366  }
367  }
368  else
369  {
370  if (qpoases_active_set_bounds_[var_index] == 0.0)
371  {
372  active_set[0][number_of_constraints_ + i] = ConstraintActivationType::INACTIVE;
373  }
374  else
375  {
376  if (qpoases_active_set_bounds_[var_index] == 1.0)
377  {
378  if (qp_problem_.getSimpleBounds().getUpperBounds()[i] > qp_problem_.getSolutionUpperBounds()[var_index])
379  {
380  active_set[0][number_of_constraints_ + i] = ConstraintActivationType::INACTIVE;
381  }
382  else
383  {
384  active_set[0][number_of_constraints_ + i] = ConstraintActivationType::UPPER_BOUND;
385  }
386  }
387  else
388  {
389  HUMOTO_THROW_MSG("Active set returned by qpOASES is incorrectly initialized.");
390  }
391  }
392  }
393  }
394  }
395 
396 
397  /**
398  * @copydoc humoto::SolverGuessActiveSetMixin::setActiveSet
399  *
400  * @attention One variable may be simply bounded multiple
401  * times. In this case, the guess is set to the guess for the
402  * last bound on the level.
403  */
404  void setActiveSet( const humoto::ActiveSet &active_set,
405  const humoto::OptimizationProblem &hierarchy)
406  {
407  active_set_guess_provided_ = true;
408 
409  qpoases_active_set_guess_constraints_.init(number_of_constraints_);
410 
411  for (std::size_t i = 0; i < number_of_constraints_; ++i)
412  {
413  ConstraintActivationType::Type type = active_set[0][i];
414 
415  switch (type)
416  {
418  qpoases_active_set_guess_constraints_.setupConstraint(i, qpOASES::ST_LOWER);
419  break;
421  qpoases_active_set_guess_constraints_.setupConstraint(i, qpOASES::ST_INACTIVE);
422  break;
424  qpoases_active_set_guess_constraints_.setupConstraint(i, qpOASES::ST_UPPER);
425  break;
427  qpoases_active_set_guess_constraints_.setupConstraint(i, qpOASES::ST_LOWER);
428  break;
429  default:
430  HUMOTO_THROW_MSG("Incorrectly initialized active set guess.");
431  break;
432  }
433  }
434 
435 
436  qpoases_active_set_guess_bounds_.init(qp_problem_.getSimpleBounds().getNumberOfVariables());
437  qpoases_active_set_guess_bounds_.setupAllFree();
438  for (std::size_t i = 0; i < qp_problem_.getSimpleBounds().getNumberOfConstraints() ; ++i)
439  {
440  std::size_t var_index = qp_problem_.getSimpleBounds().getIndices()[i];
441  ConstraintActivationType::Type type = active_set[0][number_of_constraints_+i];
442 
443  switch (type)
444  {
446  qpoases_active_set_guess_bounds_.setupBound(var_index, qpOASES::ST_LOWER);
447  break;
449  // all bounds are already inactive due to setupAllFree().
450  //qpoases_active_set_guess_bounds_.setupBound(var_index, qpOASES::ST_INACTIVE);
451  break;
453  qpoases_active_set_guess_bounds_.setupBound(var_index, qpOASES::ST_UPPER);
454  break;
456  qpoases_active_set_guess_bounds_.setupBound(var_index, qpOASES::ST_LOWER);
457  break;
458  default:
459  HUMOTO_THROW_MSG("Incorrectly initialized active set guess.");
460  break;
461  }
462  }
463  }
464 
465 
466  /// @copydoc humoto::SolverGuessSolutionMixin::setSolutionGuess
467  void setSolutionGuess(const humoto::Solution & solution_guess)
468  {
469  solution_guess_ = solution_guess.x_.data();
470  }
471 
472 
473  /// @copydoc humoto::Solver::reset
474  void reset()
475  {
476  number_of_constraints_ = 0;
477 
478  active_set_guess_provided_ = false;
479 
480  solution_guess_ = NULL;
481 
482  if (qp_ != NULL)
483  {
484  delete qp_;
485  qp_ = NULL;
486  }
487  }
488 
489 
490  public:
491  /**
492  * @brief Default constructor (with default parameters)
493  */
495  {
496  qp_ = NULL;
497  reset();
498  }
499 
500 
501  /**
502  * @brief Destructor
503  */
505  {
506  reset();
507  }
508 
509 
510  /**
511  * @brief Set parameters.
512  *
513  * @param[in] parameters parameters
514  */
515  explicit Solver(const SolverParameters &parameters)
516  {
517  qp_ = NULL;
518  reset();
519  setParameters(parameters);
520  }
521 
522 
523  /**
524  * @brief Log a QP problem
525  *
526  * @param[in,out] logger logger
527  * @param[in] parent parent
528  * @param[in] name name
529  */
531  const LogEntryName &parent = LogEntryName(),
532  const std::string &name = "qpoases") const
533  {
534  LogEntryName subname = parent; subname.add(name);
535  qp_problem_.log(logger, subname);
536  }
537  };
538  }
539 }
qpOASES::Constraints qpoases_active_set_guess_constraints_
Definition: solver.h:151
std::size_t getNumberOfVariables() const
Get total number of variables in the solution vector.
Definition: solution.h:109
const humoto::constraints::ContainerALU & getGeneralConstraints() const
get container with general constraints
Definition: qp_problem.h:250
void reset()
Reset solver.
Definition: solver.h:474
void log(humoto::Logger &logger, const LogEntryName &parent=LogEntryName(), const std::string &name="qpoases") const
Log a QP problem.
Definition: solver.h:530
#define HUMOTO_LOCAL
Definition: export_import.h:26
Solution of a QP.
Definition: solution.h:21
Constraint is an equality (always active)
Definition: active_set.h:35
Eigen::VectorXd qpoases_active_set_bounds_
Definition: solver.h:154
Eigen::VectorXd x_
Definition: solution.h:186
std::size_t getNumberOfVariables() const
Returns number of variables in the task.
std::size_t getNumberOfConstraints() const
Returns number of constraints in the task.
#define HUMOTO_GLOBAL_LOGGER_IF_DEFINED
Definition: logger.h:997
Default configurable base is strict.
Definition: config.h:353
humoto::IndexVector & getIndices()
Get indices.
Analog of &#39;sol_structure&#39; struct in Octave code. [determine_solution_structure.m].
Definition: solution.h:33
Container of the solution.
Definition: solution.h:176
void set(const std::size_t level_index, const ConstraintActivationType::Type type)
Set type of all constraints on the given level.
Definition: active_set.h:319
const humoto::constraints::ContainerILU & getSimpleBounds() const
get container with simple bounds
Definition: qp_problem.h:240
#define HUMOTO_THROW_MSG(s)
HUMOTO_THROW_MSG throws an error message concatenated with the name of the function (if supported)...
Represents log entry name.
Definition: logger.h:169
int number_of_iterations_
Number of iterations made by the solver.
Definition: solution.h:25
Active set corresponding to a hierarchy of Constraints.
Definition: active_set.h:278
const Eigen::VectorXd & getSolutionLowerBounds() const
Get LB vector: &#39;LB <= X&#39;.
Definition: qp_problem.h:260
const Eigen::VectorXd & getGradient() const
Const accessor.
Definition: qp_problem.h:108
SolverStatus::Status return_status_
Definition: solution.h:187
Eigen::VectorXd & getUpperBounds()
Get upper bounds (ub vectors from &#39;A*x <= ub&#39;).
Eigen::MatrixXd & getA()
Get matrix A from general constraints: &#39;A*x = b&#39;, &#39;lb <= A*x <= ub&#39;.
SolverParameters()
Default constructor.
Definition: solver.h:115
An optimization problem [initialize_stack.m, simulation_loop.m].
Definition: hierarchy.h:20
std::size_t getNumberOfConstraints() const
Returns number of constraints in the task.
qpOASES::returnValue qpoases_return_value_
The return value of qpOASES.
Definition: solution.h:28
int max_number_of_iterations_
Limit on the number of iterations.
Definition: solver.h:98
QPoasesParameters options_
Parameters for qpOASES.
Definition: solver.h:95
double max_cpu_time_
Limit on the computation time.
Definition: solver.h:101
void setActiveSet(const humoto::ActiveSet &active_set, const humoto::OptimizationProblem &hierarchy)
Set active set guess.
Definition: solver.h:404
void setDefaults()
Initialize to default parameters.
Definition: solver.h:86
Threaded logger: any data sent to this logger is wrapped in a message and pushed to a queue...
Definition: logger.h:555
const Eigen::VectorXd & getSolutionUpperBounds() const
Get UB vector: &#39;X <= UB&#39;.
Definition: qp_problem.h:270
std::size_t number_of_constraints_
Nonzero if constraints are specified.
Definition: solver.h:159
A QP problem with constraints of a specific type.
Definition: qp_problem.h:450
void setSolutionGuess(const humoto::Solution &solution_guess)
Set solution guess.
Definition: solver.h:467
Eigen::VectorXd qpoases_active_set_constraints_
Definition: solver.h:155
~Solver()
Destructor.
Definition: solver.h:504
The root namespace of HuMoTo.
Definition: config.h:12
void initialize(const humoto::OptimizationProblem &hierarchy, const humoto::SolutionStructure &sol_structure)
Initialize solver.
Definition: solver.h:166
void initializeActiveSet(humoto::ActiveSet &active_set) const
Initialize active set of the hierarchy.
Definition: hierarchy.h:312
Parameters of the solver.
Definition: solver.h:21
qpOASES::QProblem * qp_
Definition: solver.h:161
Solver(const SolverParameters &parameters)
Set parameters.
Definition: solver.h:515
Mixin solver interface (QP solver)
Definition: solver.h:462
const double * solution_guess_
Guess of the solution.
Definition: solver.h:145
Parameters of a solver.
Definition: solver.h:17
Solver()
Default constructor (with default parameters)
Definition: solver.h:494
void solveHierarchy(humoto::Solution &solution, const humoto::OptimizationProblem &hierarchy)
Solve the hierarchy.
Definition: solver.h:182
bool active_set_guess_provided_
If set the active set is returned.
Definition: solver.h:138
LogEntryName & add(const char *name)
extends entry name with a subname
Definition: logger.h:232
qpOASES::Bounds qpoases_active_set_guess_bounds_
The active set guess for qpOASES.
Definition: solver.h:150
void getActiveSet(humoto::ActiveSet &active_set, const humoto::OptimizationProblem &hierarchy)
Get active set.
Definition: solver.h:296
Eigen::VectorXd & getLowerBounds()
Get lower bounds (lb/ub vectors from &#39;lb <= A*x <= ub&#39;).
void log(humoto::Logger &logger, const LogEntryName &parent=LogEntryName(), const std::string &name="qp_problem") const
Log a QP problem.
Definition: qp_problem.h:434
void setDefaults()
Set members to their default values.
Definition: solver.h:105
const Eigen::MatrixXd & getHessian() const
Const accessor.
Definition: qp_problem.h:98
humoto::QPProblem_ILU_ALU qp_problem_
Definition: solver.h:141
void getQPProblem(QPProblemBase< t_QPConstraints > &qp_problem, const humoto::SolutionStructure &sol_structure, const bool initialize_upper_triangular_part=true) const
Form a QP problem.
Definition: hierarchy.h:329