Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 1 | // Ceres Solver - A fast non-linear least squares minimizer |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 2 | // Copyright 2021 Google Inc. All rights reserved. |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 3 | // http://ceres-solver.org/ |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are met: |
| 7 | // |
| 8 | // * Redistributions of source code must retain the above copyright notice, |
| 9 | // this list of conditions and the following disclaimer. |
| 10 | // * Redistributions in binary form must reproduce the above copyright notice, |
| 11 | // this list of conditions and the following disclaimer in the documentation |
| 12 | // and/or other materials provided with the distribution. |
| 13 | // * Neither the name of Google Inc. nor the names of its contributors may be |
| 14 | // used to endorse or promote products derived from this software without |
| 15 | // specific prior written permission. |
| 16 | // |
| 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 27 | // POSSIBILITY OF SUCH DAMAGE. |
| 28 | // |
| 29 | // Author: mierle@gmail.com (Keir Mierle) |
| 30 | // |
| 31 | // WARNING WARNING WARNING |
| 32 | // WARNING WARNING WARNING Tiny solver is experimental and will change. |
| 33 | // WARNING WARNING WARNING |
| 34 | // |
| 35 | // A tiny least squares solver using Levenberg-Marquardt, intended for solving |
| 36 | // small dense problems with low latency and low overhead. The implementation |
| 37 | // takes care to do all allocation up front, so that no memory is allocated |
| 38 | // during solving. This is especially useful when solving many similar problems; |
| 39 | // for example, inverse pixel distortion for every pixel on a grid. |
| 40 | // |
Kuang Fangjun | 0d3a84f | 2018-09-22 11:53:42 +0800 | [diff] [blame] | 41 | // Note: This code has no dependencies beyond Eigen, including on other parts of |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 42 | // Ceres, so it is possible to take this file alone and put it in another |
| 43 | // project without the rest of Ceres. |
| 44 | // |
| 45 | // Algorithm based off of: |
| 46 | // |
| 47 | // [1] K. Madsen, H. Nielsen, O. Tingleoff. |
| 48 | // Methods for Non-linear Least Squares Problems. |
| 49 | // http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf |
| 50 | |
| 51 | #ifndef CERES_PUBLIC_TINY_SOLVER_H_ |
| 52 | #define CERES_PUBLIC_TINY_SOLVER_H_ |
| 53 | |
| 54 | #include <cassert> |
| 55 | #include <cmath> |
| 56 | |
Sameer Agarwal | cc0bd49 | 2017-10-14 09:41:20 -0700 | [diff] [blame] | 57 | #include "Eigen/Dense" |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 58 | |
| 59 | namespace ceres { |
| 60 | |
| 61 | // To use tiny solver, create a class or struct that allows computing the cost |
| 62 | // function (described below). This is similar to a ceres::CostFunction, but is |
Kuang Fangjun | 0d3a84f | 2018-09-22 11:53:42 +0800 | [diff] [blame] | 63 | // different to enable statically allocating all memory for the solver |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 64 | // (specifically, enum sizes). Key parts are the Scalar typedef, the enums to |
| 65 | // describe problem sizes (needed to remove all heap allocations), and the |
| 66 | // operator() overload to evaluate the cost and (optionally) jacobians. |
| 67 | // |
| 68 | // struct TinySolverCostFunctionTraits { |
| 69 | // typedef double Scalar; |
| 70 | // enum { |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 71 | // NUM_RESIDUALS = <int> OR Eigen::Dynamic, |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 72 | // NUM_PARAMETERS = <int> OR Eigen::Dynamic, |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 73 | // }; |
| 74 | // bool operator()(const double* parameters, |
| 75 | // double* residuals, |
| 76 | // double* jacobian) const; |
| 77 | // |
Kuang Fangjun | 0d3a84f | 2018-09-22 11:53:42 +0800 | [diff] [blame] | 78 | // int NumResiduals() const; -- Needed if NUM_RESIDUALS == Eigen::Dynamic. |
| 79 | // int NumParameters() const; -- Needed if NUM_PARAMETERS == Eigen::Dynamic. |
| 80 | // }; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 81 | // |
| 82 | // For operator(), the size of the objects is: |
| 83 | // |
| 84 | // double* parameters -- NUM_PARAMETERS or NumParameters() |
| 85 | // double* residuals -- NUM_RESIDUALS or NumResiduals() |
| 86 | // double* jacobian -- NUM_RESIDUALS * NUM_PARAMETERS in column-major format |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 87 | // (Eigen's default); or nullptr if no jacobian |
| 88 | // requested. |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 89 | // |
| 90 | // An example (fully statically sized): |
| 91 | // |
| 92 | // struct MyCostFunctionExample { |
| 93 | // typedef double Scalar; |
| 94 | // enum { |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 95 | // NUM_RESIDUALS = 2, |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 96 | // NUM_PARAMETERS = 3, |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 97 | // }; |
| 98 | // bool operator()(const double* parameters, |
| 99 | // double* residuals, |
| 100 | // double* jacobian) const { |
| 101 | // residuals[0] = x + 2*y + 4*z; |
| 102 | // residuals[1] = y * z; |
| 103 | // if (jacobian) { |
| 104 | // jacobian[0 * 2 + 0] = 1; // First column (x). |
| 105 | // jacobian[0 * 2 + 1] = 0; |
| 106 | // |
| 107 | // jacobian[1 * 2 + 0] = 2; // Second column (y). |
| 108 | // jacobian[1 * 2 + 1] = z; |
| 109 | // |
| 110 | // jacobian[2 * 2 + 0] = 4; // Third column (z). |
| 111 | // jacobian[2 * 2 + 1] = y; |
| 112 | // } |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 113 | // return true; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 114 | // } |
| 115 | // }; |
| 116 | // |
| 117 | // The solver supports either statically or dynamically sized cost |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 118 | // functions. If the number of residuals is dynamic then the Function |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 119 | // must define: |
| 120 | // |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 121 | // int NumResiduals() const; |
| 122 | // |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 123 | // If the number of parameters is dynamic then the Function must |
| 124 | // define: |
| 125 | // |
| 126 | // int NumParameters() const; |
| 127 | // |
Sameer Agarwal | 4362a21 | 2019-12-02 13:52:31 -0800 | [diff] [blame] | 128 | template <typename Function, |
| 129 | typename LinearSolver = |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 130 | Eigen::LDLT<Eigen::Matrix<typename Function::Scalar, // |
| 131 | Function::NUM_PARAMETERS, // |
Sameer Agarwal | 4362a21 | 2019-12-02 13:52:31 -0800 | [diff] [blame] | 132 | Function::NUM_PARAMETERS>>> |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 133 | class TinySolver { |
| 134 | public: |
Johannes Beck | 939253c | 2019-04-14 20:31:47 +0200 | [diff] [blame] | 135 | // This class needs to have an Eigen aligned operator new as it contains |
| 136 | // fixed-size Eigen types. |
| 137 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW |
| 138 | |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 139 | enum { |
| 140 | NUM_RESIDUALS = Function::NUM_RESIDUALS, |
| 141 | NUM_PARAMETERS = Function::NUM_PARAMETERS |
| 142 | }; |
| 143 | typedef typename Function::Scalar Scalar; |
| 144 | typedef typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1> Parameters; |
| 145 | |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 146 | enum Status { |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 147 | // max_norm |J'(x) * f(x)| < gradient_tolerance |
| 148 | GRADIENT_TOO_SMALL, |
| 149 | // ||dx|| <= parameter_tolerance * (||x|| + parameter_tolerance) |
| 150 | RELATIVE_STEP_SIZE_TOO_SMALL, |
| 151 | // cost_threshold > ||f(x)||^2 / 2 |
| 152 | COST_TOO_SMALL, |
| 153 | // num_iterations >= max_num_iterations |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 154 | HIT_MAX_ITERATIONS, |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 155 | // (new_cost - old_cost) < function_tolerance * old_cost |
| 156 | COST_CHANGE_TOO_SMALL, |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 157 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 158 | // TODO(sameeragarwal): Deal with numerical failures. |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 159 | }; |
| 160 | |
Sameer Agarwal | cc0bd49 | 2017-10-14 09:41:20 -0700 | [diff] [blame] | 161 | struct Options { |
Sameer Agarwal | 9814a91 | 2018-04-06 08:38:11 -0700 | [diff] [blame] | 162 | int max_num_iterations = 50; |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 163 | |
| 164 | // max_norm |J'(x) * f(x)| < gradient_tolerance |
| 165 | Scalar gradient_tolerance = 1e-10; |
| 166 | |
| 167 | // ||dx|| <= parameter_tolerance * (||x|| + parameter_tolerance) |
| 168 | Scalar parameter_tolerance = 1e-8; |
| 169 | |
| 170 | // (new_cost - old_cost) < function_tolerance * old_cost |
| 171 | Scalar function_tolerance = 1e-6; |
| 172 | |
| 173 | // cost_threshold > ||f(x)||^2 / 2 |
| 174 | Scalar cost_threshold = std::numeric_limits<Scalar>::epsilon(); |
| 175 | |
| 176 | Scalar initial_trust_region_radius = 1e4; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 177 | }; |
| 178 | |
Sameer Agarwal | cc0bd49 | 2017-10-14 09:41:20 -0700 | [diff] [blame] | 179 | struct Summary { |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 180 | // 1/2 ||f(x_0)||^2 |
| 181 | Scalar initial_cost = -1; |
| 182 | // 1/2 ||f(x)||^2 |
| 183 | Scalar final_cost = -1; |
| 184 | // max_norm(J'f(x)) |
| 185 | Scalar gradient_max_norm = -1; |
Sameer Agarwal | 9814a91 | 2018-04-06 08:38:11 -0700 | [diff] [blame] | 186 | int iterations = -1; |
| 187 | Status status = HIT_MAX_ITERATIONS; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 188 | }; |
| 189 | |
Sameer Agarwal | 4362a21 | 2019-12-02 13:52:31 -0800 | [diff] [blame] | 190 | bool Update(const Function& function, const Parameters& x) { |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 191 | if (!function(x.data(), residuals_.data(), jacobian_.data())) { |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 192 | return false; |
| 193 | } |
| 194 | |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 195 | residuals_ = -residuals_; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 196 | |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 197 | // On the first iteration, compute a diagonal (Jacobi) scaling |
| 198 | // matrix, which we store as a vector. |
| 199 | if (summary.iterations == 0) { |
| 200 | // jacobi_scaling = 1 / (1 + diagonal(J'J)) |
| 201 | // |
| 202 | // 1 is added to the denominator to regularize small diagonal |
| 203 | // entries. |
| 204 | jacobi_scaling_ = 1.0 / (1.0 + jacobian_.colwise().norm().array()); |
| 205 | } |
| 206 | |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 207 | // This explicitly computes the normal equations, which is numerically |
| 208 | // unstable. Nevertheless, it is often good enough and is fast. |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 209 | // |
| 210 | // TODO(sameeragarwal): Refactor this to allow for DenseQR |
| 211 | // factorization. |
| 212 | jacobian_ = jacobian_ * jacobi_scaling_.asDiagonal(); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 213 | jtj_ = jacobian_.transpose() * jacobian_; |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 214 | g_ = jacobian_.transpose() * residuals_; |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 215 | summary.gradient_max_norm = g_.array().abs().maxCoeff(); |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 216 | cost_ = residuals_.squaredNorm() / 2; |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 217 | return true; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 218 | } |
| 219 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 220 | const Summary& Solve(const Function& function, Parameters* x_and_min) { |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 221 | Initialize<NUM_RESIDUALS, NUM_PARAMETERS>(function); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 222 | assert(x_and_min); |
| 223 | Parameters& x = *x_and_min; |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 224 | summary = Summary(); |
Sameer Agarwal | 9814a91 | 2018-04-06 08:38:11 -0700 | [diff] [blame] | 225 | summary.iterations = 0; |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 226 | |
| 227 | // TODO(sameeragarwal): Deal with failure here. |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 228 | Update(function, x); |
| 229 | summary.initial_cost = cost_; |
| 230 | summary.final_cost = cost_; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 231 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 232 | if (summary.gradient_max_norm < options.gradient_tolerance) { |
| 233 | summary.status = GRADIENT_TOO_SMALL; |
| 234 | return summary; |
| 235 | } |
| 236 | |
| 237 | if (cost_ < options.cost_threshold) { |
| 238 | summary.status = COST_TOO_SMALL; |
| 239 | return summary; |
| 240 | } |
| 241 | |
| 242 | Scalar u = 1.0 / options.initial_trust_region_radius; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 243 | Scalar v = 2; |
| 244 | |
Keir Mierle | dc9bf01 | 2017-10-19 23:46:49 -0700 | [diff] [blame] | 245 | for (summary.iterations = 1; |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 246 | summary.iterations < options.max_num_iterations; |
Keir Mierle | dc9bf01 | 2017-10-19 23:46:49 -0700 | [diff] [blame] | 247 | summary.iterations++) { |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 248 | jtj_regularized_ = jtj_; |
| 249 | const Scalar min_diagonal = 1e-6; |
| 250 | const Scalar max_diagonal = 1e32; |
Keir Mierle | dc9bf01 | 2017-10-19 23:46:49 -0700 | [diff] [blame] | 251 | for (int i = 0; i < lm_diagonal_.rows(); ++i) { |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 252 | lm_diagonal_[i] = std::sqrt( |
| 253 | u * std::min(std::max(jtj_(i, i), min_diagonal), max_diagonal)); |
| 254 | jtj_regularized_(i, i) += lm_diagonal_[i] * lm_diagonal_[i]; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 255 | } |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 256 | |
| 257 | // TODO(sameeragarwal): Check for failure and deal with it. |
| 258 | linear_solver_.compute(jtj_regularized_); |
| 259 | lm_step_ = linear_solver_.solve(g_); |
| 260 | dx_ = jacobi_scaling_.asDiagonal() * lm_step_; |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 261 | |
| 262 | // Adding parameter_tolerance to x.norm() ensures that this |
| 263 | // works if x is near zero. |
| 264 | const Scalar parameter_tolerance = |
| 265 | options.parameter_tolerance * |
| 266 | (x.norm() + options.parameter_tolerance); |
| 267 | if (dx_.norm() < parameter_tolerance) { |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 268 | summary.status = RELATIVE_STEP_SIZE_TOO_SMALL; |
| 269 | break; |
| 270 | } |
| 271 | x_new_ = x + dx_; |
| 272 | |
| 273 | // TODO(keir): Add proper handling of errors from user eval of cost |
| 274 | // functions. |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 275 | function(&x_new_[0], &f_x_new_[0], nullptr); |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 276 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 277 | const Scalar cost_change = (2 * cost_ - f_x_new_.squaredNorm()); |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 278 | // TODO(sameeragarwal): Better more numerically stable evaluation. |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 279 | const Scalar model_cost_change = lm_step_.dot(2 * g_ - jtj_ * lm_step_); |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 280 | |
| 281 | // rho is the ratio of the actual reduction in error to the reduction |
| 282 | // in error that would be obtained if the problem was linear. See [1] |
| 283 | // for details. |
| 284 | Scalar rho(cost_change / model_cost_change); |
| 285 | if (rho > 0) { |
| 286 | // Accept the Levenberg-Marquardt step because the linear |
| 287 | // model fits well. |
| 288 | x = x_new_; |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 289 | |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 290 | if (std::abs(cost_change) < options.function_tolerance) { |
| 291 | cost_ = f_x_new_.squaredNorm() / 2; |
| 292 | summary.status = COST_CHANGE_TOO_SMALL; |
| 293 | break; |
| 294 | } |
| 295 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 296 | // TODO(sameeragarwal): Deal with failure. |
| 297 | Update(function, x); |
| 298 | if (summary.gradient_max_norm < options.gradient_tolerance) { |
| 299 | summary.status = GRADIENT_TOO_SMALL; |
| 300 | break; |
| 301 | } |
| 302 | |
| 303 | if (cost_ < options.cost_threshold) { |
| 304 | summary.status = COST_TOO_SMALL; |
| 305 | break; |
| 306 | } |
| 307 | |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 308 | Scalar tmp = Scalar(2 * rho - 1); |
Sameer Agarwal | e84cf10 | 2020-12-14 08:55:05 -0800 | [diff] [blame] | 309 | u = u * std::max(Scalar(1 / 3.), 1 - tmp * tmp * tmp); |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 310 | v = 2; |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 311 | |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 312 | } else { |
| 313 | // Reject the update because either the normal equations failed to solve |
| 314 | // or the local linear model was not good (rho < 0). |
| 315 | |
| 316 | // Additionally if the cost change is too small, then terminate. |
| 317 | if (std::abs(cost_change) < options.function_tolerance) { |
| 318 | // Terminate |
| 319 | summary.status = COST_CHANGE_TOO_SMALL; |
| 320 | break; |
| 321 | } |
| 322 | |
| 323 | // Reduce the size of the trust region. |
| 324 | u *= v; |
| 325 | v *= 2; |
| 326 | } |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 327 | } |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 328 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 329 | summary.final_cost = cost_; |
Sameer Agarwal | cc0bd49 | 2017-10-14 09:41:20 -0700 | [diff] [blame] | 330 | return summary; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 331 | } |
| 332 | |
Sameer Agarwal | cc0bd49 | 2017-10-14 09:41:20 -0700 | [diff] [blame] | 333 | Options options; |
| 334 | Summary summary; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 335 | |
| 336 | private: |
| 337 | // Preallocate everything, including temporary storage needed for solving the |
| 338 | // linear system. This allows reusing the intermediate storage across solves. |
| 339 | LinearSolver linear_solver_; |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 340 | Scalar cost_; |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 341 | Parameters dx_, x_new_, g_, jacobi_scaling_, lm_diagonal_, lm_step_; |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 342 | Eigen::Matrix<Scalar, NUM_RESIDUALS, 1> residuals_, f_x_new_; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 343 | Eigen::Matrix<Scalar, NUM_RESIDUALS, NUM_PARAMETERS> jacobian_; |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 344 | Eigen::Matrix<Scalar, NUM_PARAMETERS, NUM_PARAMETERS> jtj_, jtj_regularized_; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 345 | |
| 346 | // The following definitions are needed for template metaprogramming. |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 347 | template <bool Condition, typename T> |
| 348 | struct enable_if; |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 349 | |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 350 | template <typename T> |
| 351 | struct enable_if<true, T> { |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 352 | typedef T type; |
| 353 | }; |
| 354 | |
| 355 | // The number of parameters and residuals are dynamically sized. |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 356 | template <int R, int P> |
| 357 | typename enable_if<(R == Eigen::Dynamic && P == Eigen::Dynamic), void>::type |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 358 | Initialize(const Function& function) { |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 359 | Initialize(function.NumResiduals(), function.NumParameters()); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 360 | } |
| 361 | |
| 362 | // The number of parameters is dynamically sized and the number of |
| 363 | // residuals is statically sized. |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 364 | template <int R, int P> |
| 365 | typename enable_if<(R == Eigen::Dynamic && P != Eigen::Dynamic), void>::type |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 366 | Initialize(const Function& function) { |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 367 | Initialize(function.NumResiduals(), P); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | // The number of parameters is statically sized and the number of |
| 371 | // residuals is dynamically sized. |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 372 | template <int R, int P> |
| 373 | typename enable_if<(R != Eigen::Dynamic && P == Eigen::Dynamic), void>::type |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 374 | Initialize(const Function& function) { |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 375 | Initialize(R, function.NumParameters()); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 376 | } |
| 377 | |
| 378 | // The number of parameters and residuals are statically sized. |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 379 | template <int R, int P> |
| 380 | typename enable_if<(R != Eigen::Dynamic && P != Eigen::Dynamic), void>::type |
Sameer Agarwal | 030b41d | 2017-10-21 22:33:55 -0700 | [diff] [blame] | 381 | Initialize(const Function& /* function */) {} |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 382 | |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 383 | void Initialize(int num_residuals, int num_parameters) { |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 384 | dx_.resize(num_parameters); |
| 385 | x_new_.resize(num_parameters); |
| 386 | g_.resize(num_parameters); |
| 387 | jacobi_scaling_.resize(num_parameters); |
| 388 | lm_diagonal_.resize(num_parameters); |
| 389 | lm_step_.resize(num_parameters); |
Sameer Agarwal | 8841119 | 2021-09-14 09:54:50 -0700 | [diff] [blame] | 390 | residuals_.resize(num_residuals); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 391 | f_x_new_.resize(num_residuals); |
| 392 | jacobian_.resize(num_residuals, num_parameters); |
| 393 | jtj_.resize(num_parameters, num_parameters); |
Sameer Agarwal | ba73ce1 | 2017-10-18 20:48:12 -0700 | [diff] [blame] | 394 | jtj_regularized_.resize(num_parameters, num_parameters); |
Keir Mierle | 7928ca0 | 2017-10-10 02:15:25 -0700 | [diff] [blame] | 395 | } |
| 396 | }; |
| 397 | |
| 398 | } // namespace ceres |
| 399 | |
Sameer Agarwal | 4d88f50 | 2017-10-11 16:03:15 -0700 | [diff] [blame] | 400 | #endif // CERES_PUBLIC_TINY_SOLVER_H_ |