| 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 |