$darkmode
pinocchio 4.0.0
A fast and flexible implementation of Rigid Body Dynamics algorithms and their analytical derivatives
pgs-solver.hpp
1 //
2 // Copyright (c) 2022-2024 INRIA
3 //
4 
5 #pragma once
6 
7 // IWYU pragma: begin_keep
8 #include <cassert>
9 #include <cstddef>
10 #include <optional>
11 #include <limits>
12 #include <vector>
13 
14 #include <Eigen/Core>
15 
16 #include <boost/fusion/container/vector/vector.hpp>
17 
18 #include "pinocchio/macros.hpp"
19 #include "pinocchio/eigen-common.hpp"
20 
21 #include "pinocchio/utils/check.hpp"
22 #include "pinocchio/utils/reference.hpp"
23 
24 #include "pinocchio/container/eigen-storage.hpp"
25 
26 #include "pinocchio/math.hpp"
27 
28 #include "pinocchio/constraints.hpp"
29 
30 #include "pinocchio/algorithm/solvers/fwd.hpp"
31 #include "pinocchio/algorithm/solvers/constraint-solver-base.hpp"
32 #include "pinocchio/algorithm/solvers/constraint-solver-utils.hpp"
33 // IWYU pragma: end_keep
34 
35 namespace pinocchio
36 {
37  // fwd declarations for PGS-internal structs
38  // user-api structs are fwd delclared in solvers/fwd.hpp.
39  // see below for definitions
40  namespace internal
41  {
42  template<typename Scalar, int Options>
43  struct PGSSolverWorkspaceTpl;
44  }
45 
46  template<typename _Scalar, int _Options>
47  struct traits<PGSConstraintSolverTpl<_Scalar, _Options>>
48  {
49  typedef _Scalar Scalar;
50  static constexpr int Options = _Options;
51 
54  };
55 
57  template<typename _Scalar, int _Options>
58  struct PGSConstraintSolverTpl : ConstraintSolverBase<PGSConstraintSolverTpl<_Scalar, _Options>>
59  {
60  typedef _Scalar Scalar;
61  static constexpr int Options = _Options;
63  typedef ConstraintSolverBase<Self> Base;
64  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
65  typedef Eigen::Ref<const VectorXs> RefConstVectorXs;
66 
67  typedef internal::PGSSolverWorkspaceTpl<Scalar, Options> PGSSolverWorkspace;
71 
72  using Base::reset;
73  using Base::timerStart;
74  using Base::timerStop;
75 
84  explicit PGSConstraintSolverTpl(std::size_t max_problem_size = 0)
85  : Base()
86  , stats()
87  , m_workspace(max_problem_size)
88  , m_is_valid(false)
89  {
90  // we need to call reset - the solver needs to look as if it never ran
91  reset();
92  }
93 
96  bool isValid() const
97  {
98  return m_is_valid;
99  }
100 
101  template<
102  typename DelassusDerived,
103  typename VectorLike,
104  typename ConstraintModel,
105  typename ConstraintModelAllocator,
106  typename ConstraintData,
107  typename ConstraintDataAllocator>
108  bool solveImpl(
109  DelassusOperatorBase<DelassusDerived> & delassus,
110  const Eigen::MatrixBase<VectorLike> & g,
111  const std::vector<ConstraintModel, ConstraintModelAllocator> & constraint_models,
112  const std::vector<ConstraintData, ConstraintDataAllocator> & constraint_datas,
113  const PGSSolverSettings & settings,
114  PGSSolverResult & result);
115 
116  void resetImpl()
117  {
118  stats.reset();
119  m_workspace.reset();
120  m_is_valid = false;
121  }
122 
125 
126  protected:
130  PGSSolverWorkspace m_workspace;
131 
135 
136  }; // struct PGSConstraintSolverTpl
137 
138  template<typename _Scalar>
139  struct traits<PGSSolverSettingsTpl<_Scalar>>
140  {
141  typedef _Scalar Scalar;
142  };
143 
146  template<typename _Scalar>
147  struct PGSSolverSettingsTpl : ConstraintSolverSettingsBase<PGSSolverSettingsTpl<_Scalar>>
148  {
149  typedef _Scalar Scalar;
150  typedef PGSSolverSettingsTpl Self;
151  typedef ConstraintSolverSettingsBase<Self> Base;
152  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> VectorXs;
153 
156  std::size_t max_iterations = 1000,
157  Scalar absolute_feasibility_tol = Scalar(1e-6),
158  Scalar relative_feasibility_tol = Scalar(1e-6),
159  Scalar absolute_complementarity_tol = Scalar(1e-6),
160  Scalar relative_complementarity_tol = Scalar(1e-6),
161  bool solve_ncp = true,
162  bool measure_timings = false,
163  bool stat_record = false,
164  Scalar over_relaxation = Scalar(1))
165  : Base(
166  max_iterations,
167  absolute_feasibility_tol,
168  relative_feasibility_tol,
169  absolute_complementarity_tol,
170  relative_complementarity_tol,
171  solve_ncp,
172  measure_timings,
173  stat_record)
175  {
176  }
177 
178  void checkValidityImpl() const
179  {
180  PINOCCHIO_CHECK_INPUT_ARGUMENT(
181  over_relaxation < Scalar(2) && over_relaxation > Scalar(0),
182  "over_relaxation should lie in ]0,2[.");
183  }
184 
185  // ----------------------
186  // General settings
187 
189  using Base::max_iterations;
190 
192  using Base::absolute_feasibility_tol;
193 
195  using Base::relative_feasibility_tol;
196 
198  using Base::absolute_complementarity_tol;
199 
201  using Base::relative_complementarity_tol;
202 
205  using Base::solve_ncp;
206 
208  using Base::measure_timings;
209 
211  using Base::stat_record;
212 
213  // ----------------------
214  // PGS specific settings
215 
218  }; // struct PGSSolverSettingsTpl
219 
220  template<typename _Scalar, int _Options>
221  struct traits<PGSSolverResultTpl<_Scalar, _Options>>
222  {
223  typedef _Scalar Scalar;
224  static constexpr int Options = _Options;
225  };
226 
231  template<typename _Scalar, int _Options>
232  struct PGSSolverResultTpl : ConstraintSolverResultBase<PGSSolverResultTpl<_Scalar, _Options>>
233  {
234  typedef _Scalar Scalar;
235  static constexpr int Options = _Options;
236  typedef PGSSolverResultTpl Self;
237  typedef ConstraintSolverResultBase<Self> Base;
238 
239  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
240  typedef Eigen::Ref<const VectorXs> RefConstVectorXs;
241  typedef internal::EigenStorageTpl<VectorXs> VectorXsStorage;
242 
243  using Base::constraintSize;
244  using Base::setConstraintImpulseGuess;
245  using Base::setConstraintVelocityGuess;
246 
249  : Base()
250  , problem_size(0)
251  , impulse_guess(std::nullopt)
252  {
253  }
254 
257  {
258  *this = other;
259  }
260 
263  {
264  if (this != &other)
265  {
266  Base::operator=(other);
267  problem_size = other.problem_size;
268  // Since some members are maps reference on EigenStorage, we cannot simply copy them.
269  // Thus we need to explicitly say we copy the storage, and the maps will automatically point
270  // to the new storage.
271  x_storage = other.x_storage;
272  y_storage = other.y_storage;
273 
274  // special care must be taken for the optional guesses
275  if (other.impulse_guess)
276  {
277  setConstraintImpulseGuess(other.impulse_guess.value());
278  }
279  else
280  {
281  clearConstraintImpulseGuessImpl();
282  }
283  }
284  return *this;
285  }
286 
288  int constraintSizeImpl() const
289  {
290  return static_cast<int>(problem_size);
291  }
292 
293  void resetImpl()
294  {
295  impulse_guess.reset();
296 
297  // set solution to nan - solver has not run
298  x.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
299  y.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
300  }
301 
303  void resize(std::size_t problem_size_)
304  {
305  problem_size = problem_size_;
306 
307  Eigen::Index np = static_cast<Eigen::Index>(problem_size);
308  x_storage.resize(np);
309  y_storage.resize(np);
310  }
311 
313  template<typename VectorLike>
314  void retrievePrimalSolution(const Eigen::MatrixBase<VectorLike> & primal_solution_) const
315  {
316  auto & primal_solution = primal_solution_.const_cast_derived();
317  primal_solution = x;
318  }
319 
321  template<typename VectorLike>
322  void retrieveDualSolution(const Eigen::MatrixBase<VectorLike> & dual_solution_) const
323  {
324  auto & dual_solution = dual_solution_.const_cast_derived();
325  dual_solution = y;
326  }
327 
328  template<typename VectorLike>
329  void setConstraintImpulseGuessImpl(const Eigen::MatrixBase<VectorLike> & impulse_guess_in)
330  {
331  m_impulse_guess_storage.resize(impulse_guess_in.size());
332  m_impulse_guess = impulse_guess_in;
333  impulse_guess.emplace(m_impulse_guess);
334  }
335 
336  void clearConstraintImpulseGuessImpl()
337  {
338  m_impulse_guess_storage.resize(0);
339  impulse_guess.reset();
340  }
341 
342  template<typename VectorLike>
343  void setConstraintVelocityGuessImpl(const Eigen::MatrixBase<VectorLike> & /*velocity_guess_*/)
344  {
345  // do nothing, no velocity guess for PGS
346  }
347 
348  void clearConstraintVelocityGuessImpl()
349  {
350  // do nothing, no velocity guess for PGS
351  }
352 
353  template<typename VectorLike>
354  void
355  retrieveConstraintImpulsesImpl(const Eigen::MatrixBase<VectorLike> & constraint_impulses_) const
356  {
357  auto & constraint_impulses = constraint_impulses_.const_cast_derived();
358  constraint_impulses = x;
359  }
360 
364  template<typename VectorLike>
366  const Eigen::MatrixBase<VectorLike> & constraint_velocities_) const
367  {
368  auto & constraint_velocities = constraint_velocities_.const_cast_derived();
369  constraint_velocities = y;
370  }
371 
373  using Base::iterations;
374 
376  using Base::converged;
377 
379  using Base::primal_feasibility;
380 
382  using Base::dual_feasibility;
383 
385  using Base::complementarity;
386 
388  std::size_t problem_size;
389 
390  // ----------------------
391  // Solution warmstart
392 
394  std::optional<RefConstVectorXs> impulse_guess;
395 
396  // ----------------------
397  // Solution - output of the solver
398 
402  VectorXsStorage x_storage;
403  typename VectorXsStorage::RefMapType x = x_storage.map();
404 
406  VectorXsStorage y_storage;
407  typename VectorXsStorage::RefMapType y = y_storage.map();
408 
410  VectorXsStorage m_impulse_guess_storage;
411  typename VectorXsStorage::RefMapType m_impulse_guess = m_impulse_guess_storage.map();
412  }; // struct PGSSolverResultTpl
413 
414  template<typename _Scalar>
415  struct traits<PGSSolverStatsTpl<_Scalar>>
416  {
417  typedef _Scalar Scalar;
418  };
419 
422  template<typename _Scalar>
423  struct PGSSolverStatsTpl : ConstraintSolverStatsBase<PGSSolverStatsTpl<_Scalar>>
424  {
425  typedef _Scalar Scalar;
426  typedef PGSSolverStatsTpl Self;
427  typedef ConstraintSolverStatsBase<Self> Base;
428 
429  using Base::reserve;
430 
433  : Base()
434  {
435  }
436 
438  explicit PGSSolverStatsTpl(std::size_t max_iterations)
439  : Base(max_iterations)
440  {
441  reserve(max_iterations);
442  }
443 
444  void reserveImpl(std::size_t /*max_iterations*/)
445  {
446  // No extra fields to reserve for PGS stats.
447  }
448 
449  void resetImpl()
450  {
451  // No extra fields to reset for PGS stats.
452  }
453 
455  using Base::iterations;
456 
458  using Base::primal_feasibility;
459 
461  using Base::dual_feasibility;
462 
464  using Base::dual_feasibility_ncp;
465 
467  using Base::complementarity;
468  }; // struct PGSSolverStatsTpl
469 
470  namespace internal
471  {
474  template<typename _Scalar, int _Options>
475  struct PGSSolverWorkspaceTpl
476  {
477  typedef _Scalar Scalar;
478  static constexpr int Options = _Options;
479  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
480  typedef internal::EigenStorageTpl<VectorXs> VectorXsStorage;
481  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Options> MatrixXs;
482  typedef internal::EigenStorageTpl<MatrixXs> MatrixXsStorage;
483 
485  PGSSolverWorkspaceTpl(std::size_t problem_size = 0)
486  : problem_size(problem_size)
487  {
488  resize(problem_size);
489  reset();
490  }
491 
493  PGSSolverWorkspaceTpl(const PGSSolverWorkspaceTpl & other)
494  : PGSSolverWorkspaceTpl(0)
495  {
496  *this = other;
497  }
498 
500  PGSSolverWorkspaceTpl & operator=(const PGSSolverWorkspaceTpl & other)
501  {
502  if (this != &other)
503  {
504  problem_size = other.problem_size;
505 
506  // Since some members are maps reference on EigenStorage, we cannot simply copy them.
507  // Thus we need to explicitly say we copy the storage, and the maps will automatically
508  // point to the new storage.
509  delassus_matrix_storage = other.delassus_matrix_storage;
510  x_storage = other.x_storage;
511  x_previous_storage = other.x_previous_storage;
512  y_storage = other.y_storage;
513  rhs_storage = other.rhs_storage;
514  tmp_storage = other.tmp_storage;
515  }
516  return *this;
517  }
518 
520  void reset()
521  {
522  resize(problem_size);
523 
524 #ifndef NDEBUG
525  delassus_matrix.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
526  x.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
527  x_previous.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
528  y.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
529  tmp.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
530  rhs.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
531 #endif
532  }
533 
535  void resize(std::size_t problem_size_)
536  {
537  problem_size = problem_size_;
538 
539  Eigen::Index np = static_cast<Eigen::Index>(problem_size);
540  delassus_matrix_storage.resize(np, np);
541  x_storage.resize(np);
542  x_previous_storage.resize(np);
543  y_storage.resize(np);
544  tmp_storage.resize(np);
545  rhs_storage.resize(np);
546  }
547 
549  std::size_t problem_size;
550 
552  MatrixXsStorage delassus_matrix_storage;
553  typename MatrixXsStorage::RefMapType delassus_matrix = delassus_matrix_storage.map();
554 
556  VectorXsStorage x_storage;
557  typename VectorXsStorage::RefMapType x = x_storage.map();
558 
560  VectorXsStorage x_previous_storage;
561  typename VectorXsStorage::RefMapType x_previous = x_previous_storage.map();
562 
564  VectorXsStorage y_storage;
565  typename VectorXsStorage::RefMapType y = y_storage.map();
566 
568  VectorXsStorage rhs_storage;
569  typename VectorXsStorage::RefMapType rhs = rhs_storage.map();
570 
572  VectorXsStorage tmp_storage;
573  typename VectorXsStorage::RefMapType tmp = tmp_storage.map();
574  }; // struct PGSSolverWorkspaceTpl
575  } // namespace internal
576 
577 } // namespace pinocchio
578 
579 // IWYU pragma: begin_exports
580 #include "pinocchio/src/algorithm/solvers/pgs-solver.hxx"
581 // IWYU pragma: end_exports
Main pinocchio namespace.
Definition: treeview.dox:11
Projected Gauss Siedel solver.
Definition: pgs-solver.hpp:59
bool m_is_valid
Flag to check whether or not the solver is in a reset state. If not, its stats are valid.
Definition: pgs-solver.hpp:134
bool isValid() const
Returns true if solver is in a valid state (it has solved a constraint problem). If so,...
Definition: pgs-solver.hpp:96
PGSConstraintSolverTpl(std::size_t max_problem_size=0)
Default constructor.
Definition: pgs-solver.hpp:84
PGSSolverStats stats
Per-iteration stats of the PGS solver.
Definition: pgs-solver.hpp:124
PGSSolverWorkspace m_workspace
Workspace of the PGS solver. This is an internal of the solver and is not meant to be accessed by use...
Definition: pgs-solver.hpp:130
Struct describing the solution of the PGS constraint solver after calling the solve method....
Definition: pgs-solver.hpp:233
PGSSolverResultTpl()
Default constructor.
Definition: pgs-solver.hpp:248
VectorXsStorage x_storage
Primal solution.
Definition: pgs-solver.hpp:402
void resize(std::size_t problem_size_)
Resize the primal/dual vectors of the solution.
Definition: pgs-solver.hpp:303
std::optional< RefConstVectorXs > impulse_guess
Optional guess for the primal variable (impulses).
Definition: pgs-solver.hpp:394
void retrieveDualSolution(const Eigen::MatrixBase< VectorLike > &dual_solution_) const
Retrieve dual solution.
Definition: pgs-solver.hpp:322
PGSSolverResultTpl(const PGSSolverResultTpl &other)
Copy constructor.
Definition: pgs-solver.hpp:256
PGSSolverResultTpl & operator=(const PGSSolverResultTpl &other)
Assignment operator.
Definition: pgs-solver.hpp:262
VectorXsStorage y_storage
Dual solution.
Definition: pgs-solver.hpp:406
std::size_t problem_size
Size of primal/dual variables.
Definition: pgs-solver.hpp:388
void retrievePrimalSolution(const Eigen::MatrixBase< VectorLike > &primal_solution_) const
Retrieve primal solution.
Definition: pgs-solver.hpp:314
void retrieveConstraintVelocitiesImpl(const Eigen::MatrixBase< VectorLike > &constraint_velocities_) const
Definition: pgs-solver.hpp:365
VectorXsStorage m_impulse_guess_storage
Storage for the optional impulse guess.
Definition: pgs-solver.hpp:410
Settings for the PGS constraint solver loop.
Definition: pgs-solver.hpp:148
PGSSolverSettingsTpl(std::size_t max_iterations=1000, Scalar absolute_feasibility_tol=Scalar(1e-6), Scalar relative_feasibility_tol=Scalar(1e-6), Scalar absolute_complementarity_tol=Scalar(1e-6), Scalar relative_complementarity_tol=Scalar(1e-6), bool solve_ncp=true, bool measure_timings=false, bool stat_record=false, Scalar over_relaxation=Scalar(1))
Default constructor.
Definition: pgs-solver.hpp:155
Scalar over_relaxation
Over-relaxation of PGS step. Default value is 1.
Definition: pgs-solver.hpp:217
PGSSolverStatsTpl(std::size_t max_iterations)
Constructor given a maximum iteration of the solver.
Definition: pgs-solver.hpp:438
PGSSolverStatsTpl()
Default constructor.
Definition: pgs-solver.hpp:432