pinocchio 4.1.0
A fast and flexible implementation of Rigid Body Dynamics algorithms and their analytical derivatives
Loading...
Searching...
No Matches
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
37namespace 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
107 typedef ADMMSolverSettingsTpl<Scalar> SolverSettings;
108 typedef ADMMSolverResultTpl<Scalar, Options> SolverResult;
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
125 typedef ADMMConstraintSolverTpl Self;
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
134 typedef ADMMSolverResultTpl<Scalar, Options> ADMMSolverResult;
135 typedef internal::ADMMSolverWorkspaceTpl<Scalar, Options> ADMMSolverWorkspace;
136 typedef ADMMSolverSettingsTpl<Scalar> ADMMSolverSettings;
137 typedef ADMMSolverStatsTpl<Scalar> ADMMSolverStats;
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
196 ADMMSolverStats stats;
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)
299 , tau(tau)
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
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);
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
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 {
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 {
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
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();
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...
ADMMUpdateRule
ADMM rho update rule. SPECTRAL: if primal/dual ratio met, multiply rho by a spectral factor....
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.
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,...
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 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
void retrieveDualSolution(const Eigen::MatrixBase< VectorLike > &dual_solution_) const
Retrieve dual solution. At the optimum we have Gx + g + desaxce - z = 0.
void resetImpl()
Reset the results.
ADMMSolverResultTpl()
Default constructor.
std::optional< RefConstVectorXs > velocity_guess
ADMMSolverResultTpl & operator=(const ADMMSolverResultTpl &other)
Assignment operator.
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.
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.
Struct to track per iteration progress of ADMM constraint solver.
ADMMSolverStatsTpl(std::size_t max_iterations)
Constructor given a maximum iteration of the solver.
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.