Directory: | ./ |
---|---|
File: | include/hpp/constraints/solver/hierarchical-iterative.hh |
Date: | 2025-05-05 12:19:30 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 40 | 42 | 95.2% |
Branches: | 13 | 52 | 25.0% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2017, 2018 | ||
2 | // Authors: Joseph Mirabel (joseph.mirabel@laas.fr) | ||
3 | // | ||
4 | |||
5 | // Redistribution and use in source and binary forms, with or without | ||
6 | // modification, are permitted provided that the following conditions are | ||
7 | // met: | ||
8 | // | ||
9 | // 1. Redistributions of source code must retain the above copyright | ||
10 | // notice, this list of conditions and the following disclaimer. | ||
11 | // | ||
12 | // 2. Redistributions in binary form must reproduce the above copyright | ||
13 | // notice, this list of conditions and the following disclaimer in the | ||
14 | // documentation and/or other materials provided with the distribution. | ||
15 | // | ||
16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
20 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||
27 | // DAMAGE. | ||
28 | |||
29 | #ifndef HPP_CONSTRAINTS_SOLVER_HIERARCHICAL_ITERATIVE_HH | ||
30 | #define HPP_CONSTRAINTS_SOLVER_HIERARCHICAL_ITERATIVE_HH | ||
31 | |||
32 | #include <functional> | ||
33 | #include <hpp/constraints/config.hh> | ||
34 | #include <hpp/constraints/fwd.hh> | ||
35 | #include <hpp/constraints/implicit-constraint-set.hh> | ||
36 | #include <hpp/constraints/matrix-view.hh> | ||
37 | #include <hpp/util/serialization-fwd.hh> | ||
38 | #include <map> | ||
39 | |||
40 | namespace hpp { | ||
41 | namespace constraints { | ||
42 | namespace solver { | ||
43 | /// \addtogroup solvers | ||
44 | /// \{ | ||
45 | namespace lineSearch { | ||
46 | /// No line search. Use \f$\alpha_i = 1\f$ | ||
47 | struct Constant { | ||
48 | template <typename SolverType> | ||
49 | bool operator()(const SolverType& solver, vectorOut_t arg, vectorOut_t darg); | ||
50 | }; | ||
51 | |||
52 | /// Implements the backtracking line search algorithm. | ||
53 | /// See https://en.wikipedia.org/wiki/Backtracking_line_search. | ||
54 | struct Backtracking { | ||
55 | Backtracking(); | ||
56 | |||
57 | template <typename SolverType> | ||
58 | bool operator()(const SolverType& solver, vectorOut_t arg, vectorOut_t darg); | ||
59 | |||
60 | template <typename SolverType> | ||
61 | inline value_type computeLocalSlope(const SolverType& solver) const; | ||
62 | |||
63 | value_type c, tau, smallAlpha; // 0.8 ^ 7 = 0.209, 0.8 ^ 8 = 0.1677 | ||
64 | mutable vector_t arg_darg, df, darg; | ||
65 | }; | ||
66 | |||
67 | /// The step size is computed using the recursion | ||
68 | /// \f$ \alpha_{i+1} = \alpha - K \times (\alpha_{max} - \alpha_i) \f$ | ||
69 | /// where \f$K\f$ and \f$\alpha_{max}\f$ are some constant values. | ||
70 | struct FixedSequence { | ||
71 | FixedSequence(); | ||
72 | |||
73 | template <typename SolverType> | ||
74 | bool operator()(const SolverType& solver, vectorOut_t arg, vectorOut_t darg); | ||
75 | |||
76 | value_type alpha; | ||
77 | value_type alphaMax, K; | ||
78 | }; | ||
79 | |||
80 | /// The step size is computed using the formula | ||
81 | /// \f$ \alpha_{i} = C - K \times \text{tanh}(a | ||
82 | /// \frac{\|f(\mathbf{q}_i)\|}{\epsilon^2} + b) \f$, where \li \f$\epsilon\f$ is | ||
83 | /// the error threshold: if \f$\|f(\mathbf{q}_i)\|<\epsilon\f$, | ||
84 | /// \f$\mathbf{q}_i\f$ is considered to satisfy the constraint. | ||
85 | struct ErrorNormBased { | ||
86 | ErrorNormBased(value_type alphaMin, value_type _a, value_type _b); | ||
87 | ErrorNormBased(value_type alphaMin = 0.2); | ||
88 | |||
89 | template <typename SolverType> | ||
90 | bool operator()(const SolverType& solver, vectorOut_t arg, vectorOut_t darg); | ||
91 | |||
92 | value_type C, K, a, b; | ||
93 | }; | ||
94 | } // namespace lineSearch | ||
95 | |||
96 | namespace saturation { | ||
97 | /// \brief Base class for box constraints. | ||
98 | /// To prevent configuration variables to get out of joint limits during | ||
99 | /// Newton Raphson iterations, the user may provide an object derived | ||
100 | /// from this type to HierarchicalIterative using setter and getter | ||
101 | /// HierarchicalIterative::saturation. | ||
102 | /// | ||
103 | /// This class implements no saturation. | ||
104 | struct Base { | ||
105 | /// This function checks which degrees of freedom are saturated. | ||
106 | /// | ||
107 | /// \param q a configuration, | ||
108 | /// \retval qSat configuration after saturing values out of bounds | ||
109 | /// \retval saturation vector: for each degree of freedom, saturation | ||
110 | /// is set to | ||
111 | /// \li -1 if the lower bound is reached, | ||
112 | /// \li 1 if the upper bound is reached, | ||
113 | /// \li 0 otherwise. | ||
114 | /// \return true if and only if at least one degree of freedom has been | ||
115 | /// saturated | ||
116 | virtual bool saturate(vectorIn_t q, vectorOut_t qSat, | ||
117 | Eigen::VectorXi& saturation); | ||
118 | 4174 | virtual ~Base() {} | |
119 | }; | ||
120 | /// \brief saturation from a std::function. | ||
121 | struct Function : Base { | ||
122 | typedef std::function<bool(vectorIn_t, vectorOut_t, Eigen::VectorXi&)> | ||
123 | function_t; | ||
124 | 264 | bool saturate(vectorIn_t q, vectorOut_t qSat, Eigen::VectorXi& saturation) { | |
125 |
2/4✓ Branch 2 taken 264 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 264 times.
✗ Branch 6 not taken.
|
264 | return function(q, qSat, saturation); |
126 | } | ||
127 | Function() {} | ||
128 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | Function(const function_t& function) : function(function) {} |
129 | function_t function; | ||
130 | }; | ||
131 | /// \brief simple box constraints | ||
132 | struct Bounds : Base { | ||
133 | bool saturate(vectorIn_t q, vectorOut_t qSat, Eigen::VectorXi& saturation); | ||
134 | ✗ | Bounds() {} | |
135 |
3/6✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
|
12 | Bounds(const vector_t& lb, const vector_t& ub) : lb(lb), ub(ub), iq2iv_() { |
136 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | iq2iv_.resize(ub.size()); |
137 |
2/2✓ Branch 1 taken 85 times.
✓ Branch 2 taken 12 times.
|
97 | for (size_type i = 0; i < ub.size(); ++i) { |
138 |
1/2✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
|
85 | iq2iv_[i] = i; |
139 | } | ||
140 | 12 | } | |
141 | Bounds(const vector_t& lb, const vector_t& ub, const Eigen::VectorXi& iq2iv) | ||
142 | : lb(lb), ub(ub), iq2iv_(iq2iv) {} | ||
143 | vector_t lb, ub; | ||
144 | Eigen::VectorXi iq2iv_; | ||
145 | }; | ||
146 | /// \brief Box constraints use a Device joint limits. | ||
147 | struct Device : Base { | ||
148 | /// \todo write a visitor. | ||
149 | bool saturate(vectorIn_t q, vectorOut_t qSat, Eigen::VectorXi& saturation); | ||
150 | 1 | Device() {} | |
151 | 4 | Device(const DevicePtr_t& device) : device(device) {} | |
152 | DevicePtr_t device; | ||
153 | }; | ||
154 | } // namespace saturation | ||
155 | |||
156 | /// Solve a system of non-linear equations on a robot configuration | ||
157 | /// | ||
158 | /// The non-linear system of equations is built by adding equations with | ||
159 | /// method HierarchicalIterative::add. | ||
160 | /// | ||
161 | /// Note that a hierarchy between the equations can be | ||
162 | /// provided. In this case, the solver will try to solve the | ||
163 | /// highest priority equations first, and then to solve the lower priority | ||
164 | /// equations. Note that priorities are in decreasing order: 0 has higher | ||
165 | /// priority than 1. | ||
166 | /// | ||
167 | /// The algorithm used is a Newton-Raphson like algorithm that works as | ||
168 | /// follows: for a single level of priority, let \f$f (\mathbf{q}) = 0\f$ | ||
169 | /// be the system of equations where | ||
170 | /// \f$f\f$ is a \f$C^1\f$ mapping from the robot configuration space to | ||
171 | /// a Lie group space \f$\mathcal{L}\f$. | ||
172 | /// | ||
173 | /// Starting from initial guess \f$\mathbf{q}_0\f$, the method | ||
174 | /// HierarchicalIterative::solve builds a sequence of configurations | ||
175 | /// \f$\mathbf{q}_i\f$ as follows: | ||
176 | /// \f{eqnarray*} | ||
177 | /// \mathbf{q}_{i+1} = \mathbf{q}_i - | ||
178 | /// \alpha_i \frac{\partial f}{\partial \mathbf{q}}(\mathbf{q}_i)^{+} | ||
179 | /// f (\mathbf{q}_i) | ||
180 | /// \f} | ||
181 | /// where | ||
182 | /// \li \f$\frac{\partial f}{\partial \mathbf{q}}(\mathbf{q}_i)^{+}\f$ is | ||
183 | /// the Moore-Penrose pseudo-inverse of the system Jacobian, | ||
184 | /// \li \f$\alpha_i\f$ is a sequence of real numbers depending on the | ||
185 | /// line search strategy. Possible line-search strategies are | ||
186 | /// lineSearch::Constant, lineSearch::Backtracking, | ||
187 | /// lineSearch::FixedSequence, lineSearch::ErrorNormBased. | ||
188 | /// until | ||
189 | /// \li the residual \f$\|f(\mathbf{q})\|\f$ is below an error threshold, or | ||
190 | /// \li the maximal number of iterations has been reached. | ||
191 | /// | ||
192 | /// The computation of the direction of descent in the case of multiple | ||
193 | /// level of hierarchy is described in | ||
194 | /// <a href="https://hal.archives-ouvertes.fr/lirmm-00796736">this paper</a>. | ||
195 | /// | ||
196 | /// The error threshold can be accessed by methods | ||
197 | /// HierarchicalIterative::errorThreshold. The maximal number of | ||
198 | /// iterations can be accessed by methods | ||
199 | /// HierarchicalIterative::maxIterations. | ||
200 | /// | ||
201 | /// \note Solving equations one after the other | ||
202 | /// | ||
203 | /// For some applications, it can be more efficient to solve a set of | ||
204 | /// equations one after the other. In other words, an equation is ignored | ||
205 | /// until the previous one is solved (norm below the threshold). To do so, | ||
206 | /// introduce the equations using method HierarchicalIterative::add with | ||
207 | /// increasing value of \c priority, and call method | ||
208 | /// \c HierarchicalIterative::solveLevelByLevel(true). | ||
209 | /// \note Lie group | ||
210 | /// | ||
211 | /// The unknowns \f$\mathbf{q}\f$ may take values in a more general set | ||
212 | /// than the configuration space of a robot. This set should be a | ||
213 | /// Cartesian product of Lie groups: hpp::pinocchio::LiegroupSpace. | ||
214 | /// | ||
215 | /// \note Saturation | ||
216 | /// | ||
217 | /// \copydoc saturate::Base | ||
218 | /// | ||
219 | /// \note Right hand side and comparison types | ||
220 | /// | ||
221 | /// Instead of \f$f(\mathbf{q}) = 0\f$, other constraints can be defined. | ||
222 | /// Several comparison types are available: | ||
223 | /// \li Equality: \f$f(\mathbf{q}) = rhs\f$, where \f$rhs\f$ is a | ||
224 | /// parameterizable right hand side, | ||
225 | /// \li EqualToZero: \f$f(\mathbf{q}) = 0\f$, | ||
226 | /// \li Superior: \f$f(\mathbf{q}) > 0\f$ | ||
227 | /// \li Inferior: \f$f(\mathbf{q}) < 0\f$ | ||
228 | /// If several constraint are of type equality, the right hand side of the | ||
229 | /// system of equations can be modified by methods | ||
230 | /// HierarchicalIterative::rightHandSideFromInput, | ||
231 | /// HierarchicalIterative::rightHandSide. | ||
232 | /// | ||
233 | /// \note Free variables | ||
234 | /// | ||
235 | /// Some variables can be locked, or computed explicitely. In this case, | ||
236 | /// the iterative resolution will only change the other variables called | ||
237 | /// free variables. \sa methods | ||
238 | /// \li \ref freeVariables (const Indices_t& indices) and | ||
239 | /// \li \ref freeVariables (const Indices_t& indices). | ||
240 | class HPP_CONSTRAINTS_DLLAPI HierarchicalIterative { | ||
241 | public: | ||
242 | typedef Eigen::RowBlockIndices Indices_t; | ||
243 | typedef lineSearch::FixedSequence DefaultLineSearch; | ||
244 | |||
245 | enum Status { ERROR_INCREASED, MAX_ITERATION_REACHED, INFEASIBLE, SUCCESS }; | ||
246 | typedef shared_ptr<saturation::Base> Saturation_t; | ||
247 | |||
248 | HierarchicalIterative(const LiegroupSpacePtr_t& configSpace); | ||
249 | |||
250 | HierarchicalIterative(const HierarchicalIterative& other); | ||
251 | |||
252 | 2076 | virtual ~HierarchicalIterative() {} | |
253 | |||
254 | /// \name Problem definition | ||
255 | /// \{ | ||
256 | |||
257 | /// Get configuration space on which constraints are defined | ||
258 | const LiegroupSpacePtr_t& configSpace() const { return configSpace_; } | ||
259 | /// Check whether a numerical constraint has been added | ||
260 | /// \param numericalConstraint numerical constraint | ||
261 | /// \return true if numerical constraint is already in the solver | ||
262 | virtual bool contains(const ImplicitPtr_t& numericalConstraint) const; | ||
263 | |||
264 | /// Add an implicit constraint | ||
265 | /// | ||
266 | /// \param constraint implicit constraint | ||
267 | /// \param priority level of priority of the constraint: priority are | ||
268 | /// in decreasing order: 0 is the highest priority level, | ||
269 | virtual bool add(const ImplicitPtr_t& constraint, | ||
270 | const std::size_t& priority); | ||
271 | |||
272 | /// add constraints of another solver | ||
273 | /// \param other other solver | ||
274 | /// | ||
275 | /// Add constraints of other to this solver. | ||
276 | /// \note right hand side of other is not copied. | ||
277 | virtual void merge(const HierarchicalIterative& other); | ||
278 | |||
279 | /// Set the saturation function | ||
280 | 18 | void saturation(const Saturation_t& saturate) { saturate_ = saturate; } | |
281 | |||
282 | /// Get the saturation function | ||
283 | const Saturation_t& saturation() const { return saturate_; } | ||
284 | |||
285 | /// \} | ||
286 | |||
287 | /// \name Problem resolution | ||
288 | /// \{ | ||
289 | |||
290 | /// Solve the system of non linear equations | ||
291 | /// | ||
292 | /// \param arg initial guess, | ||
293 | /// \param ls line search method used. | ||
294 | /// | ||
295 | /// Use Newton Rhapson like iterative method until the error is below | ||
296 | /// the threshold, or until the maximal number of iterations has been | ||
297 | /// reached. | ||
298 | /// | ||
299 | /// \note Explicit constraints are expressed in their implicit | ||
300 | /// form: \f$\mathbf{q}_2 = f (\mathbf{q}_1)\f$ is replaced by | ||
301 | /// \f$\mathbf{q}_2 - f (\mathbf{q}_1) = 0\f$, | ||
302 | /// where \f$\mathbf{q}_1\f$ and \f$\mathbf{q}_2\f$ are vectors | ||
303 | /// composed of the components of \f$\mathbf{q}\f$. | ||
304 | template <typename LineSearchType> | ||
305 | Status solve(vectorOut_t arg, LineSearchType ls = LineSearchType()) const; | ||
306 | |||
307 | /// Solve the system of non linear equations | ||
308 | /// | ||
309 | /// \param arg initial guess, | ||
310 | /// | ||
311 | /// Use Newton Rhapson like iterative method until the error is below | ||
312 | /// the threshold, or until the maximal number of iterations has been | ||
313 | /// reached. Use the default line search method (fixed sequence of | ||
314 | /// \f$\alpha_i\f$). | ||
315 | /// | ||
316 | /// \note Explicit constraints are expressed in their implicit | ||
317 | /// form: \f$\mathbf{q}_2 = f (\mathbf{q}_1)\f$ is replaced by | ||
318 | /// \f$\mathbf{q}_2 - f (\mathbf{q}_1) = 0\f$, | ||
319 | /// where \f$\mathbf{q}_1\f$ and \f$\mathbf{q}_2\f$ are vectors | ||
320 | /// composed of the components of \f$\mathbf{q}\f$. | ||
321 | 400 | inline Status solve(vectorOut_t arg) const { | |
322 |
2/4✓ Branch 2 taken 400 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 400 times.
✗ Branch 6 not taken.
|
400 | return solve(arg, DefaultLineSearch()); |
323 | } | ||
324 | |||
325 | /// Whether input vector satisfies the constraints of the solver | ||
326 | /// \param arg input vector | ||
327 | /// Compares to internal error threshold. | ||
328 | 1803 | bool isSatisfied(vectorIn_t arg) const { | |
329 |
1/2✓ Branch 2 taken 1803 times.
✗ Branch 3 not taken.
|
1803 | computeValue<false>(arg); |
330 | 1803 | computeError(); | |
331 | 1803 | return squaredNorm_ < squaredErrorThreshold_; | |
332 | } | ||
333 | |||
334 | /// Whether input vector satisfies the constraints of the solver | ||
335 | /// \param arg input vector | ||
336 | /// \param errorThreshold threshold to use instead of the value | ||
337 | /// stored in the solver. | ||
338 | bool isSatisfied(vectorIn_t arg, value_type errorThreshold) const { | ||
339 | computeValue<false>(arg); | ||
340 | computeError(); | ||
341 | return squaredNorm_ < errorThreshold * errorThreshold; | ||
342 | } | ||
343 | |||
344 | /// Whether a constraint is satisfied for an input vector | ||
345 | /// | ||
346 | /// \param constraint, the constraint in the solver, | ||
347 | /// \param arg the input vector | ||
348 | /// \retval error the error of the constraint. | ||
349 | /// \retval constraintFound whether the constraint belongs to the | ||
350 | /// solver, | ||
351 | /// \return true if constraint belongs to solver and error is below | ||
352 | /// the threshold, false otherwise. | ||
353 | bool isConstraintSatisfied(const ImplicitPtr_t& constraint, vectorIn_t arg, | ||
354 | vectorOut_t error, bool& constraintFound) const; | ||
355 | |||
356 | /// Returns the lowest singular value. | ||
357 | /// If the jacobian has maximum rank r, then it corresponds to r-th | ||
358 | /// greatest singular value. This value is zero when the jacobian is | ||
359 | /// singular. | ||
360 | const value_type& sigma() const { return sigma_; } | ||
361 | |||
362 | /// \} | ||
363 | |||
364 | /// \name Parameters | ||
365 | /// \{ | ||
366 | |||
367 | /// Set free velocity variables | ||
368 | /// | ||
369 | /// The other variables will be left unchanged by the iterative | ||
370 | /// resolution. | ||
371 | /// | ||
372 | /// \param intervals set of index intervals | ||
373 | void freeVariables(const segments_t intervals) { | ||
374 | freeVariables_ = Indices_t(); | ||
375 | for (std::size_t i = 0; i < intervals.size(); ++i) | ||
376 | freeVariables_.addRow(intervals[i].first, intervals[i].second); | ||
377 | freeVariables_.updateIndices<true, true, true>(); | ||
378 | update(); | ||
379 | } | ||
380 | |||
381 | /// Set free velocity variables | ||
382 | /// | ||
383 | /// The other variables will be left unchanged by the iterative | ||
384 | /// resolution. | ||
385 | 29 | void freeVariables(const Indices_t& indices) { | |
386 | 29 | freeVariables_ = indices; | |
387 | 29 | update(); | |
388 | 29 | } | |
389 | |||
390 | /// Get free velocity variables | ||
391 | const Indices_t& freeVariables() const { return freeVariables_; } | ||
392 | |||
393 | /// Set maximal number of iterations | ||
394 | 1022 | void maxIterations(size_type iterations) { maxIterations_ = iterations; } | |
395 | /// Get maximal number of iterations in config projector | ||
396 | 3 | size_type maxIterations() const { return maxIterations_; } | |
397 | |||
398 | /// Set error threshold | ||
399 | 1024 | void errorThreshold(const value_type& threshold) { | |
400 | 1024 | squaredErrorThreshold_ = threshold * threshold; | |
401 | 1024 | } | |
402 | /// Get error threshold | ||
403 | 417 | value_type errorThreshold() const { return sqrt(squaredErrorThreshold_); } | |
404 | /// Get error threshold | ||
405 | 48 | value_type squaredErrorThreshold() const { return squaredErrorThreshold_; } | |
406 | |||
407 | /// Get the inequality threshold | ||
408 | value_type inequalityThreshold() const { return inequalityThreshold_; } | ||
409 | /// set the inequality threshold | ||
410 | 2 | void inequalityThreshold(const value_type& it) { inequalityThreshold_ = it; } | |
411 | |||
412 | 13 | void lastIsOptional(bool optional) { lastIsOptional_ = optional; } | |
413 | |||
414 | bool lastIsOptional() const { return lastIsOptional_; } | ||
415 | |||
416 | // Set whether the constraints are solved level by level | ||
417 | // | ||
418 | // If true, the solver will consider only the higher level of | ||
419 | // priority that is not satisfied when computing the direction | ||
420 | // of descent. | ||
421 | void solveLevelByLevel(bool solveLevelByLevel) { | ||
422 | solveLevelByLevel_ = solveLevelByLevel; | ||
423 | } | ||
424 | |||
425 | // Get whether the constraints are solved level by level | ||
426 | bool solveLevelByLevel() const { return solveLevelByLevel_; } | ||
427 | /// \} | ||
428 | |||
429 | /// \name Stack | ||
430 | /// \{ | ||
431 | |||
432 | /// Get set of constraints for a give priority level. | ||
433 | const ImplicitConstraintSet& constraints(const std::size_t priority) { | ||
434 | assert(priority < stacks_.size()); | ||
435 | return stacks_[priority]; | ||
436 | } | ||
437 | |||
438 | /// Get constraints (implicit and explicit) | ||
439 | 21 | const NumericalConstraints_t& constraints() const { return constraints_; } | |
440 | |||
441 | 9 | std::size_t numberStacks() const { return stacks_.size(); } | |
442 | |||
443 | 2020 | const size_type& dimension() const { return dimension_; } | |
444 | |||
445 | /// Dimension of the problem after removing the rows of the jacobian | ||
446 | /// which do not influence the error (only zeros along those lines). | ||
447 | 6 | const size_type& reducedDimension() const { return reducedDimension_; } | |
448 | |||
449 | /// Configuration parameters involved in the constraint resolution. | ||
450 | ArrayXb activeParameters() const; | ||
451 | |||
452 | /// Velocity parameters involved in the constraint resolution. | ||
453 | ArrayXb activeDerivativeParameters() const; | ||
454 | |||
455 | /// \} | ||
456 | |||
457 | /// Returns the squared norm of the error vector | ||
458 | 520 | value_type residualError() const { return squaredNorm_; } | |
459 | |||
460 | /// Returns the error vector | ||
461 | void residualError(vectorOut_t error) const; | ||
462 | |||
463 | /// Inclusion of manifolds | ||
464 | /// | ||
465 | /// \param solver another solver | ||
466 | /// | ||
467 | /// This function returns true if the solution manifold of the solver | ||
468 | /// given as input is a submanifold of the solution manifold of this | ||
469 | /// solver. The function tests whether constraints of the input solver | ||
470 | /// are also in this solver. | ||
471 | bool definesSubmanifoldOf(const HierarchicalIterative& solver) const; | ||
472 | |||
473 | /// \name Right hand side accessors | ||
474 | /// \{ | ||
475 | |||
476 | /// Compute right hand side of equality constraints from a configuration | ||
477 | /// \param config a configuration. | ||
478 | /// | ||
479 | /// for each constraint of type Equality, set right hand side as | ||
480 | /// \f$rhs = f(\mathbf{q})\f$. | ||
481 | /// \note Only parameterizable constraints (type Equality) are set | ||
482 | vector_t rightHandSideFromConfig(ConfigurationIn_t config); | ||
483 | |||
484 | /// Compute right hand side of a constraint from a configuration | ||
485 | /// \param constraint the constraint, | ||
486 | /// \param config a configuration. | ||
487 | /// | ||
488 | /// Set right hand side as \f$rhs = f(\mathbf{q})\f$. | ||
489 | /// \note Only parameterizable constraints (type Equality) are set | ||
490 | virtual bool rightHandSideFromConfig(const ImplicitPtr_t& constraint, | ||
491 | ConfigurationIn_t config); | ||
492 | /// Set right hand side of a constraints | ||
493 | /// \param constraint the constraint, | ||
494 | /// \param rhs right hand side. | ||
495 | /// \note Size of rhs should be equal to the total dimension of | ||
496 | /// parameterizable constraints (type Equality) . | ||
497 | virtual bool rightHandSide(const ImplicitPtr_t& constraint, vectorIn_t rhs); | ||
498 | |||
499 | /// Get right hand side of a constraints | ||
500 | virtual bool getRightHandSide(const ImplicitPtr_t& constraint, | ||
501 | vectorOut_t rhs) const; | ||
502 | |||
503 | /// Set the right hand side | ||
504 | /// \param rhs the right hand side | ||
505 | /// \note Size of rhs should be equal to the total dimension of | ||
506 | /// parameterizable constraints (type Equality). | ||
507 | virtual void rightHandSide(vectorIn_t rhs); | ||
508 | |||
509 | /// Set the right hand side at a given parameter. | ||
510 | /// \param s parameter passed to Implicit::rightHandSideAt | ||
511 | void rightHandSideAt(const value_type& s); | ||
512 | |||
513 | /// Get the right hand side | ||
514 | /// \return the right hand side | ||
515 | /// \note size of result is equal to total dimension of parameterizable | ||
516 | /// constraints (type Equality). | ||
517 | vector_t rightHandSide() const; | ||
518 | |||
519 | /// Get size of the right hand side | ||
520 | /// \return sum of dimensions of parameterizable constraints | ||
521 | /// (type Equality) | ||
522 | size_type rightHandSideSize() const; | ||
523 | |||
524 | /// \} | ||
525 | |||
526 | /// \name Access to internal data | ||
527 | /// You should know what you do when you call these functions | ||
528 | /// \{ | ||
529 | |||
530 | /// Compute the value of each level, and the jacobian if ComputeJac is true. | ||
531 | template <bool ComputeJac> | ||
532 | void computeValue(vectorIn_t arg) const; | ||
533 | void computeSaturation(vectorIn_t arg) const; | ||
534 | void getValue(vectorOut_t v) const; | ||
535 | void getReducedJacobian(matrixOut_t J) const; | ||
536 | /// If lastIsOptional() is true, then the last level is ignored. | ||
537 | /// \warning computeValue must have been called first. | ||
538 | void computeError() const; | ||
539 | |||
540 | /// Accessor to the last step done | ||
541 | const vector_t& lastStep() const { return dq_; } | ||
542 | |||
543 | virtual bool integrate(vectorIn_t from, vectorIn_t velocity, | ||
544 | vectorOut_t result) const; | ||
545 | /// \} | ||
546 | |||
547 | virtual std::ostream& print(std::ostream& os) const; | ||
548 | |||
549 | protected: | ||
550 | typedef Eigen::JacobiSVD<matrix_t> SVD_t; | ||
551 | |||
552 | struct Data { | ||
553 | /// \cond | ||
554 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW | ||
555 | /// \endcond | ||
556 | LiegroupElement output, rightHandSide; | ||
557 | vector_t error; | ||
558 | matrix_t jacobian, reducedJ; | ||
559 | |||
560 | SVD_t svd; | ||
561 | matrix_t PK; | ||
562 | |||
563 | mutable size_type maxRank; | ||
564 | |||
565 | ComparisonTypes_t comparison; | ||
566 | std::vector<std::size_t> inequalityIndices; | ||
567 | Eigen::RowBlockIndices equalityIndices; | ||
568 | Eigen::MatrixBlocks<false, false> activeRowsOfJ; | ||
569 | }; | ||
570 | |||
571 | /// Allocate datas and update sizes of the problem | ||
572 | /// Should be called whenever the stack is modified. | ||
573 | void update(); | ||
574 | |||
575 | /// Compute which rows of the jacobian of stack_[iStack] | ||
576 | /// are not zero, using the activeDerivativeParameters of the functions. | ||
577 | /// The result is stored in datas_[i].activeRowsOfJ | ||
578 | virtual void computeActiveRowsOfJ(std::size_t iStack); | ||
579 | |||
580 | /// Compute a SVD decomposition of each level and find the best descent | ||
581 | /// direction at the first order. | ||
582 | /// Linearization of the system of equations | ||
583 | /// rhs - v_{i} = J (q_i) (dq_{i+1} - q_{i}) | ||
584 | /// q_{i+1} - q_{i} = J(q_i)^{+} ( rhs - v_{i} ) | ||
585 | /// dq = J(q_i)^{+} ( rhs - v_{i} ) | ||
586 | /// \warning computeValue<true> must have been called first. | ||
587 | void computeDescentDirection() const; | ||
588 | void expandDqSmall() const; | ||
589 | void saturate(vectorOut_t arg) const; | ||
590 | |||
591 | value_type squaredErrorThreshold_, inequalityThreshold_; | ||
592 | size_type maxIterations_; | ||
593 | |||
594 | std::vector<ImplicitConstraintSet> stacks_; | ||
595 | LiegroupSpacePtr_t configSpace_; | ||
596 | size_type dimension_, reducedDimension_; | ||
597 | bool lastIsOptional_; | ||
598 | bool solveLevelByLevel_; | ||
599 | /// Unknown of the set of implicit constraints | ||
600 | Indices_t freeVariables_; | ||
601 | Saturation_t saturate_; | ||
602 | /// Members moved from core::ConfigProjector | ||
603 | NumericalConstraints_t constraints_; | ||
604 | /// Value rank of constraint in its priority level | ||
605 | std::map<DifferentiableFunctionPtr_t, size_type> iq_; | ||
606 | /// Derivative rank of constraint in its priority level | ||
607 | std::map<DifferentiableFunctionPtr_t, size_type> iv_; | ||
608 | /// Priority level of constraint | ||
609 | std::map<DifferentiableFunctionPtr_t, std::size_t> priority_; | ||
610 | |||
611 | /// The smallest non-zero singular value | ||
612 | mutable value_type sigma_; | ||
613 | |||
614 | mutable vector_t dq_, dqSmall_; | ||
615 | mutable matrix_t reducedJ_; | ||
616 | mutable Eigen::VectorXi saturation_, reducedSaturation_; | ||
617 | mutable Configuration_t qSat_; | ||
618 | mutable ArrayXb tmpSat_; | ||
619 | mutable value_type squaredNorm_; | ||
620 | mutable std::vector<Data> datas_; | ||
621 | mutable SVD_t svd_; | ||
622 | mutable vector_t OM_; | ||
623 | mutable vector_t OP_; | ||
624 | |||
625 | friend struct lineSearch::Backtracking; | ||
626 | |||
627 | protected: | ||
628 | ✗ | HierarchicalIterative() {} | |
629 | |||
630 | private: | ||
631 | 4 | HPP_SERIALIZABLE_SPLIT(); | |
632 | }; // class HierarchicalIterative | ||
633 | /// \} | ||
634 | 3 | inline std::ostream& operator<<(std::ostream& os, | |
635 | const HierarchicalIterative& hs) { | ||
636 | 3 | return hs.print(os); | |
637 | } | ||
638 | } // namespace solver | ||
639 | } // namespace constraints | ||
640 | } // namespace hpp | ||
641 | |||
642 | #endif // HPP_CONSTRAINTS_SOLVER_HIERARCHICAL_ITERATIVE_HH | ||
643 |