blob: 981c60a12e7804f8341456fc5eb71b3686a950bf [file] [log] [blame]
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
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: sameeragarwal@google.com (Sameer Agarwal)
30
31#include "ceres/trust_region_minimizer.h"
32
33#include <algorithm>
34#include <cstdlib>
35#include <cmath>
36#include <cstring>
Sameer Agarwal552f9f82012-08-31 07:27:22 -070037#include <limits>
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070038#include <string>
39#include <vector>
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070040
41#include "Eigen/Core"
42#include "ceres/array_utils.h"
43#include "ceres/evaluator.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070044#include "ceres/internal/eigen.h"
45#include "ceres/internal/scoped_ptr.h"
Sameer Agarwal0beab862012-08-13 15:12:01 -070046#include "ceres/linear_least_squares_problems.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070047#include "ceres/sparse_matrix.h"
Sameer Agarwal9123e2f2012-09-18 21:49:06 -070048#include "ceres/stringprintf.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070049#include "ceres/trust_region_strategy.h"
50#include "ceres/types.h"
Petter Strandmark76533b32012-09-04 22:08:50 -070051#include "ceres/wall_time.h"
Sameer Agarwal0beab862012-08-13 15:12:01 -070052#include "glog/logging.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070053
54namespace ceres {
55namespace internal {
56namespace {
57// Small constant for various floating point issues.
58const double kEpsilon = 1e-12;
59} // namespace
60
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070061// Compute a scaling vector that is used to improve the conditioning
62// of the Jacobian.
63void TrustRegionMinimizer::EstimateScale(const SparseMatrix& jacobian,
64 double* scale) const {
65 jacobian.SquaredColumnNorm(scale);
66 for (int i = 0; i < jacobian.num_cols(); ++i) {
Sameer Agarwala406b172012-08-18 15:28:49 -070067 scale[i] = 1.0 / (1.0 + sqrt(scale[i]));
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070068 }
69}
70
71void TrustRegionMinimizer::Init(const Minimizer::Options& options) {
72 options_ = options;
73 sort(options_.lsqp_iterations_to_dump.begin(),
74 options_.lsqp_iterations_to_dump.end());
75}
76
77bool TrustRegionMinimizer::MaybeDumpLinearLeastSquaresProblem(
78 const int iteration,
79 const SparseMatrix* jacobian,
80 const double* residuals,
81 const double* step) const {
82 // TODO(sameeragarwal): Since the use of trust_region_radius has
83 // moved inside TrustRegionStrategy, its not clear how we dump the
84 // regularization vector/matrix anymore.
85 //
Sameer Agarwal65625f72012-09-17 12:06:57 -070086 // Also num_eliminate_blocks is not visible to the trust region
87 // minimizer either.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070088 //
Sameer Agarwal65625f72012-09-17 12:06:57 -070089 // Both of these indicate that this is the wrong place for this
90 // code, and going forward this should needs fixing/refactoring.
Sameer Agarwal65625f72012-09-17 12:06:57 -070091 return true;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070092}
93
94void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
95 double* parameters,
96 Solver::Summary* summary) {
Petter Strandmark76533b32012-09-04 22:08:50 -070097 double start_time = WallTimeInSeconds();
98 double iteration_start_time = start_time;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070099 Init(options);
100
101 summary->termination_type = NO_CONVERGENCE;
102 summary->num_successful_steps = 0;
103 summary->num_unsuccessful_steps = 0;
104
105 Evaluator* evaluator = CHECK_NOTNULL(options_.evaluator);
106 SparseMatrix* jacobian = CHECK_NOTNULL(options_.jacobian);
107 TrustRegionStrategy* strategy = CHECK_NOTNULL(options_.trust_region_strategy);
108
109 const int num_parameters = evaluator->NumParameters();
110 const int num_effective_parameters = evaluator->NumEffectiveParameters();
111 const int num_residuals = evaluator->NumResiduals();
112
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700113 VectorRef x_min(parameters, num_parameters);
114 Vector x = x_min;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700115 double x_norm = x.norm();
116
117 Vector residuals(num_residuals);
118 Vector trust_region_step(num_effective_parameters);
119 Vector delta(num_effective_parameters);
120 Vector x_plus_delta(num_parameters);
121 Vector gradient(num_effective_parameters);
122 Vector model_residuals(num_residuals);
123 Vector scale(num_effective_parameters);
124
125 IterationSummary iteration_summary;
126 iteration_summary.iteration = 0;
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700127 iteration_summary.step_is_valid = false;
128 iteration_summary.step_is_successful = false;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700129 iteration_summary.cost_change = 0.0;
130 iteration_summary.gradient_max_norm = 0.0;
131 iteration_summary.step_norm = 0.0;
132 iteration_summary.relative_decrease = 0.0;
133 iteration_summary.trust_region_radius = strategy->Radius();
134 // TODO(sameeragarwal): Rename eta to linear_solver_accuracy or
135 // something similar across the board.
136 iteration_summary.eta = options_.eta;
137 iteration_summary.linear_solver_iterations = 0;
Sameer Agarwalfa015192012-06-11 14:21:42 -0700138 iteration_summary.step_solver_time_in_seconds = 0;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700139
140 // Do initial cost and Jacobian evaluation.
141 double cost = 0.0;
Keir Mierlef44907f2012-07-06 13:52:32 -0700142 if (!evaluator->Evaluate(x.data(), &cost, residuals.data(), NULL, jacobian)) {
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700143 LOG(WARNING) << "Terminating: Residual and Jacobian evaluation failed.";
144 summary->termination_type = NUMERICAL_FAILURE;
145 return;
146 }
147
Sameer Agarwalf102a682013-02-11 15:08:40 -0800148 summary->initial_cost = cost + summary->fixed_cost;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700149 iteration_summary.cost = cost + summary->fixed_cost;
150
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700151 int num_consecutive_nonmonotonic_steps = 0;
152 double minimum_cost = cost;
153 double reference_cost = cost;
154 double accumulated_reference_model_cost_change = 0.0;
155 double candidate_cost = cost;
156 double accumulated_candidate_model_cost_change = 0.0;
157
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700158 gradient.setZero();
159 jacobian->LeftMultiply(residuals.data(), gradient.data());
160 iteration_summary.gradient_max_norm = gradient.lpNorm<Eigen::Infinity>();
161
162 if (options_.jacobi_scaling) {
163 EstimateScale(*jacobian, scale.data());
164 jacobian->ScaleColumns(scale.data());
165 } else {
166 scale.setOnes();
167 }
168
Sameer Agarwal4441b5b2012-06-12 18:01:11 -0700169 // The initial gradient max_norm is bounded from below so that we do
170 // not divide by zero.
Sameer Agarwal9883fc32012-11-30 12:32:43 -0800171 const double initial_gradient_max_norm =
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700172 max(iteration_summary.gradient_max_norm, kEpsilon);
Sameer Agarwal4441b5b2012-06-12 18:01:11 -0700173 const double absolute_gradient_tolerance =
Sameer Agarwal9883fc32012-11-30 12:32:43 -0800174 options_.gradient_tolerance * initial_gradient_max_norm;
Sameer Agarwal4441b5b2012-06-12 18:01:11 -0700175
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700176 if (iteration_summary.gradient_max_norm <= absolute_gradient_tolerance) {
177 summary->termination_type = GRADIENT_TOLERANCE;
178 VLOG(1) << "Terminating: Gradient tolerance reached."
179 << "Relative gradient max norm: "
Sameer Agarwal9883fc32012-11-30 12:32:43 -0800180 << iteration_summary.gradient_max_norm / initial_gradient_max_norm
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700181 << " <= " << options_.gradient_tolerance;
182 return;
183 }
184
Sameer Agarwalfa015192012-06-11 14:21:42 -0700185 iteration_summary.iteration_time_in_seconds =
Petter Strandmark76533b32012-09-04 22:08:50 -0700186 WallTimeInSeconds() - iteration_start_time;
187 iteration_summary.cumulative_time_in_seconds =
188 WallTimeInSeconds() - start_time
189 + summary->preprocessor_time_in_seconds;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700190 summary->iterations.push_back(iteration_summary);
Sameer Agarwalfa015192012-06-11 14:21:42 -0700191
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800192 int num_consecutive_invalid_steps = 0;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700193 while (true) {
Sameer Agarwal9883fc32012-11-30 12:32:43 -0800194 if (!RunCallbacks(options.callbacks, iteration_summary, summary)) {
195 return;
196 }
197
Petter Strandmark76533b32012-09-04 22:08:50 -0700198 iteration_start_time = WallTimeInSeconds();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700199 if (iteration_summary.iteration >= options_.max_num_iterations) {
200 summary->termination_type = NO_CONVERGENCE;
201 VLOG(1) << "Terminating: Maximum number of iterations reached.";
202 break;
203 }
204
Sameer Agarwalfa015192012-06-11 14:21:42 -0700205 const double total_solver_time = iteration_start_time - start_time +
206 summary->preprocessor_time_in_seconds;
207 if (total_solver_time >= options_.max_solver_time_in_seconds) {
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700208 summary->termination_type = NO_CONVERGENCE;
209 VLOG(1) << "Terminating: Maximum solver time reached.";
210 break;
211 }
212
213 iteration_summary = IterationSummary();
214 iteration_summary = summary->iterations.back();
215 iteration_summary.iteration = summary->iterations.back().iteration + 1;
216 iteration_summary.step_is_valid = false;
217 iteration_summary.step_is_successful = false;
218
Petter Strandmark76533b32012-09-04 22:08:50 -0700219 const double strategy_start_time = WallTimeInSeconds();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700220 TrustRegionStrategy::PerSolveOptions per_solve_options;
221 per_solve_options.eta = options_.eta;
Sameer Agarwal05292bf2012-08-20 07:40:45 -0700222 TrustRegionStrategy::Summary strategy_summary =
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700223 strategy->ComputeStep(per_solve_options,
224 jacobian,
225 residuals.data(),
226 trust_region_step.data());
227
Sameer Agarwalfa015192012-06-11 14:21:42 -0700228 iteration_summary.step_solver_time_in_seconds =
Petter Strandmark76533b32012-09-04 22:08:50 -0700229 WallTimeInSeconds() - strategy_start_time;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700230 iteration_summary.linear_solver_iterations =
231 strategy_summary.num_iterations;
232
233 if (!MaybeDumpLinearLeastSquaresProblem(iteration_summary.iteration,
234 jacobian,
235 residuals.data(),
236 trust_region_step.data())) {
237 LOG(FATAL) << "Tried writing linear least squares problem: "
238 << options.lsqp_dump_directory << "but failed.";
239 }
240
Sameer Agarwalb329e582012-09-05 10:45:17 -0700241 double model_cost_change = 0.0;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700242 if (strategy_summary.termination_type != FAILURE) {
Sameer Agarwalb329e582012-09-05 10:45:17 -0700243 // new_model_cost
244 // = 1/2 [f + J * step]^2
245 // = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ]
246 // model_cost_change
247 // = cost - new_model_cost
248 // = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step]
249 // = -f'J * step - step' * J' * J * step / 2
250 model_residuals.setZero();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700251 jacobian->RightMultiply(trust_region_step.data(), model_residuals.data());
Sameer Agarwalb329e582012-09-05 10:45:17 -0700252 model_cost_change = -(residuals.dot(model_residuals) +
253 model_residuals.squaredNorm() / 2.0);
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700254
Sameer Agarwalb329e582012-09-05 10:45:17 -0700255 if (model_cost_change < 0.0) {
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700256 VLOG(1) << "Invalid step: current_cost: " << cost
Sameer Agarwalb329e582012-09-05 10:45:17 -0700257 << " absolute difference " << model_cost_change
258 << " relative difference " << (model_cost_change / cost);
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700259 } else {
260 iteration_summary.step_is_valid = true;
261 }
262 }
263
264 if (!iteration_summary.step_is_valid) {
265 // Invalid steps can happen due to a number of reasons, and we
266 // allow a limited number of successive failures, and return with
267 // NUMERICAL_FAILURE if this limit is exceeded.
268 if (++num_consecutive_invalid_steps >=
269 options_.max_num_consecutive_invalid_steps) {
270 summary->termination_type = NUMERICAL_FAILURE;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700271 summary->error = StringPrintf(
272 "Terminating. Number of successive invalid steps more "
273 "than Solver::Options::max_num_consecutive_invalid_steps: %d",
274 options_.max_num_consecutive_invalid_steps);
275
276 LOG(WARNING) << summary->error;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700277 return;
278 }
279
280 // We are going to try and reduce the trust region radius and
281 // solve again. To do this, we are going to treat this iteration
282 // as an unsuccessful iteration. Since the various callbacks are
283 // still executed, we are going to fill the iteration summary
284 // with data that assumes a step of length zero and no progress.
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700285 iteration_summary.cost = cost + summary->fixed_cost;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700286 iteration_summary.cost_change = 0.0;
287 iteration_summary.gradient_max_norm =
288 summary->iterations.back().gradient_max_norm;
289 iteration_summary.step_norm = 0.0;
290 iteration_summary.relative_decrease = 0.0;
291 iteration_summary.eta = options_.eta;
292 } else {
293 // The step is numerically valid, so now we can judge its quality.
294 num_consecutive_invalid_steps = 0;
295
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700296 // Undo the Jacobian column scaling.
Markus Moll47d26bc2012-08-16 00:23:38 +0200297 delta = (trust_region_step.array() * scale.array()).matrix();
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700298 if (!evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
299 summary->termination_type = NUMERICAL_FAILURE;
300 summary->error =
301 "Terminating. Failed to compute Plus(x, delta, x_plus_delta).";
302
303 LOG(WARNING) << summary->error;
304 return;
305 }
306
307 // Try this step.
308 double new_cost = numeric_limits<double>::max();
309 if (!evaluator->Evaluate(x_plus_delta.data(),
310 &new_cost,
311 NULL, NULL, NULL)) {
312 // If the evaluation of the new cost fails, treat it as a step
313 // with high cost.
314 LOG(WARNING) << "Step failed to evaluate. "
315 << "Treating it as step with infinite cost";
316 new_cost = numeric_limits<double>::max();
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700317 } else {
318 // Check if performing an inner iteration will make it better.
319 if (options.inner_iteration_minimizer != NULL) {
320 const double x_plus_delta_cost = new_cost;
321 Vector inner_iteration_x = x_plus_delta;
322 Solver::Summary inner_iteration_summary;
323 options.inner_iteration_minimizer->Minimize(options,
324 inner_iteration_x.data(),
325 &inner_iteration_summary);
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800326 if (!evaluator->Evaluate(inner_iteration_x.data(),
327 &new_cost,
328 NULL, NULL, NULL)) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700329 VLOG(2) << "Inner iteration failed.";
330 new_cost = x_plus_delta_cost;
331 } else {
332 x_plus_delta = inner_iteration_x;
Sameer Agarwal9883fc32012-11-30 12:32:43 -0800333 // Boost the model_cost_change, since the inner iteration
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700334 // improvements are not accounted for by the trust region.
335 model_cost_change += x_plus_delta_cost - new_cost;
336 VLOG(2) << "Inner iteration succeeded; current cost: " << cost
337 << " x_plus_delta_cost: " << x_plus_delta_cost
338 << " new_cost: " << new_cost;
339 }
340 }
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700341 }
342
343 iteration_summary.step_norm = (x - x_plus_delta).norm();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700344
345 // Convergence based on parameter_tolerance.
346 const double step_size_tolerance = options_.parameter_tolerance *
347 (x_norm + options_.parameter_tolerance);
348 if (iteration_summary.step_norm <= step_size_tolerance) {
349 VLOG(1) << "Terminating. Parameter tolerance reached. "
350 << "relative step_norm: "
351 << iteration_summary.step_norm /
352 (x_norm + options_.parameter_tolerance)
353 << " <= " << options_.parameter_tolerance;
354 summary->termination_type = PARAMETER_TOLERANCE;
355 return;
356 }
357
Sameer Agarwald28b3c82012-06-05 21:50:31 -0700358 VLOG(2) << "old cost: " << cost << " new cost: " << new_cost;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700359 iteration_summary.cost_change = cost - new_cost;
360 const double absolute_function_tolerance =
361 options_.function_tolerance * cost;
362 if (fabs(iteration_summary.cost_change) < absolute_function_tolerance) {
363 VLOG(1) << "Terminating. Function tolerance reached. "
Sameer Agarwalfa015192012-06-11 14:21:42 -0700364 << "|cost_change|/cost: "
365 << fabs(iteration_summary.cost_change) / cost
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700366 << " <= " << options_.function_tolerance;
367 summary->termination_type = FUNCTION_TOLERANCE;
368 return;
369 }
370
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700371 const double relative_decrease =
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700372 iteration_summary.cost_change / model_cost_change;
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700373
374 const double historical_relative_decrease =
375 (reference_cost - new_cost) /
376 (accumulated_reference_model_cost_change + model_cost_change);
377
378 // If monotonic steps are being used, then the relative_decrease
379 // is the usual ratio of the change in objective function value
380 // divided by the change in model cost.
381 //
382 // If non-monotonic steps are allowed, then we take the maximum
383 // of the relative_decrease and the
384 // historical_relative_decrease, which measures the increase
385 // from a reference iteration. The model cost change is
386 // estimated by accumulating the model cost changes since the
387 // reference iteration. The historical relative_decrease offers
388 // a boost to a step which is not too bad compared to the
389 // reference iteration, allowing for non-monotonic steps.
390 iteration_summary.relative_decrease =
391 options.use_nonmonotonic_steps
392 ? max(relative_decrease, historical_relative_decrease)
393 : relative_decrease;
394
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700395 iteration_summary.step_is_successful =
396 iteration_summary.relative_decrease > options_.min_relative_decrease;
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700397
398 if (iteration_summary.step_is_successful) {
399 accumulated_candidate_model_cost_change += model_cost_change;
400 accumulated_reference_model_cost_change += model_cost_change;
401 if (relative_decrease <= options_.min_relative_decrease) {
Sameer Agarwalb23fd4e2012-09-25 09:04:41 -0700402 iteration_summary.step_is_nonmonotonic = true;
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700403 VLOG(2) << "Non-monotonic step! "
404 << " relative_decrease: " << relative_decrease
405 << " historical_relative_decrease: "
406 << historical_relative_decrease;
407 }
408 }
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700409 }
410
411 if (iteration_summary.step_is_successful) {
412 ++summary->num_successful_steps;
413 strategy->StepAccepted(iteration_summary.relative_decrease);
414 x = x_plus_delta;
415 x_norm = x.norm();
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700416
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700417 // Step looks good, evaluate the residuals and Jacobian at this
418 // point.
Keir Mierlef44907f2012-07-06 13:52:32 -0700419 if (!evaluator->Evaluate(x.data(),
420 &cost,
421 residuals.data(),
422 NULL,
423 jacobian)) {
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700424 summary->termination_type = NUMERICAL_FAILURE;
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800425 summary->error =
426 "Terminating: Residual and Jacobian evaluation failed.";
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700427 LOG(WARNING) << summary->error;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700428 return;
429 }
430
431 gradient.setZero();
432 jacobian->LeftMultiply(residuals.data(), gradient.data());
433 iteration_summary.gradient_max_norm = gradient.lpNorm<Eigen::Infinity>();
434
435 if (iteration_summary.gradient_max_norm <= absolute_gradient_tolerance) {
436 summary->termination_type = GRADIENT_TOLERANCE;
437 VLOG(1) << "Terminating: Gradient tolerance reached."
438 << "Relative gradient max norm: "
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800439 << (iteration_summary.gradient_max_norm /
440 initial_gradient_max_norm)
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700441 << " <= " << options_.gradient_tolerance;
442 return;
443 }
444
445 if (options_.jacobi_scaling) {
446 jacobian->ScaleColumns(scale.data());
447 }
Sameer Agarwala8f87d72012-08-08 10:38:31 -0700448
449 // Update the best, reference and candidate iterates.
450 //
451 // Based on algorithm 10.1.2 (page 357) of "Trust Region
452 // Methods" by Conn Gould & Toint, or equations 33-40 of
453 // "Non-monotone trust-region algorithms for nonlinear
454 // optimization subject to convex constraints" by Phil Toint,
455 // Mathematical Programming, 77, 1997.
456 if (cost < minimum_cost) {
457 // A step that improves solution quality was found.
458 x_min = x;
459 minimum_cost = cost;
460 // Set the candidate iterate to the current point.
461 candidate_cost = cost;
462 num_consecutive_nonmonotonic_steps = 0;
463 accumulated_candidate_model_cost_change = 0.0;
464 } else {
465 ++num_consecutive_nonmonotonic_steps;
466 if (cost > candidate_cost) {
467 // The current iterate is has a higher cost than the
468 // candidate iterate. Set the candidate to this point.
469 VLOG(2) << "Updating the candidate iterate to the current point.";
470 candidate_cost = cost;
471 accumulated_candidate_model_cost_change = 0.0;
472 }
473
474 // At this point we have made too many non-monotonic steps and
475 // we are going to reset the value of the reference iterate so
476 // as to force the algorithm to descend.
477 //
478 // This is the case because the candidate iterate has a value
479 // greater than minimum_cost but smaller than the reference
480 // iterate.
481 if (num_consecutive_nonmonotonic_steps ==
482 options.max_consecutive_nonmonotonic_steps) {
483 VLOG(2) << "Resetting the reference point to the candidate point";
484 reference_cost = candidate_cost;
485 accumulated_reference_model_cost_change =
486 accumulated_candidate_model_cost_change;
487 }
488 }
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700489 } else {
490 ++summary->num_unsuccessful_steps;
Sameer Agarwalfa015192012-06-11 14:21:42 -0700491 if (iteration_summary.step_is_valid) {
492 strategy->StepRejected(iteration_summary.relative_decrease);
493 } else {
494 strategy->StepIsInvalid();
495 }
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700496 }
497
498 iteration_summary.cost = cost + summary->fixed_cost;
499 iteration_summary.trust_region_radius = strategy->Radius();
500 if (iteration_summary.trust_region_radius <
501 options_.min_trust_region_radius) {
502 summary->termination_type = PARAMETER_TOLERANCE;
503 VLOG(1) << "Termination. Minimum trust region radius reached.";
504 return;
505 }
506
Sameer Agarwalfa015192012-06-11 14:21:42 -0700507 iteration_summary.iteration_time_in_seconds =
Petter Strandmark76533b32012-09-04 22:08:50 -0700508 WallTimeInSeconds() - iteration_start_time;
509 iteration_summary.cumulative_time_in_seconds =
510 WallTimeInSeconds() - start_time
511 + summary->preprocessor_time_in_seconds;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700512 summary->iterations.push_back(iteration_summary);
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700513 }
514}
515
516
517} // namespace internal
518} // namespace ceres