$darkmode
pinocchio 4.0.0
A fast and flexible implementation of Rigid Body Dynamics algorithms and their analytical derivatives
admm-solver.hpp
1 //
2 // Copyright (c) 2022-2025 INRIA
3 //
4 
5 #pragma once
6 
7 // IWYU pragma: begin_keep
8 #include <cassert>
9 #include <cmath>
10 #include <cstddef>
11 #include <vector>
12 #include <optional>
13 #include <limits>
14 
15 #include <Eigen/Core>
16 #include <Eigen/Eigenvalues>
17 
18 #include "pinocchio/eigen-common.hpp"
19 #include "pinocchio/macros.hpp"
20 
21 #include "pinocchio/container/eigen-storage.hpp"
22 
23 #include "pinocchio/math.hpp"
24 
25 #include "pinocchio/tracy.hpp"
26 
27 #include "pinocchio/utils/check.hpp"
28 
29 #include "pinocchio/constraints.hpp"
30 
31 #include "pinocchio/algorithm/solvers/fwd.hpp"
32 #include "pinocchio/algorithm/solvers/constraint-solver-base.hpp"
33 #include "pinocchio/algorithm/solvers/constraint-solver-utils.hpp"
34 #include "pinocchio/algorithm/solvers/anderson-acceleration.hpp"
35 // IWYU pragma: end_keep
36 
37 namespace pinocchio
38 {
39  // fwd declarations for ADMM-internal structs.
40  // user-api structs are fwd delclared in solvers/fwd.hpp.
41  // see bottom of file for definitions
42  namespace internal
43  {
44  template<typename Scalar, int Options>
45  struct ADMMSolverWorkspaceTpl;
46 
47  template<typename Scalar>
48  struct ADMMSpectralUpdateRuleTpl;
49 
50  template<typename Scalar>
51  struct ADMMLinearUpdateRuleTpl;
52 
53  template<typename Scalar>
54  struct ADMMOSQPUpdateRuleTpl;
55 
57  template<typename Scalar>
58  union ADMMUpdateRuleContainerTpl {
59  ADMMUpdateRuleContainerTpl()
60  : dummy() {};
61  ADMMSpectralUpdateRuleTpl<Scalar> spectral_rule;
62  ADMMOSQPUpdateRuleTpl<Scalar> osqp_rule;
63  ADMMLinearUpdateRuleTpl<Scalar> linear_rule;
64 
65  protected:
66  struct Dummy
67  {
68  Dummy() {};
69  };
70 
71  Dummy dummy{};
72  }; // struct ADMMUpdateRuleContainerTpl
73  } // namespace internal
74 
81  enum class ADMMUpdateRule : char
82  {
83  SPECTRAL = 'S',
84  OSQP = 'O',
85  LINEAR = 'L',
86  CONSTANT = 'C',
87  };
88 
95  enum class ADMMProximalRule : char
96  {
97  MANUAL = 'M',
98  AUTOMATIC = 'A',
99  };
100 
101  template<typename _Scalar, int _Options>
102  struct traits<ADMMConstraintSolverTpl<_Scalar, _Options>>
103  {
104  typedef _Scalar Scalar;
105  static constexpr int Options = _Options;
106 
109  };
110 
119  template<typename _Scalar, int _Options>
120  struct ADMMConstraintSolverTpl : ConstraintSolverBase<ADMMConstraintSolverTpl<_Scalar, _Options>>
121  {
122  typedef _Scalar Scalar;
123  static constexpr int Options = _Options;
124 
126  typedef ConstraintSolverBase<Self> Base;
127 
128  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
129  typedef Eigen::Ref<VectorXs> RefVectorXs;
130  typedef Eigen::Ref<const VectorXs> RefConstVectorXs;
131  typedef const Eigen::Ref<const VectorXs> ConstRefVectorXs;
132  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Options> MatrixXs;
133 
135  typedef internal::ADMMSolverWorkspaceTpl<Scalar, Options> ADMMSolverWorkspace;
138 
139  typedef internal::ADMMSpectralUpdateRuleTpl<Scalar> ADMMSpectralUpdateRule;
140  typedef internal::ADMMOSQPUpdateRuleTpl<Scalar> ADMMOSQPUpdateRule;
141  typedef internal::ADMMLinearUpdateRuleTpl<Scalar> ADMMLinearUpdateRule;
142  typedef internal::ADMMUpdateRuleContainerTpl<Scalar> ADMMUpdateRuleContainer;
143 
144  using Base::reset;
145  using Base::timerStart;
146  using Base::timerStop;
147 
156  explicit ADMMConstraintSolverTpl(std::size_t max_problem_size = 0)
157  : Base()
158  , stats()
159  , m_workspace(max_problem_size)
160  , m_is_valid(false)
161  {
162  // we need to call reset - the solver needs to look as if it never ran
163  reset();
164  }
165 
168  bool isValid() const
169  {
170  return m_is_valid;
171  }
172 
173  template<
174  typename DelassusDerived,
175  typename VectorLike,
176  typename ConstraintModel,
177  typename ConstraintModelAllocator,
178  typename ConstraintData,
179  typename ConstraintDataAllocator>
180  bool solveImpl(
181  DelassusOperatorBase<DelassusDerived> & delassus,
182  const Eigen::MatrixBase<VectorLike> & g,
183  const std::vector<ConstraintModel, ConstraintModelAllocator> & constraint_models,
184  const std::vector<ConstraintData, ConstraintDataAllocator> & constraint_datas,
185  const ADMMSolverSettings & settings,
186  ADMMSolverResult & result);
187 
188  void resetImpl()
189  {
190  stats.reset();
191  m_workspace.reset();
192  m_is_valid = false;
193  }
194 
197 
198  protected:
202  ADMMSolverWorkspace m_workspace;
203 
207 
209  template<typename DelassusDerived>
211  const DelassusOperatorBase<DelassusDerived> & delassus, ADMMSolverWorkspace & workspace);
212 
214  template<
215  typename DelassusDerived,
216  typename VectorLike,
217  typename ConstraintModel,
218  typename ConstraintModelAllocator,
219  typename ConstraintData,
220  typename ConstraintDataAllocator>
222  DelassusOperatorBase<DelassusDerived> & delassus,
223  const Eigen::MatrixBase<VectorLike> & g,
224  const std::vector<ConstraintModel, ConstraintModelAllocator> & constraint_models,
225  const std::vector<ConstraintData, ConstraintDataAllocator> & constraint_datas,
226  const ADMMSolverSettings & settings,
227  const ADMMSolverResult & result,
228  ADMMSolverWorkspace & workspace);
229 
231  template<typename DelassusDerived>
232  static void retrieveRhoGuess(
233  const DelassusOperatorBase<DelassusDerived> & delassus,
234  const ADMMSolverSettings & settings,
235  const ADMMSolverResult & result,
236  ADMMSolverWorkspace & workspace);
237  }; // struct ADMMConstraintSolverTpl
238 
239  template<typename _Scalar>
240  struct traits<ADMMSolverSettingsTpl<_Scalar>>
241  {
242  typedef _Scalar Scalar;
243  };
244 
247  template<typename _Scalar>
248  struct ADMMSolverSettingsTpl : ConstraintSolverSettingsBase<ADMMSolverSettingsTpl<_Scalar>>
249  {
250  typedef _Scalar Scalar;
251  typedef ADMMSolverSettingsTpl Self;
252  typedef ConstraintSolverSettingsBase<Self> Base;
253 
256  std::size_t max_iterations = 1000,
257  Scalar absolute_feasibility_tol = Scalar(1e-6),
258  Scalar relative_feasibility_tol = Scalar(1e-6),
259  Scalar absolute_complementarity_tol = Scalar(1e-6),
260  Scalar relative_complementarity_tol = Scalar(1e-6),
261  bool solve_ncp = true,
262  bool measure_timings = false,
263  bool stat_record = false,
264  std::optional<Scalar> rho_init = std::nullopt,
266  ADMMUpdateRule admm_update_rule = ADMMUpdateRule::OSQP,
267  ADMMProximalRule admm_proximal_rule = ADMMProximalRule::MANUAL,
268  Scalar mu_prox = Scalar(1e-6),
269  Scalar tau_prox = Scalar(1),
270  Scalar tau = Scalar(0.5),
271  Scalar ratio_primal_dual = Scalar(10),
272  Scalar dual_momentum = Scalar(0),
273  Scalar rho_update_ratio = Scalar(0),
274  std::size_t rho_min_update_frequency = 1,
275  Scalar rho_momentum = Scalar(0),
276  Scalar rho_min = Scalar(1e-6),
277  Scalar rho_max = Scalar(1e6),
278  Scalar spectral_rho_power_init = Scalar(0.2),
279  Scalar spectral_rho_power_factor = Scalar(0.05),
280  Scalar linear_update_rule_factor = Scalar(2),
281  std::size_t lanczos_size = std::numeric_limits<int>::max(),
282  std::size_t max_delassus_decomposition_updates = std::numeric_limits<int>::max(),
283  std::size_t anderson_capacity = 0)
284  : Base(
285  max_iterations,
286  absolute_feasibility_tol,
287  relative_feasibility_tol,
288  absolute_complementarity_tol,
289  relative_complementarity_tol,
290  solve_ncp,
291  measure_timings,
292  stat_record)
293  , rho_init(rho_init)
297  , mu_prox(mu_prox)
298  , tau_prox(tau_prox)
299  , tau(tau)
305  , rho_min(rho_min)
306  , rho_max(rho_max)
313  {
314  }
315 
316  void checkValidityImpl() const
317  {
318  if (rho_init)
319  {
320  PINOCCHIO_CHECK_INPUT_ARGUMENT(
321  rho_init.value() > Scalar(0), "rho_init should be none or > 0.");
322  }
323  PINOCCHIO_CHECK_INPUT_ARGUMENT(mu_prox > Scalar(0), "mu_prox should be > 0.");
324  PINOCCHIO_CHECK_INPUT_ARGUMENT(
325  tau_prox <= Scalar(1) && tau_prox > Scalar(0), "tau_prox should lie in ]0,1].");
326  PINOCCHIO_CHECK_INPUT_ARGUMENT(
327  tau <= Scalar(1) && tau > Scalar(0), "tau should lie in ]0,1].");
328  PINOCCHIO_CHECK_INPUT_ARGUMENT(
329  ratio_primal_dual > Scalar(0), "ratio_primal_dual should be > 0.");
330  PINOCCHIO_CHECK_INPUT_ARGUMENT(dual_momentum >= Scalar(0), "dual_momentum should be >= 0.");
331  PINOCCHIO_CHECK_INPUT_ARGUMENT(
332  rho_momentum >= Scalar(0) && rho_momentum <= Scalar(1),
333  "rho_momentum should be in [0, 1].");
334  PINOCCHIO_CHECK_INPUT_ARGUMENT(
335  linear_update_rule_factor >= Scalar(0), "linear_update_rule_factor should be >= 0.");
336  PINOCCHIO_CHECK_INPUT_ARGUMENT(
337  rho_min >= Scalar(0) && rho_max >= Scalar(0) && rho_min <= rho_max,
338  "rho_min and rho_max don't verify 0 <= rho_min <= rho_max");
339  }
340 
341  // ----------------------
342  // General settings
343 
345  using Base::max_iterations;
346 
348  using Base::absolute_feasibility_tol;
349 
351  using Base::relative_feasibility_tol;
352 
354  using Base::absolute_complementarity_tol;
355 
357  using Base::relative_complementarity_tol;
358 
361  using Base::solve_ncp;
362 
364  using Base::measure_timings;
365 
367  using Base::stat_record;
368 
369  // ----------------------
370  // ADMM specific settings
371 
375  std::optional<Scalar> rho_init;
376 
382 
385 
388 
391  Scalar mu_prox;
392 
394  Scalar tau_prox;
395 
397  Scalar tau;
398 
402 
405 
410 
414 
416  Scalar rho_momentum;
417 
420  Scalar rho_min;
421 
424  Scalar rho_max;
425 
428 
431 
434 
437  std::size_t lanczos_size;
438 
442 
445  std::size_t anderson_capacity;
446 
447  }; // struct ADMMSolverSettingsTpl
448 
449  template<typename _Scalar, int _Options>
450  struct traits<ADMMSolverResultTpl<_Scalar, _Options>>
451  {
452  typedef _Scalar Scalar;
453  static constexpr int Options = _Options;
454  };
455 
460  template<typename _Scalar, int _Options>
461  struct ADMMSolverResultTpl : ConstraintSolverResultBase<ADMMSolverResultTpl<_Scalar, _Options>>
462  {
463  typedef _Scalar Scalar;
464  static constexpr int Options = _Options;
465  typedef ADMMSolverResultTpl Self;
466  typedef ConstraintSolverResultBase<Self> Base;
467 
468  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
469  typedef internal::EigenStorageTpl<VectorXs> VectorXsStorage;
470  typedef Eigen::Ref<const VectorXs> RefConstVectorXs;
471 
472  using Base::constraintSize;
473  using Base::setConstraintImpulseGuess;
474  using Base::setConstraintVelocityGuess;
475 
478  : Base()
479  , problem_size(0)
481  , impulse_guess(std::nullopt)
482  , velocity_guess(std::nullopt)
483  , rho(std::numeric_limits<Scalar>::quiet_NaN())
484  , spectral_rho_power(std::numeric_limits<Scalar>::quiet_NaN())
485  , mu_prox(std::numeric_limits<Scalar>::quiet_NaN())
486  {
487  }
488 
491  {
492  *this = other;
493  }
494 
497  {
498  if (this != &other)
499  {
500  Base::operator=(other);
501  problem_size = other.problem_size;
503  rho = other.rho;
505  mu_prox = other.mu_prox;
506 
507  // Since some members are maps reference on EigenStorage, we cannot simply copy them.
508  // Thus we need to explicitly say we copy the storage, and the maps will automatically point
509  // to the new storage.
510  x_storage = other.x_storage;
511  y_storage = other.y_storage;
512  z_storage = other.z_storage;
514 
515  // special care must be taken for the optional guesses
516  if (other.impulse_guess)
517  {
518  setConstraintImpulseGuess(other.impulse_guess.value());
519  }
520  else
521  {
522  clearConstraintImpulseGuessImpl();
523  }
524  if (other.velocity_guess)
525  {
526  setConstraintVelocityGuess(other.velocity_guess.value());
527  }
528  else
529  {
530  clearConstraintVelocityGuessImpl();
531  }
532  }
533  return *this;
534  }
535 
537  int constraintSizeImpl() const
538  {
539  return static_cast<int>(problem_size);
540  }
541 
543  void resetImpl()
544  {
546 
547  impulse_guess.reset();
548  velocity_guess.reset();
549 
550  rho = std::numeric_limits<Scalar>::quiet_NaN();
551  spectral_rho_power = std::numeric_limits<Scalar>::quiet_NaN();
552  mu_prox = std::numeric_limits<Scalar>::quiet_NaN();
553 
554  // set solution to nan - solver has not run
555  x.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
556  y.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
557  z.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
558  desaxce.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
559  }
560 
562  void resize(std::size_t problem_size_)
563  {
564  problem_size = problem_size_;
565 
566  Eigen::Index np = static_cast<Eigen::Index>(problem_size);
567  x_storage.resize(np);
568  y_storage.resize(np);
569  z_storage.resize(np);
570  desaxce_storage.resize(np);
571  }
572 
574  template<typename VectorLike>
576  const Eigen::MatrixBase<VectorLike> & non_projected_primal_solution_) const
577  {
578  auto & non_projected_primal_solution = non_projected_primal_solution_.const_cast_derived();
579  non_projected_primal_solution = x;
580  }
581 
583  template<typename VectorLike>
584  void retrievePrimalSolution(const Eigen::MatrixBase<VectorLike> & primal_solution_) const
585  {
586  auto & primal_solution = primal_solution_.const_cast_derived();
587  primal_solution = y;
588  }
589 
592  template<typename VectorLike>
593  void retrieveDualSolution(const Eigen::MatrixBase<VectorLike> & dual_solution_) const
594  {
595  auto & dual_solution = dual_solution_.const_cast_derived();
596  dual_solution = z;
597  }
598 
600  template<typename VectorLike>
601  void retrieveDesaxceTerm(const Eigen::MatrixBase<VectorLike> & desaxce_term_) const
602  {
603  auto & desaxce_term = desaxce_term_.const_cast_derived();
604  desaxce_term = desaxce;
605  }
606 
607  template<typename VectorLike>
608  void setConstraintImpulseGuessImpl(const Eigen::MatrixBase<VectorLike> & impulse_guess_in)
609  {
610  m_impulse_guess_storage.resize(impulse_guess_in.size());
611  m_impulse_guess = impulse_guess_in;
612  impulse_guess.emplace(m_impulse_guess);
613  }
614 
615  void clearConstraintImpulseGuessImpl()
616  {
617  m_impulse_guess_storage.resize(0);
618  impulse_guess.reset();
619  }
620 
621  template<typename VectorLike>
622  void setConstraintVelocityGuessImpl(const Eigen::MatrixBase<VectorLike> & velocity_guess_in)
623  {
624  m_velocity_guess_storage.resize(velocity_guess_in.size());
625  m_velocity_guess = velocity_guess_in;
626  velocity_guess.emplace(m_velocity_guess);
627  }
628 
629  void clearConstraintVelocityGuessImpl()
630  {
631  m_velocity_guess_storage.resize(0);
632  velocity_guess.reset();
633  }
634 
635  template<typename VectorLike>
636  void
637  retrieveConstraintImpulsesImpl(const Eigen::MatrixBase<VectorLike> & constraint_impulses_) const
638  {
639  auto & constraint_impulses = constraint_impulses_.const_cast_derived();
640  constraint_impulses = y;
641  }
642 
645  template<typename VectorLike>
647  const Eigen::MatrixBase<VectorLike> & constraint_velocities_) const
648  {
649  auto & constraint_velocities = constraint_velocities_.const_cast_derived();
650  constraint_velocities = z - desaxce;
651  }
652 
654  using Base::iterations;
655 
657  using Base::converged;
658 
660  using Base::primal_feasibility;
661 
663  using Base::dual_feasibility;
664 
666  using Base::complementarity;
667 
669  std::size_t problem_size;
670 
673 
674  // ----------------------
675  // Solution warmstart
676 
678  std::optional<RefConstVectorXs> impulse_guess;
679 
681  std::optional<RefConstVectorXs> velocity_guess;
682 
683  // ----------------------
684  // Solution - output of the solver
685 
687  Scalar rho;
688 
692 
694  Scalar mu_prox;
695 
699  VectorXsStorage x_storage;
700  typename VectorXsStorage::RefMapType x = x_storage.map();
701 
703  VectorXsStorage y_storage;
704  typename VectorXsStorage::RefMapType y = y_storage.map();
705 
707  VectorXsStorage z_storage;
708  typename VectorXsStorage::RefMapType z = z_storage.map();
709 
711  VectorXsStorage desaxce_storage;
712  typename VectorXsStorage::RefMapType desaxce = desaxce_storage.map();
713 
714  protected:
716  VectorXsStorage m_impulse_guess_storage;
717  typename VectorXsStorage::RefMapType m_impulse_guess = m_impulse_guess_storage.map();
718 
720  VectorXsStorage m_velocity_guess_storage;
721  typename VectorXsStorage::RefMapType m_velocity_guess = m_velocity_guess_storage.map();
722  }; // struct ADMMSolverResultTpl
723 
724  template<typename _Scalar>
725  struct traits<ADMMSolverStatsTpl<_Scalar>>
726  {
727  typedef _Scalar Scalar;
728  };
729 
732  template<typename _Scalar>
733  struct ADMMSolverStatsTpl : ConstraintSolverStatsBase<ADMMSolverStatsTpl<_Scalar>>
734  {
735  typedef _Scalar Scalar;
736  typedef ADMMSolverStatsTpl Self;
737  typedef ConstraintSolverStatsBase<Self> Base;
738 
739  using Base::reserve;
740 
743  : Base()
745  {
746  }
747 
749  explicit ADMMSolverStatsTpl(std::size_t max_iterations)
750  : Base(max_iterations)
752  {
753  reserve(max_iterations);
754  }
755 
756  void reserveImpl(std::size_t max_iterations)
757  {
758  rho.reserve(max_iterations);
759  mu_prox.reserve(max_iterations);
760  anderson_size.reserve(max_iterations);
761  linear_system_residual.reserve(max_iterations);
762  linear_system_consistency.reserve(max_iterations);
763  }
764 
766  void resetImpl()
767  {
768  rho.clear();
769  mu_prox.clear();
770  anderson_size.clear();
771  linear_system_residual.clear();
774  }
775 
777  using Base::iterations;
778 
780  using Base::primal_feasibility;
781 
783  using Base::dual_feasibility;
784 
786  using Base::dual_feasibility_ncp;
787 
789  using Base::complementarity;
790 
792  std::vector<Scalar> rho;
793 
795  std::vector<Scalar> mu_prox;
796 
798  std::vector<std::size_t> anderson_size;
799 
801  std::vector<Scalar> linear_system_residual;
802 
804  std::vector<Scalar> linear_system_consistency;
805 
808  }; // struct ADMMSolverStatsTpl
809 
810  namespace internal
811  {
814  template<typename _Scalar, int _Options>
815  struct ADMMSolverWorkspaceTpl
816  {
817  typedef _Scalar Scalar;
818  static constexpr int Options = _Options;
819  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
820  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Options> MatrixXs;
821  typedef internal::EigenStorageTpl<VectorXs> VectorXsStorage;
822  typedef LanczosDecompositionTpl<MatrixXs> LanczosDecomposition;
823  typedef AndersonAccelerationTpl<Scalar> AndersonAcceleration;
824 
826  ADMMSolverWorkspaceTpl(
827  std::size_t problem_size = 0, //
828  std::size_t lanczos_size = 2, //
829  std::size_t anderson_capacity = 0)
830  : problem_size(problem_size)
831  , lanczos_size(lanczos_size)
832  , anderson_capacity(anderson_capacity)
833  , delassus_decomposition_update_count(0)
834  , delassus_smallest_eigenvalue(std::nullopt)
835  , delassus_largest_eigenvalue(std::nullopt)
836  , rho(std::numeric_limits<Scalar>::quiet_NaN())
837  , spectral_rho_power(std::numeric_limits<Scalar>::quiet_NaN())
838  , mu_prox(std::numeric_limits<Scalar>::quiet_NaN())
839  , lanczos_decomposition(
840  static_cast<Eigen::Index>(math::max(std::size_t(2), problem_size)), //
841  static_cast<Eigen::Index>(
842  math::max(std::size_t(2), math::min(problem_size, lanczos_size))))
843  , anderson_history(problem_size, anderson_capacity)
844  {
845  resize(problem_size, lanczos_size, anderson_capacity);
846  reset();
847  }
848 
850  ADMMSolverWorkspaceTpl(const ADMMSolverWorkspaceTpl & other)
851  : ADMMSolverWorkspaceTpl(0, 2, 0)
852  {
853  *this = other;
854  }
855 
857  ADMMSolverWorkspaceTpl & operator=(const ADMMSolverWorkspaceTpl & other)
858  {
859  if (this != &other)
860  {
861  problem_size = other.problem_size;
862  lanczos_size = other.lanczos_size;
863  anderson_capacity = other.anderson_capacity;
864  delassus_decomposition_update_count = other.delassus_decomposition_update_count;
865  delassus_smallest_eigenvalue = other.delassus_smallest_eigenvalue;
866  delassus_largest_eigenvalue = other.delassus_largest_eigenvalue;
867  rho = other.rho;
868  spectral_rho_power = other.spectral_rho_power;
869  mu_prox = other.mu_prox;
870  lanczos_decomposition = other.lanczos_decomposition;
871  anderson_history = other.anderson_history;
872 
873  // Since we some members are maps on EigenStorage, we cannot simply copy them.
874  // Thus we need to explicitly say we copy the storage, and the maps will automatically
875  // point to the new storage.
876  x_storage = other.x_storage;
877  x_previous_storage = other.x_previous_storage;
878  x_anderson_storage = other.x_anderson_storage;
879  y_storage = other.y_storage;
880  y_previous_storage = other.y_previous_storage;
881  z_storage = other.z_storage;
882  z_previous_storage = other.z_previous_storage;
883  z_anderson_storage = other.z_anderson_storage;
884  desaxce_storage = other.desaxce_storage;
885  rhs_storage = other.rhs_storage;
886  tmp_storage = other.tmp_storage;
887  primal_feasibility_vector_storage = other.primal_feasibility_vector_storage;
888  anderson_primal_feasibility_vector_storage =
889  other.anderson_primal_feasibility_vector_storage;
890  dual_feasibility_vector_storage = other.dual_feasibility_vector_storage;
891  }
892  return *this;
893  }
894 
896  void reset()
897  {
898  delassus_decomposition_update_count = 0;
899  delassus_smallest_eigenvalue.reset();
900  delassus_largest_eigenvalue.reset();
901 
902  rho = std::numeric_limits<Scalar>::quiet_NaN();
903  spectral_rho_power = std::numeric_limits<Scalar>::quiet_NaN();
904  mu_prox = std::numeric_limits<Scalar>::quiet_NaN();
905 
906 #ifndef NDEBUG
907  // for debugging purposes
908  x.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
909  x_previous.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
910  x_anderson.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
911  y.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
912  y_previous.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
913  z.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
914  z_previous.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
915  z_anderson.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
916  desaxce.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
917  rhs.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
918  tmp.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
919  primal_feasibility_vector.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
920  anderson_primal_feasibility_vector.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
921  dual_feasibility_vector.setConstant(std::numeric_limits<Scalar>::quiet_NaN());
922 #endif
923  }
924 
926  void resize(
927  std::size_t problem_size_, //
928  std::size_t lanczos_size_, //
929  std::size_t anderson_capacity_)
930  {
931  problem_size = problem_size_;
932  lanczos_size = math::max(std::size_t(2), math::min(problem_size, lanczos_size_));
933  anderson_capacity = anderson_capacity_;
934 
935  const Eigen::Index np = static_cast<Eigen::Index>(problem_size);
936  x_storage.resize(np);
937  x_anderson_storage.resize(np);
938  y_storage.resize(np);
939  x_previous_storage.resize(np);
940  y_previous_storage.resize(np);
941  z_storage.resize(np);
942  z_anderson_storage.resize(np);
943  z_previous_storage.resize(np);
944  desaxce_storage.resize(np);
945  rhs_storage.resize(np);
946  tmp_storage.resize(np);
947  primal_feasibility_vector_storage.resize(np);
948  anderson_primal_feasibility_vector_storage.resize(np);
949  dual_feasibility_vector_storage.resize(np);
950 
951  // resize lanczos
952  const std::size_t lanczos_problem_size = math::max(std::size_t(2), problem_size);
953  if (
954  lanczos_decomposition.size() != static_cast<Eigen::Index>(lanczos_problem_size)
955  || lanczos_decomposition.decompositionSize() != static_cast<Eigen::Index>(lanczos_size))
956  {
957  lanczos_decomposition = LanczosDecomposition(
958  static_cast<Eigen::Index>(lanczos_problem_size),
959  static_cast<Eigen::Index>(lanczos_size));
960  }
961 
962  // resize anderson
963  anderson_history.reserve(problem_size, anderson_capacity);
964  }
965 
967  std::size_t problem_size;
968 
970  std::size_t lanczos_size;
971 
973  std::size_t anderson_capacity;
974 
976  std::size_t delassus_decomposition_update_count;
977 
980  std::optional<Scalar> delassus_smallest_eigenvalue;
981 
984  std::optional<Scalar> delassus_largest_eigenvalue;
985 
987  Scalar rho;
988 
990  Scalar spectral_rho_power;
991 
993  Scalar mu_prox;
994 
999  LanczosDecomposition lanczos_decomposition;
1000 
1002  AndersonAcceleration anderson_history;
1003 
1005  VectorXsStorage x_storage;
1006  typename VectorXsStorage::RefMapType x = x_storage.map();
1007 
1009  VectorXsStorage x_previous_storage;
1010  typename VectorXsStorage::RefMapType x_previous = x_previous_storage.map();
1011 
1013  VectorXsStorage x_anderson_storage;
1014  typename VectorXsStorage::RefMapType x_anderson = x_anderson_storage.map();
1015 
1017  VectorXsStorage y_storage;
1018  typename VectorXsStorage::RefMapType y = y_storage.map();
1019 
1021  VectorXsStorage y_previous_storage;
1022  typename VectorXsStorage::RefMapType y_previous = y_previous_storage.map();
1023 
1025  VectorXsStorage z_storage;
1026  typename VectorXsStorage::RefMapType z = z_storage.map();
1027 
1029  VectorXsStorage z_previous_storage;
1030  typename VectorXsStorage::RefMapType z_previous = z_previous_storage.map();
1031 
1033  VectorXsStorage z_anderson_storage;
1034  typename VectorXsStorage::RefMapType z_anderson = z_anderson_storage.map();
1035 
1037  VectorXsStorage desaxce_storage;
1038  typename VectorXsStorage::RefMapType desaxce = desaxce_storage.map();
1039 
1041  VectorXsStorage rhs_storage;
1042  typename VectorXsStorage::RefMapType rhs = rhs_storage.map();
1043 
1045  VectorXsStorage tmp_storage;
1046  typename VectorXsStorage::RefMapType tmp = tmp_storage.map();
1047 
1049  VectorXsStorage primal_feasibility_vector_storage;
1050  typename VectorXsStorage::RefMapType primal_feasibility_vector =
1051  primal_feasibility_vector_storage.map();
1052 
1054  VectorXsStorage anderson_primal_feasibility_vector_storage;
1055  typename VectorXsStorage::RefMapType anderson_primal_feasibility_vector =
1056  anderson_primal_feasibility_vector_storage.map();
1057 
1059  VectorXsStorage dual_feasibility_vector_storage;
1060  typename VectorXsStorage::RefMapType dual_feasibility_vector =
1061  dual_feasibility_vector_storage.map();
1062  }; // struct ADMMSolverWorkspaceTpl
1063 
1066  template<typename _Scalar>
1067  struct ADMMSpectralUpdateRuleTpl
1068  {
1069  typedef _Scalar Scalar;
1070 
1072  ADMMSpectralUpdateRuleTpl(
1073  const Scalar ratio_primal_dual,
1074  const Scalar L,
1075  const Scalar m,
1076  const Scalar rho_power_factor)
1077  : ratio_primal_dual(ratio_primal_dual)
1078  , rho_increment(std::pow(L / m, rho_power_factor))
1079  {
1080  PINOCCHIO_CHECK_INPUT_ARGUMENT(m > Scalar(0), "m should be positive.");
1081  PINOCCHIO_CHECK_INPUT_ARGUMENT(L > m, "L should be > m");
1082  }
1083 
1085  void eval(const Scalar primal_feasibility, const Scalar dual_feasibility, Scalar & rho) const
1086  {
1087  if (primal_feasibility > ratio_primal_dual * dual_feasibility)
1088  {
1089  rho *= rho_increment;
1090  // rho *= math::pow(cond,rho_power_factor);
1091  // rho_power += rho_power_factor;
1092  }
1093  else if (dual_feasibility > ratio_primal_dual * primal_feasibility)
1094  {
1095  rho /= rho_increment;
1096  // rho *= math::pow(cond,-rho_power_factor);
1097  // rho_power -= rho_power_factor;
1098  }
1099  }
1100 
1103  static inline Scalar computeRho(const Scalar L, const Scalar m, const Scalar rho_power)
1104  {
1105  const Scalar cond = L / m;
1106  const Scalar rho = math::sqrt(L * m) * math::pow(cond, rho_power);
1107  return rho;
1108  }
1109 
1112  static inline Scalar computeRhoPower(const Scalar L, const Scalar m, const Scalar rho)
1113  {
1114  const Scalar cond = L / m;
1115  const Scalar sqrt_L_m = math::sqrt(L * m);
1116  const Scalar rho_power = math::log(rho / sqrt_L_m) / math::log(cond);
1117  return rho_power;
1118  }
1119 
1121  Scalar ratio_primal_dual;
1122 
1125  Scalar rho_increment;
1126  };
1127 
1130  template<typename _Scalar>
1131  struct ADMMLinearUpdateRuleTpl
1132  {
1133  typedef _Scalar Scalar;
1134 
1136  ADMMLinearUpdateRuleTpl(
1137  const Scalar ratio_primal_dual, const Scalar increase_factor, const Scalar decrease_factor)
1138  : ratio_primal_dual(ratio_primal_dual)
1139  , increase_factor(increase_factor)
1140  , decrease_factor(decrease_factor)
1141  {
1142  PINOCCHIO_CHECK_INPUT_ARGUMENT(
1143  increase_factor > Scalar(1), "increase_factor should be greater than one.");
1144  PINOCCHIO_CHECK_INPUT_ARGUMENT(
1145  decrease_factor > Scalar(1), "decrease_factor should be greater than one.");
1146  }
1147 
1150  ADMMLinearUpdateRuleTpl(const Scalar ratio_primal_dual, const Scalar factor)
1151  : ratio_primal_dual(ratio_primal_dual)
1152  , increase_factor(factor)
1153  , decrease_factor(factor)
1154  {
1155  PINOCCHIO_CHECK_INPUT_ARGUMENT(factor > Scalar(1), "factor should be greater than one.");
1156  }
1157 
1159  void eval(const Scalar primal_feasibility, const Scalar dual_feasibility, Scalar & rho) const
1160  {
1161  if (primal_feasibility > ratio_primal_dual * dual_feasibility)
1162  {
1163  rho *= increase_factor;
1164  }
1165  else if (dual_feasibility > ratio_primal_dual * primal_feasibility)
1166  {
1167  rho /= decrease_factor;
1168  }
1169  }
1170 
1172  Scalar ratio_primal_dual;
1173 
1175  Scalar increase_factor;
1176 
1178  Scalar decrease_factor;
1179  };
1180 
1183  template<typename _Scalar>
1184  struct ADMMOSQPUpdateRuleTpl
1185  {
1186  typedef _Scalar Scalar;
1187 
1189  ADMMOSQPUpdateRuleTpl(const Scalar ratio_primal_dual, const Scalar eps_reg = 1e-8)
1190  : ratio_primal_dual(ratio_primal_dual)
1191  , eps_reg(eps_reg)
1192  {
1193  PINOCCHIO_CHECK_INPUT_ARGUMENT(
1194  ratio_primal_dual > Scalar(0), "ratio_primal_dual should be positive.");
1195  PINOCCHIO_CHECK_INPUT_ARGUMENT(eps_reg > Scalar(0), "eps_reg should be positive.");
1196  }
1197 
1199  void eval(const Scalar primal_feasibility, const Scalar dual_feasibility, Scalar & rho) const
1200  {
1201  if (
1202  primal_feasibility > ratio_primal_dual * dual_feasibility //
1203  || dual_feasibility > ratio_primal_dual * primal_feasibility)
1204  {
1205  rho *= std::sqrt(primal_feasibility / (dual_feasibility + eps_reg));
1206  }
1207  }
1208 
1210  Scalar ratio_primal_dual;
1211 
1214  Scalar eps_reg;
1215  };
1216  } // namespace internal
1217 
1218 } // namespace pinocchio
1219 
1220 // IWYU pragma: begin_exports
1221 #include "pinocchio/src/algorithm/solvers/admm-solver.hxx"
1222 // IWYU pragma: end_exports
Main pinocchio namespace.
Definition: treeview.dox:11
ADMMProximalRule
ADMM proximal policy. MANUAL: mu_prox is constant and manually set. It is scaled by tau_prox AUTOMATI...
Definition: admm-solver.hpp:96
ADMMUpdateRule
ADMM rho update rule. SPECTRAL: if primal/dual ratio met, multiply rho by a spectral factor....
Definition: admm-solver.hpp:82
ADMMConstraintSolverTpl(std::size_t max_problem_size=0)
Default constructor.
static Scalar computeDelassusLargestEigenvalue(const DelassusOperatorBase< DelassusDerived > &delassus, ADMMSolverWorkspace &workspace)
Compute largest eigen value of delassus.
static void retrievePrimalDualGuess(DelassusOperatorBase< DelassusDerived > &delassus, const Eigen::MatrixBase< VectorLike > &g, const std::vector< ConstraintModel, ConstraintModelAllocator > &constraint_models, const std::vector< ConstraintData, ConstraintDataAllocator > &constraint_datas, const ADMMSolverSettings &settings, const ADMMSolverResult &result, ADMMSolverWorkspace &workspace)
Retrieve primal and/or dual guesses from settings and result's warmstarts.
bool m_is_valid
Flag to check whether or not the solver is in a reset state. If not, its stats are valid.
static void retrieveRhoGuess(const DelassusOperatorBase< DelassusDerived > &delassus, const ADMMSolverSettings &settings, const ADMMSolverResult &result, ADMMSolverWorkspace &workspace)
Retrieve rho parameters guesses from settings and result's warmstarts.
bool isValid() const
Returns true if solver is in a valid state (it has solved a constraint problem). If so,...
ADMMSolverStats stats
Per-iteration stats of the ADMM solver.
ADMMSolverWorkspace m_workspace
Workspace of the ADMM solver. This is an internal of the solver and is not meant to be accessed by us...
Struct describing the solution of the ADMM constraint solver after calling the solve method....
void retrieveNonProjectedPrimalSolution(const Eigen::MatrixBase< VectorLike > &non_projected_primal_solution_) const
Retrieve non-projected primal solution.
VectorXsStorage x_storage
Non-projected primal solution.
VectorXsStorage m_velocity_guess_storage
Storage for the optional velocity guess.
void resize(std::size_t problem_size_)
Resize the primal/dual/desaxce vectors of the solution.
ADMMSolverResultTpl(const ADMMSolverResultTpl &other)
Copy constructor.
std::optional< RefConstVectorXs > impulse_guess
Optional guess for the primal variable (impulses).
Scalar rho
Value of ADMM rho term.
void retrieveDualSolution(const Eigen::MatrixBase< VectorLike > &dual_solution_) const
Retrieve dual solution. At the optimum we have Gx + g + desaxce - z = 0.
std::size_t delassus_decomposition_update_count
Number of delassus decompositions.
void resetImpl()
Reset the results.
ADMMSolverResultTpl()
Default constructor.
ADMMSolverResultTpl & operator=(const ADMMSolverResultTpl &other)
Assignment operator.
Scalar spectral_rho_power
Value of ADMM spectral rule rho power at the solution. This is relevant only if SPECTRAL was selected...
VectorXsStorage z_storage
Dual solution.
VectorXsStorage y_storage
Primal solution projected onto constraints.
Scalar mu_prox
Value of ADMM proximal term.
std::optional< RefConstVectorXs > velocity_guess
Optional guess for the dual variable (velocities).
std::size_t problem_size
Size of primal/dual variables.
VectorXsStorage desaxce_storage
Desaxce term of the solution.
void retrievePrimalSolution(const Eigen::MatrixBase< VectorLike > &primal_solution_) const
Retrieve primal solution.
void retrieveConstraintVelocitiesImpl(const Eigen::MatrixBase< VectorLike > &constraint_velocities_) const
void retrieveDesaxceTerm(const Eigen::MatrixBase< VectorLike > &desaxce_term_) const
Retrieve Desaxce term of solution.
VectorXsStorage m_impulse_guess_storage
Storage for the optional impulse guess.
Settings for the ADMM constraint solver loop.
Scalar rho_min
Minimum value that rho can take. During 'solve', rho is clamped between rho_min and rho_max.
std::size_t rho_min_update_frequency
How many iterations before rho can be updated again. 1 means rho can be updated. every iterations (it...
std::optional< Scalar > rho_init
Initial value of rho parameter. If set to boost::none, the initial rho will be computed by estimating...
Scalar ratio_primal_dual
Value of the primal/dual ratio beyond/below which a rho update is considered. If the primal/dual rati...
ADMMUpdateRule admm_update_rule
Update rule for the rho admm term.
Scalar spectral_rho_power_init
Initial value of the rho power in the SPECTRAL update rule.
bool warmstart_rho_with_previous_result
Whether or not to warmstart rho with previous result. If set to true, the rho_init will be bypassed b...
Scalar rho_max
Maximum value that rho can take. During 'solve', rho is clamped between rho_min and rho_max.
Scalar tau_prox
Scaling factor in front of the primal proximal term.
Scalar rho_update_ratio
Ratio w.r.t previous rho value beyond/below which rho is updated. In essence, if rho has not changed ...
Scalar spectral_rho_power_factor
Power factor used to update rho in the SPECTRAL update rule.
std::size_t anderson_capacity
Size of the andersion history used to fit the anderson linear system. If set to 0 or 1,...
std::size_t lanczos_size
Size of the lanczos decomposition. Higher values lead to more precise estimation of the Delassus' max...
Scalar mu_prox
Value of the proximal term when admm_proximal_rule is MANUAL. When admm_proximal_rule is AUTOMATIC,...
Scalar rho_momentum
Momentum on rho value.
Scalar tau
Scaling factor in front of the rho ADMM term.
std::size_t max_delassus_decomposition_updates
Maximum number of delassus decomposition updates. Once this number is reached, rho and mu_prox are ke...
Scalar linear_update_rule_factor
Value by which rho is multiplied/divided in the LINEAR update rule.
Scalar dual_momentum
Momentum on the dual variable. 0 means no momentum.
ADMMSolverSettingsTpl(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, std::optional< Scalar > rho_init=std::nullopt, bool warmstart_rho_with_previous_result=false, ADMMUpdateRule admm_update_rule=ADMMUpdateRule::OSQP, ADMMProximalRule admm_proximal_rule=ADMMProximalRule::MANUAL, Scalar mu_prox=Scalar(1e-6), Scalar tau_prox=Scalar(1), Scalar tau=Scalar(0.5), Scalar ratio_primal_dual=Scalar(10), Scalar dual_momentum=Scalar(0), Scalar rho_update_ratio=Scalar(0), std::size_t rho_min_update_frequency=1, Scalar rho_momentum=Scalar(0), Scalar rho_min=Scalar(1e-6), Scalar rho_max=Scalar(1e6), Scalar spectral_rho_power_init=Scalar(0.2), Scalar spectral_rho_power_factor=Scalar(0.05), Scalar linear_update_rule_factor=Scalar(2), std::size_t lanczos_size=std::numeric_limits< int >::max(), std::size_t max_delassus_decomposition_updates=std::numeric_limits< int >::max(), std::size_t anderson_capacity=0)
Default constructor.
ADMMProximalRule admm_proximal_rule
Update rule for the primal proximal term.
ADMMSolverStatsTpl(std::size_t max_iterations)
Constructor given a maximum iteration of the solver.
std::size_t delassus_decomposition_update_count
Number of Delassus decomposition updates.
void resetImpl()
Reset stats.
std::vector< Scalar > mu_prox
History of mu_prox values.
std::vector< std::size_t > anderson_size
History of Anderson size.
std::vector< Scalar > linear_system_residual
History of linear system residual.
std::vector< Scalar > rho
History of rho values.
std::vector< Scalar > linear_system_consistency
History of linear system consistency.
ADMMSolverStatsTpl()
Default constructor.