|
Packit |
ea1746 |
// Ceres Solver - A fast non-linear least squares minimizer
|
|
Packit |
ea1746 |
// Copyright 2015 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/levenberg_marquardt_strategy.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include <cmath>
|
|
Packit |
ea1746 |
#include "Eigen/Core"
|
|
Packit |
ea1746 |
#include "ceres/array_utils.h"
|
|
Packit |
ea1746 |
#include "ceres/internal/eigen.h"
|
|
Packit |
ea1746 |
#include "ceres/linear_least_squares_problems.h"
|
|
Packit |
ea1746 |
#include "ceres/linear_solver.h"
|
|
Packit |
ea1746 |
#include "ceres/sparse_matrix.h"
|
|
Packit |
ea1746 |
#include "ceres/trust_region_strategy.h"
|
|
Packit |
ea1746 |
#include "ceres/types.h"
|
|
Packit |
ea1746 |
#include "glog/logging.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
namespace ceres {
|
|
Packit |
ea1746 |
namespace internal {
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
LevenbergMarquardtStrategy::LevenbergMarquardtStrategy(
|
|
Packit |
ea1746 |
const TrustRegionStrategy::Options& options)
|
|
Packit |
ea1746 |
: linear_solver_(options.linear_solver),
|
|
Packit |
ea1746 |
radius_(options.initial_radius),
|
|
Packit |
ea1746 |
max_radius_(options.max_radius),
|
|
Packit |
ea1746 |
min_diagonal_(options.min_lm_diagonal),
|
|
Packit |
ea1746 |
max_diagonal_(options.max_lm_diagonal),
|
|
Packit |
ea1746 |
decrease_factor_(2.0),
|
|
Packit |
ea1746 |
reuse_diagonal_(false) {
|
|
Packit |
ea1746 |
CHECK_NOTNULL(linear_solver_);
|
|
Packit |
ea1746 |
CHECK_GT(min_diagonal_, 0.0);
|
|
Packit |
ea1746 |
CHECK_LE(min_diagonal_, max_diagonal_);
|
|
Packit |
ea1746 |
CHECK_GT(max_radius_, 0.0);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
LevenbergMarquardtStrategy::~LevenbergMarquardtStrategy() {
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TrustRegionStrategy::Summary LevenbergMarquardtStrategy::ComputeStep(
|
|
Packit |
ea1746 |
const TrustRegionStrategy::PerSolveOptions& per_solve_options,
|
|
Packit |
ea1746 |
SparseMatrix* jacobian,
|
|
Packit |
ea1746 |
const double* residuals,
|
|
Packit |
ea1746 |
double* step) {
|
|
Packit |
ea1746 |
CHECK_NOTNULL(jacobian);
|
|
Packit |
ea1746 |
CHECK_NOTNULL(residuals);
|
|
Packit |
ea1746 |
CHECK_NOTNULL(step);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const int num_parameters = jacobian->num_cols();
|
|
Packit |
ea1746 |
if (!reuse_diagonal_) {
|
|
Packit |
ea1746 |
if (diagonal_.rows() != num_parameters) {
|
|
Packit |
ea1746 |
diagonal_.resize(num_parameters, 1);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
jacobian->SquaredColumnNorm(diagonal_.data());
|
|
Packit |
ea1746 |
for (int i = 0; i < num_parameters; ++i) {
|
|
Packit |
ea1746 |
diagonal_[i] = std::min(std::max(diagonal_[i], min_diagonal_),
|
|
Packit |
ea1746 |
max_diagonal_);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
lm_diagonal_ = (diagonal_ / radius_).array().sqrt();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
LinearSolver::PerSolveOptions solve_options;
|
|
Packit |
ea1746 |
solve_options.D = lm_diagonal_.data();
|
|
Packit |
ea1746 |
solve_options.q_tolerance = per_solve_options.eta;
|
|
Packit |
ea1746 |
// Disable r_tolerance checking. Since we only care about
|
|
Packit |
ea1746 |
// termination via the q_tolerance. As Nash and Sofer show,
|
|
Packit |
ea1746 |
// r_tolerance based termination is essentially useless in
|
|
Packit |
ea1746 |
// Truncated Newton methods.
|
|
Packit |
ea1746 |
solve_options.r_tolerance = -1.0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Invalidate the output array lm_step, so that we can detect if
|
|
Packit |
ea1746 |
// the linear solver generated numerical garbage. This is known
|
|
Packit |
ea1746 |
// to happen for the DENSE_QR and then DENSE_SCHUR solver when
|
|
Packit |
ea1746 |
// the Jacobin is severly rank deficient and mu is too small.
|
|
Packit |
ea1746 |
InvalidateArray(num_parameters, step);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Instead of solving Jx = -r, solve Jy = r.
|
|
Packit |
ea1746 |
// Then x can be found as x = -y, but the inputs jacobian and residuals
|
|
Packit |
ea1746 |
// do not need to be modified.
|
|
Packit |
ea1746 |
LinearSolver::Summary linear_solver_summary =
|
|
Packit |
ea1746 |
linear_solver_->Solve(jacobian, residuals, solve_options, step);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (linear_solver_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) {
|
|
Packit |
ea1746 |
LOG(WARNING) << "Linear solver fatal error: "
|
|
Packit |
ea1746 |
<< linear_solver_summary.message;
|
|
Packit |
ea1746 |
} else if (linear_solver_summary.termination_type == LINEAR_SOLVER_FAILURE) {
|
|
Packit |
ea1746 |
LOG(WARNING) << "Linear solver failure. Failed to compute a step: "
|
|
Packit |
ea1746 |
<< linear_solver_summary.message;
|
|
Packit |
ea1746 |
} else if (!IsArrayValid(num_parameters, step)) {
|
|
Packit |
ea1746 |
LOG(WARNING) << "Linear solver failure. Failed to compute a finite step.";
|
|
Packit |
ea1746 |
linear_solver_summary.termination_type = LINEAR_SOLVER_FAILURE;
|
|
Packit |
ea1746 |
} else {
|
|
Packit |
ea1746 |
VectorRef(step, num_parameters) *= -1.0;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
reuse_diagonal_ = true;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
if (per_solve_options.dump_format_type == CONSOLE ||
|
|
Packit |
ea1746 |
(per_solve_options.dump_format_type != CONSOLE &&
|
|
Packit |
ea1746 |
!per_solve_options.dump_filename_base.empty())) {
|
|
Packit |
ea1746 |
if (!DumpLinearLeastSquaresProblem(per_solve_options.dump_filename_base,
|
|
Packit |
ea1746 |
per_solve_options.dump_format_type,
|
|
Packit |
ea1746 |
jacobian,
|
|
Packit |
ea1746 |
solve_options.D,
|
|
Packit |
ea1746 |
residuals,
|
|
Packit |
ea1746 |
step,
|
|
Packit |
ea1746 |
0)) {
|
|
Packit |
ea1746 |
LOG(ERROR) << "Unable to dump trust region problem."
|
|
Packit |
ea1746 |
<< " Filename base: " << per_solve_options.dump_filename_base;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TrustRegionStrategy::Summary summary;
|
|
Packit |
ea1746 |
summary.residual_norm = linear_solver_summary.residual_norm;
|
|
Packit |
ea1746 |
summary.num_iterations = linear_solver_summary.num_iterations;
|
|
Packit |
ea1746 |
summary.termination_type = linear_solver_summary.termination_type;
|
|
Packit |
ea1746 |
return summary;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
void LevenbergMarquardtStrategy::StepAccepted(double step_quality) {
|
|
Packit |
ea1746 |
CHECK_GT(step_quality, 0.0);
|
|
Packit |
ea1746 |
radius_ = radius_ / std::max(1.0 / 3.0,
|
|
Packit |
ea1746 |
1.0 - pow(2.0 * step_quality - 1.0, 3));
|
|
Packit |
ea1746 |
radius_ = std::min(max_radius_, radius_);
|
|
Packit |
ea1746 |
decrease_factor_ = 2.0;
|
|
Packit |
ea1746 |
reuse_diagonal_ = false;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
void LevenbergMarquardtStrategy::StepRejected(double step_quality) {
|
|
Packit |
ea1746 |
radius_ = radius_ / decrease_factor_;
|
|
Packit |
ea1746 |
decrease_factor_ *= 2.0;
|
|
Packit |
ea1746 |
reuse_diagonal_ = true;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
double LevenbergMarquardtStrategy::Radius() const {
|
|
Packit |
ea1746 |
return radius_;
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
} // namespace internal
|
|
Packit |
ea1746 |
} // namespace ceres
|