| Directory: | ./ |
|---|---|
| File: | include/hpp/constraints/solver/impl/hierarchical-iterative.hh |
| Date: | 2025-05-05 12:19:30 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 72 | 73 | 98.6% |
| Branches: | 41 | 74 | 55.4% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2017, Joseph Mirabel | ||
| 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_IMPL_HIERARCHICAL_ITERATIVE_HH | ||
| 30 | #define HPP_CONSTRAINTS_SOLVER_IMPL_HIERARCHICAL_ITERATIVE_HH | ||
| 31 | |||
| 32 | #include <hpp/constraints/config.hh> | ||
| 33 | #include <hpp/constraints/svd.hh> | ||
| 34 | #include <hpp/util/debug.hh> | ||
| 35 | |||
| 36 | namespace hpp { | ||
| 37 | namespace constraints { | ||
| 38 | namespace solver { | ||
| 39 | namespace lineSearch { | ||
| 40 | template <typename SolverType> | ||
| 41 | 34 | inline bool Constant::operator()(const SolverType& solver, vectorOut_t arg, | |
| 42 | vectorOut_t darg) { | ||
| 43 |
3/6✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 34 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 34 times.
✗ Branch 9 not taken.
|
34 | solver.integrate(arg, darg, arg); |
| 44 | 34 | return true; | |
| 45 | } | ||
| 46 | |||
| 47 | template <typename SolverType> | ||
| 48 | 224 | inline bool Backtracking::operator()(const SolverType& solver, vectorOut_t arg, | |
| 49 | vectorOut_t u) { | ||
| 50 | 224 | arg_darg.resize(arg.size()); | |
| 51 | |||
| 52 | 224 | const value_type slope = computeLocalSlope(solver); | |
| 53 | 224 | const value_type t = 2 * c * slope; | |
| 54 | 224 | const value_type f_arg_norm2 = solver.residualError(); | |
| 55 | |||
| 56 |
1/2✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
|
224 | if (t > 0) { |
| 57 | hppDout(error, "The descent direction is not valid: " << t / c); | ||
| 58 | } else { | ||
| 59 | 224 | value_type alpha = 1; | |
| 60 | /* TODO add a threshold to avoid too large steps. | ||
| 61 | const value_type u2 = u.squaredNorm(); | ||
| 62 | if (u2 > 1.) alpha = 1. / std::sqrt(u2); | ||
| 63 | */ | ||
| 64 | |||
| 65 |
2/2✓ Branch 0 taken 248 times.
✓ Branch 1 taken 6 times.
|
254 | while (alpha > smallAlpha) { |
| 66 |
2/4✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 248 times.
✗ Branch 5 not taken.
|
248 | darg = alpha * u; |
| 67 |
4/8✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 248 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 248 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 248 times.
✗ Branch 11 not taken.
|
248 | solver.integrate(arg, darg, arg_darg); |
| 68 |
2/4✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 248 times.
✗ Branch 5 not taken.
|
248 | solver.template computeValue<false>(arg_darg); |
| 69 |
1/2✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
|
248 | solver.computeError(); |
| 70 | // Check if we are doing better than the linear approximation with coef | ||
| 71 | // multiplied by c < 1 | ||
| 72 | // t < 0 must hold | ||
| 73 | 248 | const value_type f_arg_darg_norm2 = solver.residualError(); | |
| 74 |
2/2✓ Branch 0 taken 218 times.
✓ Branch 1 taken 30 times.
|
248 | if (f_arg_norm2 - f_arg_darg_norm2 >= -alpha * t) { |
| 75 |
1/2✓ Branch 1 taken 218 times.
✗ Branch 2 not taken.
|
218 | arg = arg_darg; |
| 76 |
1/2✓ Branch 1 taken 218 times.
✗ Branch 2 not taken.
|
218 | u = darg; |
| 77 | 218 | return true; | |
| 78 | } | ||
| 79 | // Prepare next step | ||
| 80 | 30 | alpha *= tau; | |
| 81 | } | ||
| 82 | hppDout(error, "Could find alpha such that ||f(q)||**2 + " | ||
| 83 | << c | ||
| 84 | << " * 2*(f(q)^T * J * dq) is doing worse than " | ||
| 85 | "||f(q + alpha * dq)||**2"); | ||
| 86 | } | ||
| 87 | |||
| 88 | 6 | u *= smallAlpha; | |
| 89 |
3/6✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
|
6 | solver.integrate(arg, u, arg); |
| 90 | 6 | return false; | |
| 91 | } | ||
| 92 | |||
| 93 | template <typename SolverType> | ||
| 94 | 224 | inline value_type Backtracking::computeLocalSlope( | |
| 95 | const SolverType& solver) const { | ||
| 96 | 224 | value_type slope = 0; | |
| 97 |
2/2✓ Branch 1 taken 341 times.
✓ Branch 2 taken 224 times.
|
565 | for (std::size_t i = 0; i < solver.stacks_.size(); ++i) { |
| 98 | 341 | typename SolverType::Data& d = solver.datas_[i]; | |
| 99 | 341 | const size_type nrows = d.reducedJ.rows(); | |
| 100 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 326 times.
|
341 | if (df.size() < nrows) df.resize(nrows); |
| 101 |
3/6✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 341 times.
✗ Branch 9 not taken.
|
341 | df.head(nrows).noalias() = d.reducedJ * solver.dqSmall_; |
| 102 | 341 | slope += | |
| 103 |
4/8✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 341 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 341 times.
✗ Branch 12 not taken.
|
341 | df.head(nrows).dot(d.activeRowsOfJ.keepRows().rview(d.error).eval()); |
| 104 | } | ||
| 105 | 224 | return slope; | |
| 106 | } | ||
| 107 | |||
| 108 | template <typename SolverType> | ||
| 109 | 21589 | inline bool FixedSequence::operator()(const SolverType& solver, vectorOut_t arg, | |
| 110 | vectorOut_t darg) { | ||
| 111 | 21589 | darg *= alpha; | |
| 112 | 21589 | alpha = alphaMax - K * (alphaMax - alpha); | |
| 113 |
3/6✓ Branch 2 taken 21589 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 21589 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 21589 times.
✗ Branch 9 not taken.
|
21589 | solver.integrate(arg, darg, arg); |
| 114 | 21589 | return true; | |
| 115 | } | ||
| 116 | |||
| 117 | template <typename SolverType> | ||
| 118 | 48 | inline bool ErrorNormBased::operator()(const SolverType& solver, | |
| 119 | vectorOut_t arg, vectorOut_t darg) { | ||
| 120 | 48 | const value_type r = solver.residualError() / solver.squaredErrorThreshold(); | |
| 121 | 48 | const value_type alpha = C - K * std::tanh(a * r + b); | |
| 122 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | darg *= alpha; |
| 123 |
4/8✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 48 times.
✗ Branch 11 not taken.
|
48 | solver.integrate(arg, darg, arg); |
| 124 | 48 | return true; | |
| 125 | } | ||
| 126 | } // namespace lineSearch | ||
| 127 | |||
| 128 | template <typename LineSearchType> | ||
| 129 | inline solver::HierarchicalIterative::Status | ||
| 130 | 440 | solver::HierarchicalIterative::solve(vectorOut_t arg, | |
| 131 | LineSearchType lineSearch) const { | ||
| 132 | hppDout(info, "before projection: " << arg.transpose()); | ||
| 133 | 440 | assert(!arg.hasNaN()); | |
| 134 | |||
| 135 | 440 | size_type errorDecreased = 3, iter = 0; | |
| 136 | 440 | value_type previousSquaredNorm = std::numeric_limits<value_type>::infinity(); | |
| 137 | static const value_type dqMinSquaredNorm = | ||
| 138 | Eigen::NumTraits<value_type>::dummy_precision(); | ||
| 139 | |||
| 140 | // Fill value and Jacobian | ||
| 141 | 440 | computeValue<true>(arg); | |
| 142 | 440 | computeError(); | |
| 143 | |||
| 144 | 440 | if (squaredNorm_ > squaredErrorThreshold_ && reducedDimension_ == 0) | |
| 145 | ✗ | return INFEASIBLE; | |
| 146 | |||
| 147 | Status status; | ||
| 148 | 8945 | while (squaredNorm_ > squaredErrorThreshold_ && errorDecreased && | |
| 149 | 8892 | iter < maxIterations_) { | |
| 150 | 8748 | computeSaturation(arg); | |
| 151 | 8748 | computeDescentDirection(); | |
| 152 | 8748 | if (dq_.squaredNorm() < dqMinSquaredNorm) { | |
| 153 | // TODO INFEASIBLE means that we have reached a local minima. | ||
| 154 | // The problem may still be feasible from a different starting point. | ||
| 155 | 243 | status = INFEASIBLE; | |
| 156 | 243 | break; | |
| 157 | } | ||
| 158 | 8505 | lineSearch(*this, arg, dq_); | |
| 159 | |||
| 160 | 8505 | computeValue<true>(arg); | |
| 161 | 8505 | computeError(); | |
| 162 | |||
| 163 | hppDout(info, "squareNorm = " << squaredNorm_); | ||
| 164 | 8505 | --errorDecreased; | |
| 165 | 8505 | if (squaredNorm_ < previousSquaredNorm) | |
| 166 | 6050 | errorDecreased = 3; | |
| 167 | else | ||
| 168 | 2455 | status = ERROR_INCREASED; | |
| 169 | 8505 | previousSquaredNorm = squaredNorm_; | |
| 170 | 8505 | ++iter; | |
| 171 | } | ||
| 172 | |||
| 173 | hppDout(info, "number of iterations: " << iter); | ||
| 174 | 440 | if (squaredNorm_ > squaredErrorThreshold_) { | |
| 175 | hppDout(info, "Projection failed."); | ||
| 176 | 396 | return (iter >= maxIterations_) ? MAX_ITERATION_REACHED : status; | |
| 177 | } | ||
| 178 | hppDout(info, "After projection: " << arg.transpose()); | ||
| 179 | 44 | assert(!arg.hasNaN()); | |
| 180 | 44 | return SUCCESS; | |
| 181 | } | ||
| 182 | } // namespace solver | ||
| 183 | } // namespace constraints | ||
| 184 | } // namespace hpp | ||
| 185 | |||
| 186 | #endif // HPP_CONSTRAINTS_SOLVER_IMPL_HIERARCHICAL_ITERATIVE_HH | ||
| 187 |