- 3.0.2 optimal control module.
Nlp.h
Go to the documentation of this file.
1 /**********************************************************************************************************************
2 This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich
3 Licensed under the BSD-2 license (see LICENSE file in main directory)
4 **********************************************************************************************************************/
5 
6 
7 #pragma once
8 
9 #include <Eigen/Sparse>
14 
15 namespace ct {
16 namespace optcon {
17 namespace tpl {
18 
19 
31 template <typename SCALAR>
32 class Nlp
33 {
34 public:
35  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
36 
37  using VectorXs = Eigen::Matrix<SCALAR, Eigen::Dynamic, 1>;
38  using VectorXi = Eigen::Matrix<int, Eigen::Dynamic, 1>;
39  using MapVecXs = Eigen::Map<VectorXs>;
40  using MapVecXi = Eigen::Map<VectorXi>;
41  using MapConstVecXs = Eigen::Map<const VectorXs>;
42 
46  Nlp() = default;
50  virtual ~Nlp() = default;
56  virtual void updateProblem() = 0;
57 
64  {
65  if (!costEvaluator_)
66  throw std::runtime_error("Error in evaluateCostFun. Costevaluator not initialized");
67 
68  return costEvaluator_->eval();
69  }
70 
71 
78  void evaluateCostGradient(const size_t n, MapVecXs& grad)
79  {
80  if (!costEvaluator_)
81  throw std::runtime_error("Error in evaluateCostGradient. Costevaluator not initialized");
82 
83  costEvaluator_->evalGradient(n, grad);
84  }
85 
93  {
94  if (!constraints_)
95  throw std::runtime_error("Error in evaluateConstraints. Constraints not initialized");
96 
97  constraints_->evalConstraints(values);
98  }
99 
106  void evaluateConstraintJacobian(const int nele_jac, MapVecXs& jac)
107  {
108  if (!constraints_)
109  throw std::runtime_error("Error in evaluateConstraintJacobian. Constraints not initialized");
110 
111  constraints_->evalSparseJacobian(jac, nele_jac);
112  }
113 
124  void evaluateHessian(const int nele_hes, MapVecXs& hes, const SCALAR obj_fac, MapConstVecXs& lambda)
125  {
126 #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
127 
128  // store objective-factor in matrix to ensure compatibility with other derivative-classes
129  Eigen::Matrix<double, 1, 1> omega;
130  omega << obj_fac;
131 
132  // evaluate Hessian values
133  Eigen::VectorXd hessianCostValues, hessianConstraintsValues;
134 
135  costEvaluator_->sparseHessianValues(optVariables_->getOptimizationVars(), omega, hessianCostValues);
136 
137  hessianConstraintsValues = constraints_->sparseHessianValues(optVariables_->getOptimizationVars(), lambda);
138 
139 
140  // collect all Hessian values (from both constraints and cost) in one vector of triplets
141  // here, we re-use the the sparsity information collected in getNonZeroHessianCount()
142  std::vector<Eigen::Triplet<SCALAR>, Eigen::aligned_allocator<Eigen::Triplet<SCALAR>>> triplets;
143  triplets.reserve(iRowHessianCost_.rows() + iRowHessianConstraints_.rows());
144 
145  for (int i = 0; i < iRowHessianCost_.rows(); i++)
146  {
147  triplets.push_back(Eigen::Triplet<SCALAR>(iRowHessianCost_(i), jColHessianCost_(i), hessianCostValues(i)));
148  }
149  for (int i = 0; i < iRowHessianConstraints_.rows(); i++)
150  {
151  triplets.push_back(Eigen::Triplet<SCALAR>(
152  iRowHessianConstraints_(i), jColHessianConstraints_(i), hessianConstraintsValues(i)));
153  }
154 
155  // setting the sparse Hessian from Eigen-triplets automatically resolves (e.g. adds) entries at same indices
156  Hessian_eval_.setFromTriplets(triplets.begin(), triplets.end());
157 
158  // IPOPT and SNOPT only expect a triangular Hessian matrix (symmetry)
159  // enforce triangular view
160  // todo: is there a nicer in-place conversion to triangularView?
161  Hessian_eval_ = Hessian_eval_.template triangularView<Eigen::Lower>();
162 
163  hes = Eigen::Map<Eigen::VectorXd>(Hessian_eval_.valuePtr(), nele_hes, 1);
164 
165 #else
166  throw std::runtime_error(
167  "Nlp::evaluateHessian() only available for Eigen 3.3 and newer. Please change solver settings to NOT use "
168  "exact Hessians");
169 #endif
170  }
171 
182  void getSparsityPatternJacobian(const int nele_jac, MapVecXi& iRow, MapVecXi& jCol) const
183  {
184  if (!constraints_)
185  throw std::runtime_error("Error in getSparsityPatternJacobian. Constraints not initialized");
186 
187  iRow.setZero();
188  jCol.setZero();
189 
190  constraints_->getSparsityPattern(iRow, jCol, nele_jac);
191  }
192 
200  void getSparsityPatternHessian(const int nele_hes, MapVecXi& iRow, MapVecXi& jCol) const
201  {
202  // todo this implicitly assumes that getNonZeroHessianCount() got called before. Assert this!
203  iRow = iRowHessian_;
204  jCol = jColHessian_;
205  }
206 
212  size_t getConstraintsCount() const
213  {
214  if (!constraints_)
215  throw std::runtime_error("Error in getConstraintsCount. Constraints not initialized");
216 
217  return constraints_->getConstraintsCount();
218  }
219 
227  size_t getNonZeroJacobianCount() const
228  {
229  if (!constraints_)
230  throw std::runtime_error("Error in getNonZeroJacobianCount. Constraints not initialized");
231 
232  return constraints_->getNonZerosJacobianCount();
233  }
234 
243  {
244 #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
245 
246  // the sparse Eigen-matrices need to be resized properly, which happens in this step.
247  // todo: need to assert that getNonZeroHessianCount() gets called before the first call to a Hessian evaluation.
248  Hessian_eval_.resize(optVariables_->size(), optVariables_->size());
249  Hessian_sparsity_.resize(optVariables_->size(), optVariables_->size());
250 
251  iRowHessianCost_.setZero();
252  jColHessianCost_.setZero();
253  iRowHessianConstraints_.setZero();
254  jColHessianConstraints_.setZero();
255 
256 
257  // evaluate sparsity patterns
258  constraints_->getSparsityPatternHessian(
260 
261  costEvaluator_->getSparsityPatternHessian(iRowHessianCost_, jColHessianCost_);
262 
263 
264  // collect all Hessian sparsity values (from both constraints and cost) in one vector of triplets
265  std::vector<Eigen::Triplet<SCALAR>, Eigen::aligned_allocator<Eigen::Triplet<SCALAR>>> triplets;
266  triplets.reserve(iRowHessianCost_.rows() + iRowHessianConstraints_.rows());
267 
268  for (int i = 0; i < iRowHessianCost_.rows(); ++i)
269  triplets.push_back(Eigen::Triplet<SCALAR>(iRowHessianCost_(i), jColHessianCost_(i), SCALAR(1.0)));
270 
271  for (int i = 0; i < iRowHessianConstraints_.rows(); ++i)
272  triplets.push_back(
273  Eigen::Triplet<SCALAR>(iRowHessianConstraints_(i), jColHessianConstraints_(i), SCALAR(1.0)));
274 
275  // setting the sparse Hessian from Eigen-triplets automatically resolves (e.g. adds) entries at same indices
276  Hessian_sparsity_.setFromTriplets(triplets.begin(), triplets.end());
277 
278  // IPOPT and SNOPT only expect a triangular Hessian matrix (symmetry)
279  // enforce triangular view
280  // todo: is there a nicer in-place conversion to triangularView?
281  Hessian_sparsity_ = Hessian_sparsity_.template triangularView<Eigen::Lower>();
282 
283  // lastly, collect and combine the sparsity as filled into the helper-matrix and store in Eigen::Vectors.
284  std::vector<int> iRowHessianStdVec;
285  std::vector<int> jColHessianStdVec;
286  for (int k = 0; k < Hessian_sparsity_.outerSize(); ++k)
287  {
288  for (typename Eigen::SparseMatrix<SCALAR>::InnerIterator it(Hessian_sparsity_, k); it; ++it)
289  {
290  iRowHessianStdVec.push_back(it.row());
291  jColHessianStdVec.push_back(it.col());
292  }
293  }
294  iRowHessian_ = Eigen::Map<Eigen::VectorXi>(iRowHessianStdVec.data(), iRowHessianStdVec.size(), 1);
295  jColHessian_ = Eigen::Map<Eigen::VectorXi>(jColHessianStdVec.data(), jColHessianStdVec.size(), 1);
296 
297  // the number of non-zero elements is equal to the number rows
298  size_t nonZerosHessian = iRowHessian_.rows();
299  return nonZerosHessian;
300 #else
301  throw std::runtime_error(
302  "Nlp::getNonZeroHessianCount() only available for Eigen 3.3 and newer. Please change solver settings to "
303  "NOT use "
304  "exact Hessians or upgrade Eigen version.");
305  return 0;
306 #endif
307  }
308 
316  void getConstraintBounds(MapVecXs& lowerBound, MapVecXs& upperBound, const size_t m) const
317  {
318  if (!constraints_)
319  throw std::runtime_error("Error in getConstraintBounds. Constraints not initialized");
320 
321  constraints_->getBounds(lowerBound, upperBound);
322  }
323 
329  size_t getVarCount() const
330  {
331  if (!optVariables_)
332  throw std::runtime_error("Error in getVarCount. Optvariables not initialized");
333 
334  return optVariables_->size();
335  }
336 
344  void getVariableBounds(MapVecXs& lowerBound, MapVecXs& upperBound, const size_t n) const
345  {
346  if (!optVariables_)
347  throw std::runtime_error("Error in getVariableBounds. Optvariables not initialized");
348 
349  optVariables_->getLowerBounds(lowerBound);
350  optVariables_->getUpperBounds(upperBound);
351  }
352 
360  void extractOptimizationVars(const MapConstVecXs& x, bool isNew)
361  {
362  if (!optVariables_)
363  throw std::runtime_error("Error in extractOptimizationVars. Optvariables not initialized");
364 
365  if (isNew)
366  {
367  optVariables_->setOptimizationVars(x);
368  updateProblem();
369  }
370  }
371 
378  void getInitialGuess(const size_t n, MapVecXs& x) const
379  {
380  if (!optVariables_)
381  throw std::runtime_error("Error in getOptimizationVars. Optvariables not initialized");
382 
383  optVariables_->getInitialGuess(n, x);
384  }
385 
395  void getOptimizationMultState(const size_t n, MapVecXs& xMul, MapVecXi& xState) const
396  {
397  if (!optVariables_)
398  throw std::runtime_error("Error in getOptimizationMultState. Optvariables not initialized");
399 
400  optVariables_->getOptimizationMultState(n, xMul, xState);
401  }
402 
411  void getConstraintsMultState(const size_t m, MapVecXs& zMul, MapVecXi& zState) const
412  {
413  if (!optVariables_)
414  throw std::runtime_error("Error in getConstraintsMultState. Optvariables not initialized");
415 
416  optVariables_->getConstraintsMultState(m, zMul, zState);
417  }
418 
426  void getBoundMultipliers(size_t n, MapVecXs& zLow, MapVecXs& zUp) const
427  {
428  if (!optVariables_)
429  throw std::runtime_error("Error in getBoundMultipliers. Optvariables not initialized");
430 
431  optVariables_->getBoundMultipliers(n, zLow, zUp);
432  }
433 
440  void getLambdaVars(size_t m, MapVecXs& lambda) const
441  {
442  if (!optVariables_)
443  throw std::runtime_error("Error in getLambdaVars. Optvariables not initialized");
444 
445  optVariables_->getLambdaVars(m, lambda);
446  }
447 
457  const MapConstVecXs& zL,
458  const MapConstVecXs& zU,
459  const MapConstVecXs& lambda)
460  {
461  if (!optVariables_)
462  throw std::runtime_error("Error in extractIpoptSolution. Optvariables not initialized");
463 
464  optVariables_->setNewIpoptSolution(x, zL, zU, lambda);
465  }
466 
477  const MapVecXs& xMul,
478  const MapVecXi& xState,
479  const MapVecXs& fMul,
480  const MapVecXi& fState)
481  {
482  if (!optVariables_)
483  throw std::runtime_error("Error in extractSnoptSolution. Optvariables not initialized");
484 
485  optVariables_->setNewSnoptSolution(x, xMul, xState, fMul, fState);
486  }
487 
488 
489 protected:
491  std::shared_ptr<DiscreteCostEvaluatorBase<SCALAR>> costEvaluator_;
492 
494  std::shared_ptr<OptVector<SCALAR>> optVariables_;
495 
497  std::shared_ptr<DiscreteConstraintContainerBase<SCALAR>> constraints_;
498 
499 #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
500  Eigen::SparseMatrix<SCALAR> Hessian_eval_;
501  Eigen::SparseMatrix<SCALAR> Hessian_sparsity_; // this is just a helper data structure
502 #endif
503 
506 
508  Eigen::VectorXi iRowHessian_, jColHessian_;
509 };
510 } // namespace tpl
511 
513 
514 } // namespace optcon
515 } // namespace ct
void getVariableBounds(MapVecXs &lowerBound, MapVecXs &upperBound, const size_t n) const
Reads the bounds on the Optimization optimization variables.
Definition: Nlp.h:344
Eigen::VectorXi jColHessianConstraints_
Definition: Nlp.h:505
void getConstraintsMultState(const size_t m, MapVecXs &zMul, MapVecXi &zState) const
Gets the constraint multiplier and state, used in the NLP solver SNOPT.
Definition: Nlp.h:411
void getConstraintBounds(MapVecXs &lowerBound, MapVecXs &upperBound, const size_t m) const
Reads the bounds of the constraints.
Definition: Nlp.h:316
void evaluateHessian(const int nele_hes, MapVecXs &hes, const SCALAR obj_fac, MapConstVecXs &lambda)
Evaluates the hessian of the lagrangian.
Definition: Nlp.h:124
void extractSnoptSolution(const MapVecXs &x, const MapVecXs &xMul, const MapVecXi &xState, const MapVecXs &fMul, const MapVecXi &fState)
{ Extracts the solution values from SNOPT }
Definition: Nlp.h:476
The NLP base class. This class serves as abstract base class to use as an interface to the NLP solver...
Definition: Nlp.h:32
void evaluateConstraints(MapVecXs &values)
{ Evaluates the constraints }
Definition: Nlp.h:92
size_t getConstraintsCount() const
Returns the number of constraints in the NLP.
Definition: Nlp.h:212
void extractIpoptSolution(const MapConstVecXs &x, const MapConstVecXs &zL, const MapConstVecXs &zU, const MapConstVecXs &lambda)
{ Extracts the solution values from IPOPT }
Definition: Nlp.h:456
Eigen::Matrix< SCALAR, Eigen::Dynamic, 1 > VectorXs
Definition: Nlp.h:37
void getSparsityPatternHessian(const int nele_hes, MapVecXi &iRow, MapVecXi &jCol) const
Gets the sparsity pattern of the Hessian of the Lagrangian.
Definition: Nlp.h:200
void getBoundMultipliers(size_t n, MapVecXs &zLow, MapVecXs &zUp) const
Gets the bound multipliers used in the NLP solver IPOPT.
Definition: Nlp.h:426
virtual ~Nlp()=default
Destructor.
void getLambdaVars(size_t m, MapVecXs &lambda) const
Gets the values of the constraint multipliers.
Definition: Nlp.h:440
std::shared_ptr< DiscreteCostEvaluatorBase< SCALAR > > costEvaluator_
Ptr to cost evaluator, which approximates the cost evaluation for the discrete problem.
Definition: Nlp.h:491
size_t getVarCount() const
Returns the number of Optimization optimization variables.
Definition: Nlp.h:329
Eigen::VectorXi jColHessianCost_
Definition: Nlp.h:505
std::shared_ptr< OptVector< SCALAR > > optVariables_
Ptr to optimization variable container, which holds the optimization variables used in the NLP solver...
Definition: Nlp.h:494
void extractOptimizationVars(const MapConstVecXs &x, bool isNew)
{Extracts the Optimization optimization variables from the nlp solvers between nlp iterations} ...
Definition: Nlp.h:360
CppAD::AD< CppAD::cg::CG< double > > SCALAR
void evaluateCostGradient(const size_t n, MapVecXs &grad)
{ Evaluates the gradient of the costfunction}
Definition: Nlp.h:78
virtual void updateProblem()=0
{ This method gets called at each update of the Optimization variables. This can be used to distribut...
for i
Definition: mpc_unittest_plotting.m:14
void getSparsityPatternJacobian(const int nele_jac, MapVecXi &iRow, MapVecXi &jCol) const
Gets the sparsity pattern.
Definition: Nlp.h:182
size_t getNonZeroJacobianCount() const
Returns the number of the non zero elements of the constraint jacobian.
Definition: Nlp.h:227
size_t getNonZeroHessianCount()
Returns the number of non zeros in the Hessian.
Definition: Nlp.h:242
Eigen::VectorXi iRowHessianCost_
helper containers for calculating the sparsity patterns
Definition: Nlp.h:505
ct::core::StateVector< state_dim > x
Definition: LoadFromFileTest.cpp:20
Eigen::VectorXi jColHessian_
Definition: Nlp.h:508
void getOptimizationMultState(const size_t n, MapVecXs &xMul, MapVecXi &xState) const
Gets the variable multiplier and the variable state, used in the NLP solver SNOPT. See the snopt documentation for further explanations.
Definition: Nlp.h:395
Nlp()=default
Default constructor.
Eigen::Map< const VectorXs > MapConstVecXs
Definition: Nlp.h:41
void evaluateConstraintJacobian(const int nele_jac, MapVecXs &jac)
{ Evaluates the constraint jacobian }
Definition: Nlp.h:106
Eigen::Map< VectorXi > MapVecXi
Definition: Nlp.h:40
std::shared_ptr< DiscreteConstraintContainerBase< SCALAR > > constraints_
Ptr to constraint container, which contains the discretized constraints for the problem.
Definition: Nlp.h:497
Eigen::VectorXi iRowHessianConstraints_
Definition: Nlp.h:505
Eigen::Map< VectorXs > MapVecXs
Definition: Nlp.h:39
Eigen::Matrix< int, Eigen::Dynamic, 1 > VectorXi
Definition: Nlp.h:38
void getInitialGuess(const size_t n, MapVecXs &x) const
Gets the Optimization variables.
Definition: Nlp.h:378
SCALAR evaluateCostFun()
{ Evaluates the costfunction at the current nlp iteration }
Definition: Nlp.h:63
Eigen::VectorXi iRowHessian_
combined Hessian sparsity pattern gets stored here
Definition: Nlp.h:508