GCC Code Coverage Report


Directory: ./
File: include/pinocchio/algorithm/admm-solver.hpp
Date: 2024-08-27 18:20:05
Exec Total Coverage
Lines: 0 84 0.0%
Branches: 0 56 0.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2022-2024 INRIA
3 //
4
5 #ifndef __pinocchio_algorithm_admm_solver_hpp__
6 #define __pinocchio_algorithm_admm_solver_hpp__
7
8 #include "pinocchio/algorithm/constraints/fwd.hpp"
9 #include "pinocchio/math/fwd.hpp"
10 #include "pinocchio/math/comparison-operators.hpp"
11 #include "pinocchio/math/eigenvalues.hpp"
12
13 #include "pinocchio/algorithm/contact-solver-base.hpp"
14 #include "pinocchio/algorithm/delassus-operator-base.hpp"
15
16 #include <boost/optional.hpp>
17
18 namespace pinocchio
19 {
20 template<typename _Scalar>
21 struct PINOCCHIO_UNSUPPORTED_MESSAGE("The API will change towards more flexibility")
22 ADMMContactSolverTpl : ContactSolverBaseTpl<_Scalar>
23 {
24 typedef _Scalar Scalar;
25 typedef ContactSolverBaseTpl<_Scalar> Base;
26 typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> VectorXs;
27 typedef const Eigen::Ref<const VectorXs> ConstRefVectorXs;
28 typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> MatrixXs;
29 typedef PowerIterationAlgoTpl<VectorXs> PowerIterationAlgo;
30
31 using Base::problem_size;
32
33 // struct SolverParameters
34 // {
35 // explicit SolverParameters(const int problem_dim)
36 // : rho_power(Scalar(0.2))
37 // , ratio_primal_dual(Scalar(10))
38 // , mu_prox
39 // {
40 //
41 // }
42 //
43 // /// \brief Rho solver ADMM
44 // boost::optional<Scalar> rho;
45 // /// \brief Power value associated to rho. This quantity will be automatically updated.
46 // Scalar rho_power;
47 // /// \brief Ratio primal/dual
48 // Scalar ratio_primal_dual;
49 // /// \brief Proximal value
50 // Scalar mu_prox;
51 //
52 // /// \brief Largest eigenvalue
53 // boost::optional<Scalar> L_value;
54 // /// \brief Largest eigenvector
55 // boost::optional<VectorXs> L_vector;
56 // };
57 //
58 struct SolverStats
59 {
60 explicit SolverStats(const int max_it)
61 : it(0)
62 , cholesky_update_count(0)
63 {
64 primal_feasibility.reserve(size_t(max_it));
65 dual_feasibility.reserve(size_t(max_it));
66 dual_feasibility_ncp.reserve(size_t(max_it));
67 complementarity.reserve(size_t(max_it));
68 rho.reserve(size_t(max_it));
69 }
70
71 void reset()
72 {
73 primal_feasibility.clear();
74 dual_feasibility.clear();
75 complementarity.clear();
76 dual_feasibility_ncp.clear();
77 rho.clear();
78 it = 0;
79 cholesky_update_count = 0;
80 }
81
82 size_t size() const
83 {
84 return primal_feasibility.size();
85 }
86
87 ///  \brief Number of total iterations.
88 int it;
89
90 ///  \brief Number of Cholesky updates.
91 int cholesky_update_count;
92
93 /// \brief History of primal feasibility values.
94 std::vector<Scalar> primal_feasibility;
95
96 /// \brief History of dual feasibility values.
97 std::vector<Scalar> dual_feasibility;
98 std::vector<Scalar> dual_feasibility_ncp;
99
100 /// \brief History of complementarity values.
101 std::vector<Scalar> complementarity;
102
103 /// \brief History of rho values.
104 std::vector<Scalar> rho;
105 };
106 //
107 // struct SolverResults
108 // {
109 // explicit SolverResults(const int problem_dim, const int max_it)
110 // : L_vector(problem_dim)
111 //
112 // /// \brief Largest eigenvalue
113 // Scalar L_value;
114 // /// \brief Largest eigenvector
115 // VectorXs L_vector;
116 //
117 // SolverStats stats;
118 // };
119
120 explicit ADMMContactSolverTpl(
121 int problem_dim,
122 Scalar mu_prox = Scalar(1e-6),
123 Scalar tau = Scalar(0.5),
124 Scalar rho_power = Scalar(0.2),
125 Scalar rho_power_factor = Scalar(0.05),
126 Scalar ratio_primal_dual = Scalar(10),
127 int max_it_largest_eigen_value_solver = 20)
128 : Base(problem_dim)
129 , is_initialized(false)
130 , mu_prox(mu_prox)
131 , tau(tau)
132 , rho(Scalar(-1))
133 , rho_power(rho_power)
134 , rho_power_factor(rho_power_factor)
135 , ratio_primal_dual(ratio_primal_dual)
136 , max_it_largest_eigen_value_solver(max_it_largest_eigen_value_solver)
137 , power_iteration_algo(problem_dim)
138 , x_(VectorXs::Zero(problem_dim))
139 , y_(VectorXs::Zero(problem_dim))
140 , x_previous(VectorXs::Zero(problem_dim))
141 , y_previous(VectorXs::Zero(problem_dim))
142 , z_previous(VectorXs::Zero(problem_dim))
143 , z_(VectorXs::Zero(problem_dim))
144 , s_(VectorXs::Zero(problem_dim))
145 , rhs(problem_dim)
146 , primal_feasibility_vector(VectorXs::Zero(problem_dim))
147 , dual_feasibility_vector(VectorXs::Zero(problem_dim))
148 , stats(Base::max_it)
149 {
150 power_iteration_algo.max_it = max_it_largest_eigen_value_solver;
151 }
152
153 /// \brief Set the ADMM penalty value.
154 void setRho(const Scalar rho)
155 {
156 this->rho = rho;
157 }
158 /// \brief Get the ADMM penalty value.
159 Scalar getRho() const
160 {
161 return rho;
162 }
163
164 /// \brief Set the power associated to the problem conditionning.
165 void setRhoPower(const Scalar rho_power)
166 {
167 this->rho_power = rho_power;
168 }
169 /// \brief Get the power associated to the problem conditionning.
170 Scalar getRhoPower() const
171 {
172 return rho_power;
173 }
174
175 /// \brief Set the power factor associated to the problem conditionning.
176 void setRhoPowerFactor(const Scalar rho_power_factor)
177 {
178 this->rho_power_factor = rho_power_factor;
179 }
180 /// \brief Get the power factor associated to the problem conditionning.
181 Scalar getRhoPowerFactor() const
182 {
183 return rho_power_factor;
184 }
185
186 /// \brief Set the tau linear scaling factor.
187 void setTau(const Scalar tau)
188 {
189 this->tau = tau;
190 }
191 /// \brief Get the tau linear scaling factor.
192 Scalar getTau() const
193 {
194 return tau;
195 }
196
197 /// \brief Set the proximal value.
198 void setProximalValue(const Scalar mu)
199 {
200 this->mu_prox = mu;
201 }
202 /// \brief Get the proximal value.
203 Scalar getProximalValue() const
204 {
205 return mu_prox;
206 }
207
208 /// \brief Set the primal/dual ratio.
209 void setRatioPrimalDual(const Scalar ratio_primal_dual)
210 {
211 PINOCCHIO_CHECK_INPUT_ARGUMENT(
212 ratio_primal_dual > 0., "The ratio primal/dual should be positive strictly");
213 this->ratio_primal_dual = ratio_primal_dual;
214 }
215 /// \brief Get the primal/dual ratio.
216 Scalar getRatioPrimalDual() const
217 {
218 return ratio_primal_dual;
219 }
220
221 ///  \returns the number of updates of the Cholesky factorization due to rho updates.
222 int getCholeskyUpdateCount() const
223 {
224 return cholesky_update_count;
225 }
226
227 ///
228 /// \brief Solve the constrained conic problem composed of problem data (G,g,cones) and starting
229 /// from the initial guess.
230 ///
231 /// \param[in] G Symmetric PSD matrix representing the Delassus of the contact problem.
232 /// \param[in] g Free contact acceleration or velicity associted with the contact problem.
233 /// \param[in] cones Vector of conic constraints.
234 /// \param[in,out] x Initial guess and output solution of the problem
235 /// \param[in] mu_prox Proximal smoothing value associated to the algorithm.
236 /// \param[in] R Proximal regularization value associated to the compliant contacts (corresponds
237 /// to the lowest non-zero). \param[in] tau Over relaxation value
238 ///
239 /// \returns True if the problem has converged.
240 template<
241 typename DelassusDerived,
242 typename VectorLike,
243 typename ConstraintAllocator,
244 typename VectorLikeR>
245 bool solve(
246 DelassusOperatorBase<DelassusDerived> & delassus,
247 const Eigen::MatrixBase<VectorLike> & g,
248 const std::vector<CoulombFrictionConeTpl<Scalar>, ConstraintAllocator> & cones,
249 const Eigen::MatrixBase<VectorLikeR> & R,
250 const boost::optional<ConstRefVectorXs> primal_guess = boost::none,
251 const boost::optional<ConstRefVectorXs> dual_guess = boost::none,
252 bool compute_largest_eigen_values = true,
253 bool stat_record = false);
254
255 ///
256 /// \brief Solve the constrained conic problem composed of problem data (G,g,cones) and starting
257 /// from the initial guess.
258 ///
259 /// \param[in] G Symmetric PSD matrix representing the Delassus of the contact problem.
260 /// \param[in] g Free contact acceleration or velicity associted with the contact problem.
261 /// \param[in] cones Vector of conic constraints.
262 /// \param[in,out] x Initial guess and output solution of the problem
263 /// \param[in] mu_prox Proximal smoothing value associated to the algorithm.
264 /// \param[in] tau Over relaxation value
265 ///
266 /// \returns True if the problem has converged.
267 template<
268 typename DelassusDerived,
269 typename VectorLike,
270 typename ConstraintAllocator,
271 typename VectorLikeOut>
272 bool solve(
273 DelassusOperatorBase<DelassusDerived> & delassus,
274 const Eigen::MatrixBase<VectorLike> & g,
275 const std::vector<CoulombFrictionConeTpl<Scalar>, ConstraintAllocator> & cones,
276 const Eigen::DenseBase<VectorLikeOut> & x)
277 {
278 return solve(
279 delassus.derived(), g.derived(), cones, x.const_cast_derived(),
280 VectorXs::Zero(problem_size));
281 }
282
283 /// \returns the primal solution of the problem
284 const VectorXs & getPrimalSolution() const
285 {
286 return y_;
287 }
288 /// \returns the dual solution of the problem
289 const VectorXs & getDualSolution() const
290 {
291 return z_;
292 }
293 /// \returns the complementarity shift
294 const VectorXs & getComplementarityShift() const
295 {
296 return s_;
297 }
298
299 /// \brief Compute the penalty ADMM value from the current largest and lowest eigenvalues and
300 /// the scaling spectral factor.
301 static inline Scalar computeRho(const Scalar L, const Scalar m, const Scalar rho_power)
302 {
303 const Scalar cond = L / m;
304 const Scalar rho = math::sqrt(L * m) * math::pow(cond, rho_power);
305 return rho;
306 }
307
308 /// \brief Compute the scaling spectral factor of the ADMM penalty term from the current
309 /// largest and lowest eigenvalues and the ADMM penalty term.
310 static inline Scalar computeRhoPower(const Scalar L, const Scalar m, const Scalar rho)
311 {
312 const Scalar cond = L / m;
313 const Scalar sqtr_L_m = math::sqrt(L * m);
314 const Scalar rho_power = math::log(rho / sqtr_L_m) / math::log(cond);
315 return rho_power;
316 }
317
318 PowerIterationAlgo & getPowerIterationAlgo()
319 {
320 return power_iteration_algo;
321 }
322
323 SolverStats & getStats()
324 {
325 return stats;
326 }
327
328 protected:
329 bool is_initialized;
330
331 /// \brief proximal value
332 Scalar mu_prox;
333
334 /// \brief Linear scaling of the ADMM penalty term
335 Scalar tau;
336
337 /// \brief Penalty term associated to the ADMM.
338 Scalar rho;
339 /// \brief Power value associated to rho. This quantity will be automatically updated.
340 Scalar rho_power;
341 /// \brief Update factor for the primal/dual update of rho.
342 Scalar rho_power_factor;
343 ///  \brief Ratio primal/dual
344 Scalar ratio_primal_dual;
345
346 /// \brief Maximum number of iterarions called for the power iteration algorithm
347 int max_it_largest_eigen_value_solver;
348
349 /// \brief Power iteration algo.
350 PowerIterationAlgo power_iteration_algo;
351
352 /// \brief Primal variables (corresponds to the contact forces)
353 VectorXs x_, y_;
354 /// \brief Previous value of y.
355 VectorXs x_previous, y_previous, z_previous;
356 /// \brief Dual varible of the ADMM (corresponds to the contact velocity or acceleration).
357 VectorXs z_;
358 /// \brief De Saxé shift
359 VectorXs s_;
360
361 VectorXs rhs, primal_feasibility_vector, dual_feasibility_vector;
362
363 int cholesky_update_count;
364
365 /// \brief Stats of the solver
366 SolverStats stats;
367
368 #ifdef PINOCCHIO_WITH_HPP_FCL
369 using Base::timer;
370 #endif // PINOCCHIO_WITH_HPP_FCL
371 }; // struct ADMMContactSolverTpl
372 } // namespace pinocchio
373
374 #include "pinocchio/algorithm/admm-solver.hxx"
375
376 #endif // ifndef __pinocchio_algorithm_admm_solver_hpp__
377