|
Packit |
ea1746 |
// Ceres Solver - A fast non-linear least squares minimizer
|
|
Packit |
ea1746 |
// Copyright 2016 Google Inc. All rights reserved.
|
|
Packit |
ea1746 |
// http://ceres-solver.org/
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Redistribution and use in source and binary forms, with or without
|
|
Packit |
ea1746 |
// modification, are permitted provided that the following conditions are met:
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// * Redistributions of source code must retain the above copyright notice,
|
|
Packit |
ea1746 |
// this list of conditions and the following disclaimer.
|
|
Packit |
ea1746 |
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
Packit |
ea1746 |
// this list of conditions and the following disclaimer in the documentation
|
|
Packit |
ea1746 |
// and/or other materials provided with the distribution.
|
|
Packit |
ea1746 |
// * Neither the name of Google Inc. nor the names of its contributors may be
|
|
Packit |
ea1746 |
// used to endorse or promote products derived from this software without
|
|
Packit |
ea1746 |
// specific prior written permission.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
Packit |
ea1746 |
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
Packit |
ea1746 |
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
ea1746 |
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
Packit |
ea1746 |
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
Packit |
ea1746 |
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
Packit |
ea1746 |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
ea1746 |
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
Packit |
ea1746 |
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
ea1746 |
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
Packit |
ea1746 |
// POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Author: sameeragarwal@google.com (Sameer Agarwal)
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include "ceres/trust_region_minimizer.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include <algorithm>
|
|
Packit |
ea1746 |
#include <cmath>
|
|
Packit |
ea1746 |
#include <cstdlib>
|
|
Packit |
ea1746 |
#include <cstring>
|
|
Packit |
ea1746 |
#include <limits>
|
|
Packit |
ea1746 |
#include <string>
|
|
Packit |
ea1746 |
#include <vector>
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include "Eigen/Core"
|
|
Packit |
ea1746 |
#include "ceres/array_utils.h"
|
|
Packit |
ea1746 |
#include "ceres/coordinate_descent_minimizer.h"
|
|
Packit |
ea1746 |
#include "ceres/evaluator.h"
|
|
Packit |
ea1746 |
#include "ceres/file.h"
|
|
Packit |
ea1746 |
#include "ceres/line_search.h"
|
|
Packit |
ea1746 |
#include "ceres/stringprintf.h"
|
|
Packit |
ea1746 |
#include "ceres/types.h"
|
|
Packit |
ea1746 |
#include "ceres/wall_time.h"
|
|
Packit |
ea1746 |
#include "glog/logging.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Helper macro to simplify some of the control flow.
|
|
Packit |
ea1746 |
#define RETURN_IF_ERROR_AND_LOG(expr) \
|
|
Packit |
ea1746 |
do { \
|
|
Packit |
ea1746 |
if (!(expr)) { \
|
|
Packit |
ea1746 |
LOG(ERROR) << "Terminating: " << solver_summary_->message; \
|
|
Packit |
ea1746 |
return; \
|
|
Packit |
ea1746 |
} \
|
|
Packit |
ea1746 |
} while (0)
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
namespace ceres {
|
|
Packit |
ea1746 |
namespace internal {
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TrustRegionMinimizer::~TrustRegionMinimizer() {}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
|
|
Packit |
ea1746 |
double* parameters,
|
|
Packit |
ea1746 |
Solver::Summary* solver_summary) {
|
|
Packit |
ea1746 |
start_time_in_secs_ = WallTimeInSeconds();
|
|
Packit |
ea1746 |
iteration_start_time_in_secs_ = start_time_in_secs_;
|
|
Packit |
ea1746 |
Init(options, parameters, solver_summary);
|
|
Packit |
ea1746 |
RETURN_IF_ERROR_AND_LOG(IterationZero());
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Create the TrustRegionStepEvaluator. The construction needs to be
|
|
Packit |
ea1746 |
// delayed to this point because we need the cost for the starting
|
|
Packit |
ea1746 |
// point to initialize the step evaluator.
|
|
Packit |
ea1746 |
step_evaluator_.reset(new TrustRegionStepEvaluator(
|
|
Packit |
ea1746 |
x_cost_,
|
|
Packit |
ea1746 |
options_.use_nonmonotonic_steps
|
|
Packit |
ea1746 |
? options_.max_consecutive_nonmonotonic_steps
|
|
Packit |
ea1746 |
: 0));
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
while (FinalizeIterationAndCheckIfMinimizerCanContinue()) {
|
|
Packit |
ea1746 |
iteration_start_time_in_secs_ = WallTimeInSeconds();
|
|
Packit |
ea1746 |
iteration_summary_ = IterationSummary();
|
|
Packit |
ea1746 |
iteration_summary_.iteration =
|
|
Packit |
ea1746 |
solver_summary->iterations.back().iteration + 1;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
RETURN_IF_ERROR_AND_LOG(ComputeTrustRegionStep());
|
|
Packit |
ea1746 |
if (!iteration_summary_.step_is_valid) {
|
|
Packit |
ea1746 |
RETURN_IF_ERROR_AND_LOG(HandleInvalidStep());
|
|
Packit |
ea1746 |
continue;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (options_.is_constrained) {
|
|
Packit |
ea1746 |
// Use a projected line search to enforce the bounds constraints
|
|
Packit |
ea1746 |
// and improve the quality of the step.
|
|
Packit |
ea1746 |
DoLineSearch(x_, gradient_, x_cost_, &delta_;;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ComputeCandidatePointAndEvaluateCost();
|
|
Packit |
ea1746 |
DoInnerIterationsIfNeeded();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (ParameterToleranceReached()) {
|
|
Packit |
ea1746 |
return;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (FunctionToleranceReached()) {
|
|
Packit |
ea1746 |
return;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (IsStepSuccessful()) {
|
|
Packit |
ea1746 |
RETURN_IF_ERROR_AND_LOG(HandleSuccessfulStep());
|
|
Packit |
ea1746 |
continue;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
HandleUnsuccessfulStep();
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Initialize the minimizer, allocate working space and set some of
|
|
Packit |
ea1746 |
// the fields in the solver_summary.
|
|
Packit |
ea1746 |
void TrustRegionMinimizer::Init(const Minimizer::Options& options,
|
|
Packit |
ea1746 |
double* parameters,
|
|
Packit |
ea1746 |
Solver::Summary* solver_summary) {
|
|
Packit |
ea1746 |
options_ = options;
|
|
Packit |
ea1746 |
sort(options_.trust_region_minimizer_iterations_to_dump.begin(),
|
|
Packit |
ea1746 |
options_.trust_region_minimizer_iterations_to_dump.end());
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
parameters_ = parameters;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_ = solver_summary;
|
|
Packit |
ea1746 |
solver_summary_->termination_type = NO_CONVERGENCE;
|
|
Packit |
ea1746 |
solver_summary_->num_successful_steps = 0;
|
|
Packit |
ea1746 |
solver_summary_->num_unsuccessful_steps = 0;
|
|
Packit |
ea1746 |
solver_summary_->is_constrained = options.is_constrained;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
evaluator_ = CHECK_NOTNULL(options_.evaluator.get());
|
|
Packit |
ea1746 |
jacobian_ = CHECK_NOTNULL(options_.jacobian.get());
|
|
Packit |
ea1746 |
strategy_ = CHECK_NOTNULL(options_.trust_region_strategy.get());
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
is_not_silent_ = !options.is_silent;
|
|
Packit |
ea1746 |
inner_iterations_are_enabled_ =
|
|
Packit |
ea1746 |
options.inner_iteration_minimizer.get() != NULL;
|
|
Packit |
ea1746 |
inner_iterations_were_useful_ = false;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
num_parameters_ = evaluator_->NumParameters();
|
|
Packit |
ea1746 |
num_effective_parameters_ = evaluator_->NumEffectiveParameters();
|
|
Packit |
ea1746 |
num_residuals_ = evaluator_->NumResiduals();
|
|
Packit |
ea1746 |
num_consecutive_invalid_steps_ = 0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
x_ = ConstVectorRef(parameters_, num_parameters_);
|
|
Packit |
ea1746 |
x_norm_ = x_.norm();
|
|
Packit |
ea1746 |
residuals_.resize(num_residuals_);
|
|
Packit |
ea1746 |
trust_region_step_.resize(num_effective_parameters_);
|
|
Packit |
ea1746 |
delta_.resize(num_effective_parameters_);
|
|
Packit |
ea1746 |
candidate_x_.resize(num_parameters_);
|
|
Packit |
ea1746 |
gradient_.resize(num_effective_parameters_);
|
|
Packit |
ea1746 |
model_residuals_.resize(num_residuals_);
|
|
Packit |
ea1746 |
negative_gradient_.resize(num_effective_parameters_);
|
|
Packit |
ea1746 |
projected_gradient_step_.resize(num_parameters_);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// By default scaling is one, if the user requests Jacobi scaling of
|
|
Packit |
ea1746 |
// the Jacobian, we will compute and overwrite this vector.
|
|
Packit |
ea1746 |
jacobian_scaling_ = Vector::Ones(num_effective_parameters_);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
x_norm_ = -1; // Invalid value
|
|
Packit |
ea1746 |
x_cost_ = std::numeric_limits<double>::max();
|
|
Packit |
ea1746 |
minimum_cost_ = x_cost_;
|
|
Packit |
ea1746 |
model_cost_change_ = 0.0;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// 1. Project the initial solution onto the feasible set if needed.
|
|
Packit |
ea1746 |
// 2. Compute the initial cost, jacobian & gradient.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Return true if all computations can be performed successfully.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::IterationZero() {
|
|
Packit |
ea1746 |
iteration_summary_ = IterationSummary();
|
|
Packit |
ea1746 |
iteration_summary_.iteration = 0;
|
|
Packit |
ea1746 |
iteration_summary_.step_is_valid = false;
|
|
Packit |
ea1746 |
iteration_summary_.step_is_successful = false;
|
|
Packit |
ea1746 |
iteration_summary_.cost_change = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.gradient_max_norm = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.gradient_norm = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.step_norm = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.relative_decrease = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.eta = options_.eta;
|
|
Packit |
ea1746 |
iteration_summary_.linear_solver_iterations = 0;
|
|
Packit |
ea1746 |
iteration_summary_.step_solver_time_in_seconds = 0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (options_.is_constrained) {
|
|
Packit |
ea1746 |
delta_.setZero();
|
|
Packit |
ea1746 |
if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) {
|
|
Packit |
ea1746 |
solver_summary_->message =
|
|
Packit |
ea1746 |
"Unable to project initial point onto the feasible set.";
|
|
Packit |
ea1746 |
solver_summary_->termination_type = FAILURE;
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
x_ = candidate_x_;
|
|
Packit |
ea1746 |
x_norm_ = x_.norm();
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (!EvaluateGradientAndJacobian()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->initial_cost = x_cost_ + solver_summary_->fixed_cost;
|
|
Packit |
ea1746 |
iteration_summary_.step_is_valid = true;
|
|
Packit |
ea1746 |
iteration_summary_.step_is_successful = true;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// For the current x_, compute
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// 1. Cost
|
|
Packit |
ea1746 |
// 2. Jacobian
|
|
Packit |
ea1746 |
// 3. Gradient
|
|
Packit |
ea1746 |
// 4. Scale the Jacobian if needed (and compute the scaling if we are
|
|
Packit |
ea1746 |
// in iteration zero).
|
|
Packit |
ea1746 |
// 5. Compute the 2 and max norm of the gradient.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Returns true if all computations could be performed
|
|
Packit |
ea1746 |
// successfully. Any failures are considered fatal and the
|
|
Packit |
ea1746 |
// Solver::Summary is updated to indicate this.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::EvaluateGradientAndJacobian() {
|
|
Packit |
ea1746 |
if (!evaluator_->Evaluate(x_.data(),
|
|
Packit |
ea1746 |
&x_cost_,
|
|
Packit |
ea1746 |
residuals_.data(),
|
|
Packit |
ea1746 |
gradient_.data(),
|
|
Packit |
ea1746 |
jacobian_)) {
|
|
Packit |
ea1746 |
solver_summary_->message = "Residual and Jacobian evaluation failed.";
|
|
Packit |
ea1746 |
solver_summary_->termination_type = FAILURE;
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
iteration_summary_.cost = x_cost_ + solver_summary_->fixed_cost;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (options_.jacobi_scaling) {
|
|
Packit |
ea1746 |
if (iteration_summary_.iteration == 0) {
|
|
Packit |
ea1746 |
// Compute a scaling vector that is used to improve the
|
|
Packit |
ea1746 |
// conditioning of the Jacobian.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// jacobian_scaling_ = diag(J'J)^{-1}
|
|
Packit |
ea1746 |
jacobian_->SquaredColumnNorm(jacobian_scaling_.data());
|
|
Packit |
ea1746 |
for (int i = 0; i < jacobian_->num_cols(); ++i) {
|
|
Packit |
ea1746 |
// Add one to the denominator to prevent division by zero.
|
|
Packit |
ea1746 |
jacobian_scaling_[i] = 1.0 / (1.0 + sqrt(jacobian_scaling_[i]));
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// jacobian = jacobian * diag(J'J) ^{-1}
|
|
Packit |
ea1746 |
jacobian_->ScaleColumns(jacobian_scaling_.data());
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// The gradient exists in the local tangent space. To account for
|
|
Packit |
ea1746 |
// the bounds constraints correctly, instead of just computing the
|
|
Packit |
ea1746 |
// norm of the gradient vector, we compute
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// |Plus(x, -gradient) - x|
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Where the Plus operator lifts the negative gradient to the
|
|
Packit |
ea1746 |
// ambient space, adds it to x and projects it on the hypercube
|
|
Packit |
ea1746 |
// defined by the bounds.
|
|
Packit |
ea1746 |
negative_gradient_ = -gradient_;
|
|
Packit |
ea1746 |
if (!evaluator_->Plus(x_.data(),
|
|
Packit |
ea1746 |
negative_gradient_.data(),
|
|
Packit |
ea1746 |
projected_gradient_step_.data())) {
|
|
Packit |
ea1746 |
solver_summary_->message =
|
|
Packit |
ea1746 |
"projected_gradient_step = Plus(x, -gradient) failed.";
|
|
Packit |
ea1746 |
solver_summary_->termination_type = FAILURE;
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
iteration_summary_.gradient_max_norm =
|
|
Packit |
ea1746 |
(x_ - projected_gradient_step_).lpNorm<Eigen::Infinity>();
|
|
Packit |
ea1746 |
iteration_summary_.gradient_norm = (x_ - projected_gradient_step_).norm();
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// 1. Add the final timing information to the iteration summary.
|
|
Packit |
ea1746 |
// 2. Run the callbacks
|
|
Packit |
ea1746 |
// 3. Check for termination based on
|
|
Packit |
ea1746 |
// a. Run time
|
|
Packit |
ea1746 |
// b. Iteration count
|
|
Packit |
ea1746 |
// c. Max norm of the gradient
|
|
Packit |
ea1746 |
// d. Size of the trust region radius.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Returns true if user did not terminate the solver and none of these
|
|
Packit |
ea1746 |
// termination criterion are met.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::FinalizeIterationAndCheckIfMinimizerCanContinue() {
|
|
Packit |
ea1746 |
if (iteration_summary_.step_is_successful) {
|
|
Packit |
ea1746 |
++solver_summary_->num_successful_steps;
|
|
Packit |
ea1746 |
if (x_cost_ < minimum_cost_) {
|
|
Packit |
ea1746 |
minimum_cost_ = x_cost_;
|
|
Packit |
ea1746 |
VectorRef(parameters_, num_parameters_) = x_;
|
|
Packit |
ea1746 |
iteration_summary_.step_is_nonmonotonic = false;
|
|
Packit |
ea1746 |
} else {
|
|
Packit |
ea1746 |
iteration_summary_.step_is_nonmonotonic = true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
} else {
|
|
Packit |
ea1746 |
++solver_summary_->num_unsuccessful_steps;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
iteration_summary_.trust_region_radius = strategy_->Radius();
|
|
Packit |
ea1746 |
iteration_summary_.iteration_time_in_seconds =
|
|
Packit |
ea1746 |
WallTimeInSeconds() - iteration_start_time_in_secs_;
|
|
Packit |
ea1746 |
iteration_summary_.cumulative_time_in_seconds =
|
|
Packit |
ea1746 |
WallTimeInSeconds() - start_time_in_secs_ +
|
|
Packit |
ea1746 |
solver_summary_->preprocessor_time_in_seconds;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->iterations.push_back(iteration_summary_);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (!RunCallbacks(options_, iteration_summary_, solver_summary_)) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (MaxSolverTimeReached()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (MaxSolverIterationsReached()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (GradientToleranceReached()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (MinTrustRegionRadiusReached()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Compute the trust region step using the TrustRegionStrategy chosen
|
|
Packit |
ea1746 |
// by the user.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// If the strategy returns with LINEAR_SOLVER_FATAL_ERROR, which
|
|
Packit |
ea1746 |
// indicates an unrecoverable error, return false. This is the only
|
|
Packit |
ea1746 |
// condition that returns false.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// If the strategy returns with LINEAR_SOLVER_FAILURE, which indicates
|
|
Packit |
ea1746 |
// a numerical failure that could be recovered from by retrying
|
|
Packit |
ea1746 |
// (e.g. by increasing the strength of the regularization), we set
|
|
Packit |
ea1746 |
// iteration_summary_.step_is_valid to false and return true.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// In all other cases, we compute the decrease in the trust region
|
|
Packit |
ea1746 |
// model problem. In exact arithmetic, this should always be
|
|
Packit |
ea1746 |
// positive, but due to numerical problems in the TrustRegionStrategy
|
|
Packit |
ea1746 |
// or round off error when computing the decrease it may be
|
|
Packit |
ea1746 |
// negative. In which case again, we set
|
|
Packit |
ea1746 |
// iteration_summary_.step_is_valid to false.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::ComputeTrustRegionStep() {
|
|
Packit |
ea1746 |
const double strategy_start_time = WallTimeInSeconds();
|
|
Packit |
ea1746 |
iteration_summary_.step_is_valid = false;
|
|
Packit |
ea1746 |
TrustRegionStrategy::PerSolveOptions per_solve_options;
|
|
Packit |
ea1746 |
per_solve_options.eta = options_.eta;
|
|
Packit |
ea1746 |
if (find(options_.trust_region_minimizer_iterations_to_dump.begin(),
|
|
Packit |
ea1746 |
options_.trust_region_minimizer_iterations_to_dump.end(),
|
|
Packit |
ea1746 |
iteration_summary_.iteration) !=
|
|
Packit |
ea1746 |
options_.trust_region_minimizer_iterations_to_dump.end()) {
|
|
Packit |
ea1746 |
per_solve_options.dump_format_type =
|
|
Packit |
ea1746 |
options_.trust_region_problem_dump_format_type;
|
|
Packit |
ea1746 |
per_solve_options.dump_filename_base =
|
|
Packit |
ea1746 |
JoinPath(options_.trust_region_problem_dump_directory,
|
|
Packit |
ea1746 |
StringPrintf("ceres_solver_iteration_%03d",
|
|
Packit |
ea1746 |
iteration_summary_.iteration));
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TrustRegionStrategy::Summary strategy_summary =
|
|
Packit |
ea1746 |
strategy_->ComputeStep(per_solve_options,
|
|
Packit |
ea1746 |
jacobian_,
|
|
Packit |
ea1746 |
residuals_.data(),
|
|
Packit |
ea1746 |
trust_region_step_.data());
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) {
|
|
Packit |
ea1746 |
solver_summary_->message =
|
|
Packit |
ea1746 |
"Linear solver failed due to unrecoverable "
|
|
Packit |
ea1746 |
"non-numeric causes. Please see the error log for clues. ";
|
|
Packit |
ea1746 |
solver_summary_->termination_type = FAILURE;
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
iteration_summary_.step_solver_time_in_seconds =
|
|
Packit |
ea1746 |
WallTimeInSeconds() - strategy_start_time;
|
|
Packit |
ea1746 |
iteration_summary_.linear_solver_iterations = strategy_summary.num_iterations;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (strategy_summary.termination_type == LINEAR_SOLVER_FAILURE) {
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// new_model_cost
|
|
Packit |
ea1746 |
// = 1/2 [f + J * step]^2
|
|
Packit |
ea1746 |
// = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ]
|
|
Packit |
ea1746 |
// model_cost_change
|
|
Packit |
ea1746 |
// = cost - new_model_cost
|
|
Packit |
ea1746 |
// = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step]
|
|
Packit |
ea1746 |
// = -f'J * step - step' * J' * J * step / 2
|
|
Packit |
ea1746 |
// = -(J * step)'(f + J * step / 2)
|
|
Packit |
ea1746 |
model_residuals_.setZero();
|
|
Packit |
ea1746 |
jacobian_->RightMultiply(trust_region_step_.data(), model_residuals_.data());
|
|
Packit |
ea1746 |
model_cost_change_ =
|
|
Packit |
ea1746 |
-model_residuals_.dot(residuals_ + model_residuals_ / 2.0);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// TODO(sameeragarwal)
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// 1. What happens if model_cost_change_ = 0
|
|
Packit |
ea1746 |
// 2. What happens if -epsilon <= model_cost_change_ < 0 for some
|
|
Packit |
ea1746 |
// small epsilon due to round off error.
|
|
Packit |
ea1746 |
iteration_summary_.step_is_valid = (model_cost_change_ > 0.0);
|
|
Packit |
ea1746 |
if (iteration_summary_.step_is_valid) {
|
|
Packit |
ea1746 |
// Undo the Jacobian column scaling.
|
|
Packit |
ea1746 |
delta_ = (trust_region_step_.array() * jacobian_scaling_.array()).matrix();
|
|
Packit |
ea1746 |
num_consecutive_invalid_steps_ = 0;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_ && !iteration_summary_.step_is_valid)
|
|
Packit |
ea1746 |
<< "Invalid step: current_cost: " << x_cost_
|
|
Packit |
ea1746 |
<< " absolute model cost change: " << model_cost_change_
|
|
Packit |
ea1746 |
<< " relative model cost change: " << (model_cost_change_ / x_cost_);
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Invalid steps can happen due to a number of reasons, and we allow a
|
|
Packit |
ea1746 |
// limited number of consecutive failures, and return false if this
|
|
Packit |
ea1746 |
// limit is exceeded.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::HandleInvalidStep() {
|
|
Packit |
ea1746 |
// TODO(sameeragarwal): Should we be returning FAILURE or
|
|
Packit |
ea1746 |
// NO_CONVERGENCE? The solution value is still usable in many cases,
|
|
Packit |
ea1746 |
// it is not clear if we should declare the solver a failure
|
|
Packit |
ea1746 |
// entirely. For example the case where model_cost_change ~ 0.0, but
|
|
Packit |
ea1746 |
// just slightly negative.
|
|
Packit |
ea1746 |
if (++num_consecutive_invalid_steps_ >=
|
|
Packit |
ea1746 |
options_.max_num_consecutive_invalid_steps) {
|
|
Packit |
ea1746 |
solver_summary_->message = StringPrintf(
|
|
Packit |
ea1746 |
"Number of consecutive invalid steps more "
|
|
Packit |
ea1746 |
"than Solver::Options::max_num_consecutive_invalid_steps: %d",
|
|
Packit |
ea1746 |
options_.max_num_consecutive_invalid_steps);
|
|
Packit |
ea1746 |
solver_summary_->termination_type = FAILURE;
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
strategy_->StepIsInvalid();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// We are going to try and reduce the trust region radius and
|
|
Packit |
ea1746 |
// solve again. To do this, we are going to treat this iteration
|
|
Packit |
ea1746 |
// as an unsuccessful iteration. Since the various callbacks are
|
|
Packit |
ea1746 |
// still executed, we are going to fill the iteration summary
|
|
Packit |
ea1746 |
// with data that assumes a step of length zero and no progress.
|
|
Packit |
ea1746 |
iteration_summary_.cost = x_cost_ + solver_summary_->fixed_cost;
|
|
Packit |
ea1746 |
iteration_summary_.cost_change = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.gradient_max_norm =
|
|
Packit |
ea1746 |
solver_summary_->iterations.back().gradient_max_norm;
|
|
Packit |
ea1746 |
iteration_summary_.gradient_norm =
|
|
Packit |
ea1746 |
solver_summary_->iterations.back().gradient_norm;
|
|
Packit |
ea1746 |
iteration_summary_.step_norm = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.relative_decrease = 0.0;
|
|
Packit |
ea1746 |
iteration_summary_.eta = options_.eta;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Use the supplied coordinate descent minimizer to perform inner
|
|
Packit |
ea1746 |
// iterations and compute the improvement due to it. Returns the cost
|
|
Packit |
ea1746 |
// after performing the inner iterations.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// The optimization is performed with candidate_x_ as the starting
|
|
Packit |
ea1746 |
// point, and if the optimization is successful, candidate_x_ will be
|
|
Packit |
ea1746 |
// updated with the optimized parameters.
|
|
Packit |
ea1746 |
void TrustRegionMinimizer::DoInnerIterationsIfNeeded() {
|
|
Packit |
ea1746 |
inner_iterations_were_useful_ = false;
|
|
Packit |
ea1746 |
if (!inner_iterations_are_enabled_ ||
|
|
Packit |
ea1746 |
candidate_cost_ >= std::numeric_limits<double>::max()) {
|
|
Packit |
ea1746 |
return;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
double inner_iteration_start_time = WallTimeInSeconds();
|
|
Packit |
ea1746 |
++solver_summary_->num_inner_iteration_steps;
|
|
Packit |
ea1746 |
inner_iteration_x_ = candidate_x_;
|
|
Packit |
ea1746 |
Solver::Summary inner_iteration_summary;
|
|
Packit |
ea1746 |
options_.inner_iteration_minimizer->Minimize(
|
|
Packit |
ea1746 |
options_, inner_iteration_x_.data(), &inner_iteration_summary);
|
|
Packit |
ea1746 |
double inner_iteration_cost;
|
|
Packit |
ea1746 |
if (!evaluator_->Evaluate(
|
|
Packit |
ea1746 |
inner_iteration_x_.data(), &inner_iteration_cost, NULL, NULL, NULL)) {
|
|
Packit |
ea1746 |
VLOG_IF(2, is_not_silent_) << "Inner iteration failed.";
|
|
Packit |
ea1746 |
return;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
VLOG_IF(2, is_not_silent_)
|
|
Packit |
ea1746 |
<< "Inner iteration succeeded; Current cost: " << x_cost_
|
|
Packit |
ea1746 |
<< " Trust region step cost: " << candidate_cost_
|
|
Packit |
ea1746 |
<< " Inner iteration cost: " << inner_iteration_cost;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
candidate_x_ = inner_iteration_x_;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Normally, the quality of a trust region step is measured by
|
|
Packit |
ea1746 |
// the ratio
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// cost_change
|
|
Packit |
ea1746 |
// r = -----------------
|
|
Packit |
ea1746 |
// model_cost_change
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// All the change in the nonlinear objective is due to the trust
|
|
Packit |
ea1746 |
// region step so this ratio is a good measure of the quality of
|
|
Packit |
ea1746 |
// the trust region radius. However, when inner iterations are
|
|
Packit |
ea1746 |
// being used, cost_change includes the contribution of the
|
|
Packit |
ea1746 |
// inner iterations and its not fair to credit it all to the
|
|
Packit |
ea1746 |
// trust region algorithm. So we change the ratio to be
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// cost_change
|
|
Packit |
ea1746 |
// r = ------------------------------------------------
|
|
Packit |
ea1746 |
// (model_cost_change + inner_iteration_cost_change)
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Practically we do this by increasing model_cost_change by
|
|
Packit |
ea1746 |
// inner_iteration_cost_change.
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double inner_iteration_cost_change =
|
|
Packit |
ea1746 |
candidate_cost_ - inner_iteration_cost;
|
|
Packit |
ea1746 |
model_cost_change_ += inner_iteration_cost_change;
|
|
Packit |
ea1746 |
inner_iterations_were_useful_ = inner_iteration_cost < x_cost_;
|
|
Packit |
ea1746 |
const double inner_iteration_relative_progress =
|
|
Packit |
ea1746 |
1.0 - inner_iteration_cost / candidate_cost_;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Disable inner iterations once the relative improvement
|
|
Packit |
ea1746 |
// drops below tolerance.
|
|
Packit |
ea1746 |
inner_iterations_are_enabled_ =
|
|
Packit |
ea1746 |
(inner_iteration_relative_progress > options_.inner_iteration_tolerance);
|
|
Packit |
ea1746 |
VLOG_IF(2, is_not_silent_ && !inner_iterations_are_enabled_)
|
|
Packit |
ea1746 |
<< "Disabling inner iterations. Progress : "
|
|
Packit |
ea1746 |
<< inner_iteration_relative_progress;
|
|
Packit |
ea1746 |
candidate_cost_ = inner_iteration_cost;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->inner_iteration_time_in_seconds +=
|
|
Packit |
ea1746 |
WallTimeInSeconds() - inner_iteration_start_time;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Perform a projected line search to improve the objective function
|
|
Packit |
ea1746 |
// value along delta.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// TODO(sameeragarwal): The current implementation does not do
|
|
Packit |
ea1746 |
// anything illegal but is incorrect and not terribly effective.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// https://github.com/ceres-solver/ceres-solver/issues/187
|
|
Packit |
ea1746 |
void TrustRegionMinimizer::DoLineSearch(const Vector& x,
|
|
Packit |
ea1746 |
const Vector& gradient,
|
|
Packit |
ea1746 |
const double cost,
|
|
Packit |
ea1746 |
Vector* delta) {
|
|
Packit |
ea1746 |
LineSearchFunction line_search_function(evaluator_);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
LineSearch::Options line_search_options;
|
|
Packit |
ea1746 |
line_search_options.is_silent = true;
|
|
Packit |
ea1746 |
line_search_options.interpolation_type =
|
|
Packit |
ea1746 |
options_.line_search_interpolation_type;
|
|
Packit |
ea1746 |
line_search_options.min_step_size = options_.min_line_search_step_size;
|
|
Packit |
ea1746 |
line_search_options.sufficient_decrease =
|
|
Packit |
ea1746 |
options_.line_search_sufficient_function_decrease;
|
|
Packit |
ea1746 |
line_search_options.max_step_contraction =
|
|
Packit |
ea1746 |
options_.max_line_search_step_contraction;
|
|
Packit |
ea1746 |
line_search_options.min_step_contraction =
|
|
Packit |
ea1746 |
options_.min_line_search_step_contraction;
|
|
Packit |
ea1746 |
line_search_options.max_num_iterations =
|
|
Packit |
ea1746 |
options_.max_num_line_search_step_size_iterations;
|
|
Packit |
ea1746 |
line_search_options.sufficient_curvature_decrease =
|
|
Packit |
ea1746 |
options_.line_search_sufficient_curvature_decrease;
|
|
Packit |
ea1746 |
line_search_options.max_step_expansion =
|
|
Packit |
ea1746 |
options_.max_line_search_step_expansion;
|
|
Packit |
ea1746 |
line_search_options.function = &line_search_function;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
std::string message;
|
|
Packit |
ea1746 |
scoped_ptr<LineSearch> line_search(CHECK_NOTNULL(
|
|
Packit |
ea1746 |
LineSearch::Create(ceres::ARMIJO, line_search_options, &message)));
|
|
Packit |
ea1746 |
LineSearch::Summary line_search_summary;
|
|
Packit |
ea1746 |
line_search_function.Init(x, *delta);
|
|
Packit |
ea1746 |
line_search->Search(1.0, cost, gradient.dot(*delta), &line_search_summary);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->num_line_search_steps += line_search_summary.num_iterations;
|
|
Packit |
ea1746 |
solver_summary_->line_search_cost_evaluation_time_in_seconds +=
|
|
Packit |
ea1746 |
line_search_summary.cost_evaluation_time_in_seconds;
|
|
Packit |
ea1746 |
solver_summary_->line_search_gradient_evaluation_time_in_seconds +=
|
|
Packit |
ea1746 |
line_search_summary.gradient_evaluation_time_in_seconds;
|
|
Packit |
ea1746 |
solver_summary_->line_search_polynomial_minimization_time_in_seconds +=
|
|
Packit |
ea1746 |
line_search_summary.polynomial_minimization_time_in_seconds;
|
|
Packit |
ea1746 |
solver_summary_->line_search_total_time_in_seconds +=
|
|
Packit |
ea1746 |
line_search_summary.total_time_in_seconds;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (line_search_summary.success) {
|
|
Packit |
ea1746 |
*delta *= line_search_summary.optimal_point.x;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Check if the maximum amount of time allowed by the user for the
|
|
Packit |
ea1746 |
// solver has been exceeded, and if so return false after updating
|
|
Packit |
ea1746 |
// Solver::Summary::message.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::MaxSolverTimeReached() {
|
|
Packit |
ea1746 |
const double total_solver_time =
|
|
Packit |
ea1746 |
WallTimeInSeconds() - start_time_in_secs_ +
|
|
Packit |
ea1746 |
solver_summary_->preprocessor_time_in_seconds;
|
|
Packit |
ea1746 |
if (total_solver_time < options_.max_solver_time_in_seconds) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->message = StringPrintf("Maximum solver time reached. "
|
|
Packit |
ea1746 |
"Total solver time: %e >= %e.",
|
|
Packit |
ea1746 |
total_solver_time,
|
|
Packit |
ea1746 |
options_.max_solver_time_in_seconds);
|
|
Packit |
ea1746 |
solver_summary_->termination_type = NO_CONVERGENCE;
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Check if the maximum number of iterations allowed by the user for
|
|
Packit |
ea1746 |
// the solver has been exceeded, and if so return false after updating
|
|
Packit |
ea1746 |
// Solver::Summary::message.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::MaxSolverIterationsReached() {
|
|
Packit |
ea1746 |
if (iteration_summary_.iteration < options_.max_num_iterations) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->message =
|
|
Packit |
ea1746 |
StringPrintf("Maximum number of iterations reached. "
|
|
Packit |
ea1746 |
"Number of iterations: %d.",
|
|
Packit |
ea1746 |
iteration_summary_.iteration);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->termination_type = NO_CONVERGENCE;
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Check convergence based on the max norm of the gradient (only for
|
|
Packit |
ea1746 |
// iterations where the step was declared successful).
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::GradientToleranceReached() {
|
|
Packit |
ea1746 |
if (!iteration_summary_.step_is_successful ||
|
|
Packit |
ea1746 |
iteration_summary_.gradient_max_norm > options_.gradient_tolerance) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->message = StringPrintf(
|
|
Packit |
ea1746 |
"Gradient tolerance reached. "
|
|
Packit |
ea1746 |
"Gradient max norm: %e <= %e",
|
|
Packit |
ea1746 |
iteration_summary_.gradient_max_norm,
|
|
Packit |
ea1746 |
options_.gradient_tolerance);
|
|
Packit |
ea1746 |
solver_summary_->termination_type = CONVERGENCE;
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Check convergence based the size of the trust region radius.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::MinTrustRegionRadiusReached() {
|
|
Packit |
ea1746 |
if (iteration_summary_.trust_region_radius >
|
|
Packit |
ea1746 |
options_.min_trust_region_radius) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->message =
|
|
Packit |
ea1746 |
StringPrintf("Minimum trust region radius reached. "
|
|
Packit |
ea1746 |
"Trust region radius: %e <= %e",
|
|
Packit |
ea1746 |
iteration_summary_.trust_region_radius,
|
|
Packit |
ea1746 |
options_.min_trust_region_radius);
|
|
Packit |
ea1746 |
solver_summary_->termination_type = CONVERGENCE;
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Solver::Options::parameter_tolerance based convergence check.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::ParameterToleranceReached() {
|
|
Packit |
ea1746 |
// Compute the norm of the step in the ambient space.
|
|
Packit |
ea1746 |
iteration_summary_.step_norm = (x_ - candidate_x_).norm();
|
|
Packit |
ea1746 |
const double step_size_tolerance =
|
|
Packit |
ea1746 |
options_.parameter_tolerance * (x_norm_ + options_.parameter_tolerance);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (iteration_summary_.step_norm > step_size_tolerance) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->message = StringPrintf(
|
|
Packit |
ea1746 |
"Parameter tolerance reached. "
|
|
Packit |
ea1746 |
"Relative step_norm: %e <= %e.",
|
|
Packit |
ea1746 |
(iteration_summary_.step_norm / (x_norm_ + options_.parameter_tolerance)),
|
|
Packit |
ea1746 |
options_.parameter_tolerance);
|
|
Packit |
ea1746 |
solver_summary_->termination_type = CONVERGENCE;
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Solver::Options::function_tolerance based convergence check.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::FunctionToleranceReached() {
|
|
Packit |
ea1746 |
iteration_summary_.cost_change = x_cost_ - candidate_cost_;
|
|
Packit |
ea1746 |
const double absolute_function_tolerance =
|
|
Packit |
ea1746 |
options_.function_tolerance * x_cost_;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (fabs(iteration_summary_.cost_change) > absolute_function_tolerance) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
solver_summary_->message = StringPrintf(
|
|
Packit |
ea1746 |
"Function tolerance reached. "
|
|
Packit |
ea1746 |
"|cost_change|/cost: %e <= %e",
|
|
Packit |
ea1746 |
fabs(iteration_summary_.cost_change) / x_cost_,
|
|
Packit |
ea1746 |
options_.function_tolerance);
|
|
Packit |
ea1746 |
solver_summary_->termination_type = CONVERGENCE;
|
|
Packit |
ea1746 |
VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Compute candidate_x_ = Plus(x_, delta_)
|
|
Packit |
ea1746 |
// Evaluate the cost of candidate_x_ as candidate_cost_.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Failure to compute the step or the cost mean that candidate_cost_
|
|
Packit |
ea1746 |
// is set to std::numeric_limits<double>::max(). Unlike
|
|
Packit |
ea1746 |
// EvaluateGradientAndJacobian, failure in this function is not fatal
|
|
Packit |
ea1746 |
// as we are only computing and evaluating a candidate point, and if
|
|
Packit |
ea1746 |
// for some reason we are unable to evaluate it, we consider it to be
|
|
Packit |
ea1746 |
// a point with very high cost. This allows the user to deal with edge
|
|
Packit |
ea1746 |
// cases/constraints as part of the LocalParameterization and
|
|
Packit |
ea1746 |
// CostFunction objects.
|
|
Packit |
ea1746 |
void TrustRegionMinimizer::ComputeCandidatePointAndEvaluateCost() {
|
|
Packit |
ea1746 |
if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) {
|
|
Packit |
ea1746 |
LOG_IF(WARNING, is_not_silent_)
|
|
Packit |
ea1746 |
<< "x_plus_delta = Plus(x, delta) failed. "
|
|
Packit |
ea1746 |
<< "Treating it as a step with infinite cost";
|
|
Packit |
ea1746 |
candidate_cost_ = std::numeric_limits<double>::max();
|
|
Packit |
ea1746 |
return;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (!evaluator_->Evaluate(
|
|
Packit |
ea1746 |
candidate_x_.data(), &candidate_cost_, NULL, NULL, NULL)) {
|
|
Packit |
ea1746 |
LOG_IF(WARNING, is_not_silent_)
|
|
Packit |
ea1746 |
<< "Step failed to evaluate. "
|
|
Packit |
ea1746 |
<< "Treating it as a step with infinite cost";
|
|
Packit |
ea1746 |
candidate_cost_ = std::numeric_limits<double>::max();
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::IsStepSuccessful() {
|
|
Packit |
ea1746 |
iteration_summary_.relative_decrease =
|
|
Packit |
ea1746 |
step_evaluator_->StepQuality(candidate_cost_, model_cost_change_);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// In most cases, boosting the model_cost_change by the
|
|
Packit |
ea1746 |
// improvement caused by the inner iterations is fine, but it can
|
|
Packit |
ea1746 |
// be the case that the original trust region step was so bad that
|
|
Packit |
ea1746 |
// the resulting improvement in the cost was negative, and the
|
|
Packit |
ea1746 |
// change caused by the inner iterations was large enough to
|
|
Packit |
ea1746 |
// improve the step, but also to make relative decrease quite
|
|
Packit |
ea1746 |
// small.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// This can cause the trust region loop to reject this step. To
|
|
Packit |
ea1746 |
// get around this, we expicitly check if the inner iterations
|
|
Packit |
ea1746 |
// led to a net decrease in the objective function value. If
|
|
Packit |
ea1746 |
// they did, we accept the step even if the trust region ratio
|
|
Packit |
ea1746 |
// is small.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Notice that we do not just check that cost_change is positive
|
|
Packit |
ea1746 |
// which is a weaker condition and would render the
|
|
Packit |
ea1746 |
// min_relative_decrease threshold useless. Instead, we keep
|
|
Packit |
ea1746 |
// track of inner_iterations_were_useful, which is true only
|
|
Packit |
ea1746 |
// when inner iterations lead to a net decrease in the cost.
|
|
Packit |
ea1746 |
return (inner_iterations_were_useful_ ||
|
|
Packit |
ea1746 |
iteration_summary_.relative_decrease >
|
|
Packit |
ea1746 |
options_.min_relative_decrease);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Declare the step successful, move to candidate_x, update the
|
|
Packit |
ea1746 |
// derivatives and let the trust region strategy and the step
|
|
Packit |
ea1746 |
// evaluator know that the step has been accepted.
|
|
Packit |
ea1746 |
bool TrustRegionMinimizer::HandleSuccessfulStep() {
|
|
Packit |
ea1746 |
x_ = candidate_x_;
|
|
Packit |
ea1746 |
x_norm_ = x_.norm();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (!EvaluateGradientAndJacobian()) {
|
|
Packit |
ea1746 |
return false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
iteration_summary_.step_is_successful = true;
|
|
Packit |
ea1746 |
strategy_->StepAccepted(iteration_summary_.relative_decrease);
|
|
Packit |
ea1746 |
step_evaluator_->StepAccepted(candidate_cost_, model_cost_change_);
|
|
Packit |
ea1746 |
return true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Declare the step unsuccessful and inform the trust region strategy.
|
|
Packit |
ea1746 |
void TrustRegionMinimizer::HandleUnsuccessfulStep() {
|
|
Packit |
ea1746 |
iteration_summary_.step_is_successful = false;
|
|
Packit |
ea1746 |
strategy_->StepRejected(iteration_summary_.relative_decrease);
|
|
Packit |
ea1746 |
iteration_summary_.cost = candidate_cost_ + solver_summary_->fixed_cost;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
} // namespace internal
|
|
Packit |
ea1746 |
} // namespace ceres
|