Update Ceres to the latest upstream

Summary:
This brings up much easier termination type usage,
which for us means we might use:

  ceres::Summary::IsSolutionUsable()

instead of doing manual funky enum values check.

Reviewers: keir

Differential Revision: https://developer.blender.org/D153
This commit is contained in:
Sergey Sharybin 2013-12-29 16:59:15 +06:00
parent 71f689843d
commit 5933b2455c
26 changed files with 506 additions and 518 deletions

View File

@ -332,9 +332,7 @@ int libmv_trackRegion(const libmv_TrackRegionOptions *options,
}
/* TODO(keir): Update the termination string with failure details. */
if (track_region_result.termination == libmv::TrackRegionResult::PARAMETER_TOLERANCE ||
track_region_result.termination == libmv::TrackRegionResult::FUNCTION_TOLERANCE ||
track_region_result.termination == libmv::TrackRegionResult::GRADIENT_TOLERANCE ||
if (track_region_result.termination == libmv::TrackRegionResult::CONVERGENCE ||
track_region_result.termination == libmv::TrackRegionResult::NO_CONVERGENCE)
{
tracking_result = true;

View File

@ -538,9 +538,7 @@ bool EstimateFundamentalFromCorrespondences(
LG << "Final refined matrix:\n" << *F;
return !(summary.termination_type == ceres::DID_NOT_RUN ||
summary.termination_type == ceres::NUMERICAL_FAILURE ||
summary.termination_type == ceres::USER_ABORT);
return summary.IsSolutionUsable();
}
} // namespace libmv

View File

@ -330,9 +330,7 @@ bool EstimateHomography2DFromCorrespondences(
LG << "Final refined matrix:\n" << *H;
return !(summary.termination_type == ceres::DID_NOT_RUN ||
summary.termination_type == ceres::NUMERICAL_FAILURE ||
summary.termination_type == ceres::USER_ABORT);
return summary.IsSolutionUsable();
}
/**

View File

@ -1331,9 +1331,7 @@ void TemplatedTrackRegion(const FloatImage &image1,
// Of the things that can happen in the first pass, don't try the brute
// pass (and second attempt) if the error is one of the terminations below.
if (result->termination == TrackRegionResult::PARAMETER_TOLERANCE ||
result->termination == TrackRegionResult::FUNCTION_TOLERANCE ||
result->termination == TrackRegionResult::GRADIENT_TOLERANCE ||
if (result->termination == TrackRegionResult::CONVERGENCE ||
result->termination == TrackRegionResult::SOURCE_OUT_OF_BOUNDS ||
result->termination == TrackRegionResult::DESTINATION_OUT_OF_BOUNDS ||
result->termination == TrackRegionResult::INSUFFICIENT_PATTERN_AREA) {
@ -1488,7 +1486,7 @@ void TemplatedTrackRegion(const FloatImage &image1,
// TODO(keir): Update the result statistics.
// TODO(keir): Add a normalize-cross-correlation variant.
if (summary.termination_type == ceres::USER_ABORT) {
if (summary.termination_type == ceres::USER_FAILURE) {
result->termination = TrackRegionResult::FELL_OUT_OF_BOUNDS;
return;
}
@ -1500,8 +1498,7 @@ void TemplatedTrackRegion(const FloatImage &image1,
}
// Avoid computing correlation for tracking failures.
HANDLE_TERMINATION(DID_NOT_RUN);
HANDLE_TERMINATION(NUMERICAL_FAILURE);
HANDLE_TERMINATION(FAILURE);
// Otherwise, run a final correlation check.
if (options.minimum_correlation > 0.0) {
@ -1520,13 +1517,11 @@ void TemplatedTrackRegion(const FloatImage &image1,
// than Ceres's parameter tolerance, which operates on the raw parameter
// values rather than the pixel shifts of the patch corners.
if (summary.termination_type == ceres::USER_SUCCESS) {
result->termination = TrackRegionResult::PARAMETER_TOLERANCE;
result->termination = TrackRegionResult::CONVERGENCE;
return;
}
HANDLE_TERMINATION(PARAMETER_TOLERANCE);
HANDLE_TERMINATION(FUNCTION_TOLERANCE);
HANDLE_TERMINATION(GRADIENT_TOLERANCE);
HANDLE_TERMINATION(CONVERGENCE);
HANDLE_TERMINATION(NO_CONVERGENCE);
#undef HANDLE_TERMINATION
};

View File

@ -110,12 +110,9 @@ struct TrackRegionOptions {
struct TrackRegionResult {
enum Termination {
// Ceres termination types, duplicated; though, not the int values.
PARAMETER_TOLERANCE,
FUNCTION_TOLERANCE,
GRADIENT_TOLERANCE,
CONVERGENCE,
NO_CONVERGENCE,
DID_NOT_RUN,
NUMERICAL_FAILURE,
FAILURE,
// Libmv specific errors.
SOURCE_OUT_OF_BOUNDS,

View File

@ -1,3 +1,147 @@
commit 2b16b0080b6e673eaaf9ed478c9e971d9fcd65de
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Fri Dec 20 15:22:26 2013 -0800
CompressedRowSparseMatrix::AppendRows and DeleteRows bugfix.
CompressedRowSparseMatrix can store the row and column block structure
but the AppendRows and DeleteRows methods did not pay attention to them.
This meant that it was possible to get to a CompressedRowSparseMatrix
whose block structure did not match the contents of the matrix.
This change fixes this problem.
Change-Id: I1b3c807fc03d8c049ee20511e2bc62806d211b81
commit 27bb4a8589c47a65b5ea2c01872a903043d0ef74
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Wed Dec 18 13:06:58 2013 -0800
Handle empty problems consistently.
Until now Ceres was inconsistent in the way it handled a solve
call on an "empty" Problem. If the problem did not contain
any residual or parameter blocks, it failed. However, if after
constructing the reduced program, the problem was found to not
contain any modifiable parameter blocks, it was considered a valid
problem which had a constant objective function value.
When creating problems automatically, it is often the case that
an empty problem is a corner case. This change makes handling this
corner case consistent with the rest of Ceres logic.
Change-Id: Ia9da09fbf5d5cd7eae6b39a92c1976b8645db9fe
commit dcee120bac04911bf01d8365bddca87c74ce2af9
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Sat Dec 7 21:48:56 2013 -0800
Consolidate SolverTerminationType enum.
1. Rename SolverTerminationType to TerminationType.
2. Consolidate the enum as
a. CONVERGENCE - subsumes FUNCTION_TOLERANCE, PARAMETER_TOLERANCE and GRADIENT_TOLERANCE
b. NO_CONVERGENCE
c. FAILURE - captures all kinds of failures including DID_NOT_RUN.
d. USER_SUCCESS
e. USER_FAILURE
3. Solver::Summary::error is renamed to be Solver::Summary::message, to both
reduce confusion as well as capture its true meaning.
Change-Id: I27a382e66e67f5a4750d0ee914d941f6b53c326d
commit d1cf320bb4f032cb14b20114a29ce2d867307492
Author: Sergey Sharybin <sergey.vfx@gmail.com>
Date: Thu Nov 28 23:11:11 2013 +0600
Made collections port compatible with MSVC2008
The issue was caused by the fact that in this version
of MSVC unordered_map class is defined in <unordered_map>
header file, but this file declares the class int std::tr1
namespace.
This confused existing assumption that if there's an
existing <unordered_map> file then class is declared
in std namespace.
Added an extra check to CMake which detects whether
it's std or std::tr1 which actually contains class
of unordered_map.
Change-Id: Ic5cf41913895a6ce8e791cc7602d7cf5492c34de
commit 324eccb5f6ce2a1a0061ec9f3c40778a029a2d97
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Tue Dec 3 09:28:14 2013 -0800
Restore the state of the Problem after a call to Evaluate.
Calling Problem::Evaluate mutates the state of the parameter blocks.
In particular, depending on the set and order of parameter blocks
passed to the evaluate call, it will change the internal indexing
used by the Program object used by ProblemImpl. This needs to be
undone before Evaluate returns, otherwise the Problem object
is in an invalid state.
To help with testing and debugging in the future, a new method
Program::IsValid has been added which checks whether the problem
has its parameter and residual blocks in the right state.
Thanks to Stefan Leutenegger for reporting this.
Change-Id: I209b486a31433f0cbb58b570047649eca6d42b56
commit 3b1ad31a1fe89fe0bd78e1fffdf22d47d43faaf5
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Mon Dec 2 15:43:20 2013 -0800
Fix build breakage on old versions of SuiteSparse.
Change-Id: I2a061615fc374abef2ed323c298359002a6fc5f1
commit 5fd480692b0a0c87e2af2f5a8754042a14f5f089
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Mon Dec 2 12:16:53 2013 -0800
Add more documentation to the linear solver enums.
Change-Id: Id57f76f73fa38043c0b6729972b1de8578ad7ede
commit d73acd035359886dfa1c5762b01c6f6449edcab8
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Mon Dec 2 12:02:03 2013 -0800
Lint cleanup from William Rucklidge.
Change-Id: I8abcfd369f41b895ce746a21a35f250fe05c39d1
commit 3faac6a28cec4c99c41421d3f585f3786be443b3
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Thu Nov 28 07:13:26 2013 -0800
More lint cleanups and breakage fixes.
The previous CL was a premature submit due to lack of coffee.
Change-Id: Id425d0ef332f569a954f0413e6b1ae6087f40f30
commit ed92366592a951041bd0367c24006101ef7b6286
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Thu Nov 28 06:50:43 2013 -0800
Lint cleanup from William Rucklidge.
Change-Id: I745810f5496a1b93263b20ff140f8883da61995e
commit 34b6359f39884683f2bbf06c93040afd42ae135d
Author: Sergey Sharybin <sergey.vfx@gmail.com>
Date: Thu Nov 28 18:51:34 2013 +0600
Fix compilation error after recent enum rename in 33e01b9
Change-Id: I920aa4754df6b013e86f0e77c61338d7a80e7f45
commit 33e01b9c5e1416fe29c55ac0332cdca21c053c83
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Wed Nov 27 10:24:03 2013 -0800
@ -537,148 +681,3 @@ Date: Sun Oct 27 21:38:13 2013 -0700
are not exposed. This would be done in a future change.
Change-Id: I7ddc36790943f24b19c7f08b10694ae9a822f5c9
commit 5a161a2b9653489ee9040f054b24df971e6b9bbc
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Tue Oct 29 22:08:15 2013 -0700
Template specializations for PartitionedMatrixView.
This speeds up the matrix vector products in the
IterativeSchurSolver by upto 40%.
Change-Id: Ib5e8d77c7269cf5ffdd2d161893734bb6d38215d
commit e5ce1170bc9993085c81a788e16eb48f1b2fdb97
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Tue Oct 29 07:40:51 2013 -0700
Minor bug fix to autodiff.h
Change-Id: Ib41050a2f2ba1898c71ff19d74f8eca2496212c0
commit 9e9a7d6ca0e75727293f94452d602f02b56d10ba
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Tue Oct 29 06:54:44 2013 -0700
Documentation update.
Add documentation for the new methods added to Problem.
Fix a bunch of ReST bugs.
Change-Id: I8a79a84040cfa8a679cc5355baccbe6d69bc9e70
commit c6bafdd02c33ec0ccb705578d83e4f601ddeedea
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Mon Oct 28 19:38:08 2013 -0700
Comments from Jim Roseborough.
1. Fix the tolerance on the rotation matrix conversion test.
2. Fix some out of date comments.
Change-Id: I65e80da1f96d7b4d9ac0630ad8cb708c41739840
commit fda69b52130955479591e8f03f97b1cfceca369f
Author: Keir Mierle <mierle@gmail.com>
Date: Thu Oct 10 00:25:24 2013 -0700
Export the structure of a problem to the public API
This adds three new public methods to ceres::Problem:
Problem::GetResidualBlocks()
Problem::GetParameterBlocksForResidualBlock()
Problem::GetResidualBlocksForParameterBlock()
These permit access to the underlying graph structure of the problem.
Change-Id: I55a4c7f0e5f325f140cb4830e7a7070554594650
commit 63bcdffa7d188b8d8c5309a62c255ba33f061764
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Sun Oct 27 21:34:13 2013 -0700
Add the 2_d_d SchurEliminator specialization.
This occurs far too often in bundle adjustment problems to be ignored.
Change-Id: Ib137f1566acf5fffa63e50a55fe8e78ea9eb1c14
commit 602096c91363a0b9384f887a15c82e2dac1fb923
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Sun Oct 27 05:09:38 2013 -0700
Move CERES_HASH_NAMESPACE macros to collections_port.h
Now that we have a clearer understanding of the naming rules
there is no need for these macro definitions to be done in
the cmake file.
This cleans up the compilation command line.
Change-Id: Idc8fc7a7c9376e021dc4790af66e599105351917
commit f6b67df54ad6daa7036f5b6619243f722d678892
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Fri Oct 25 06:24:19 2013 -0700
Fix handling of unordered_map/unordered_set on OSX 10.9.0.
Depending on the compiler + standard library combination,
unordered_map/set may or may not be available. If available
they maybe in the std or the std::tr1 namespaces.
Apple switched to using libc++ with 10.9.0 which places
unordered_map in std, breaking our assumptions about the
platform.
This change refactors our logic for dealing with the namespace
switching, making it a three state thing rather than two. There
are three defines now, CERES_NO_UNORDERED_MAP, CERES_STD_UNORDERED_MAP
and CERES_TR1_UNORDERED_MAP. Earlier the first two were conflated
into one, leading to the breakage.
Change-Id: I904fe8c49529169bdefa9f2ee6d629e7eab0b855
commit 21d6a99fe68e99fa51db32d55f587b42ef9a476c
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Fri Oct 25 10:20:24 2013 -0700
Fix AngleAxisToRotationMatrix near zero.
The Taylor series approximation had its sign flipped and the
tests did not catch it since we were switching exactly at zero,
which was not getting triggered.
This changes modifies the tolerance, adds a test that triggers
and fixes the bug.
Thanks to Michael Samples for reporting this.
Change-Id: I6f92f6348e5d4421ffe194fba92c04285449484c
commit 0e2743e24d013b25109396cfa0d8d0f1e8e84964
Author: Sameer Agarwal <sameeragarwal@google.com>
Date: Wed Oct 23 14:51:07 2013 -0700
Add BlockRandomAccessDiagonalMatrix.
This class is used in the SchurJacobiPreconditioner for
storing the preconditioner matrix. Using it speeds up
the computation of the preconditioner by ~15% due to
the elimination of a hash table lookup.
Change-Id: Iba2b34aad0d9eb9bcb7f6e6fad16aa416aac0d2a
commit 6a2bcaa1d55d38bc10d043f1458657caac2be7a7
Author: Alex Stewart <alexs.mac@gmail.com>
Date: Wed Oct 23 14:06:44 2013 +0100
Adding explicit link to libm for pure-C curve fitting example.
- Any pure-C program #including <math.h> will need to link against
libm, some compilers will let an indirect link slide (via Ceres in
this case) but some won't.
Change-Id: I6890702fa0d2c3fbb747f0f81fc3fa3631839de4

View File

@ -714,14 +714,15 @@ class Solver {
// termination.
string FullReport() const;
bool IsSolutionUsable() const;
// Minimizer summary -------------------------------------------------
MinimizerType minimizer_type;
SolverTerminationType termination_type;
TerminationType termination_type;
// If the solver did not run, or there was a failure, a
// description of the error.
string error;
// Reason why the solver terminated.
string message;
// Cost of the problem (value of the objective function) before
// the optimization.

View File

@ -301,41 +301,42 @@ enum DoglegType {
SUBSPACE_DOGLEG
};
enum SolverTerminationType {
// The minimizer did not run at all; usually due to errors in the user's
// Problem or the solver options.
DID_NOT_RUN,
enum TerminationType {
// Minimizer terminated because one of the convergence criterion set
// by the user was satisfied.
//
// 1. (new_cost - old_cost) < function_tolerance * old_cost;
// 2. max_i |gradient_i| < gradient_tolerance * max_i|initial_gradient_i|
// 3. |step|_2 <= parameter_tolerance * ( |x|_2 + parameter_tolerance)
//
// The user's parameter blocks will be updated with the solution.
CONVERGENCE,
// The solver ran for maximum number of iterations specified by the
// user, but none of the convergence criterion specified by the user
// were met.
// The solver ran for maximum number of iterations or maximum amount
// of time specified by the user, but none of the convergence
// criterion specified by the user were met. The user's parameter
// blocks will be updated with the solution found so far.
NO_CONVERGENCE,
// Minimizer terminated because
// (new_cost - old_cost) < function_tolerance * old_cost;
FUNCTION_TOLERANCE,
// Minimizer terminated because
// max_i |gradient_i| < gradient_tolerance * max_i|initial_gradient_i|
GRADIENT_TOLERANCE,
// Minimized terminated because
// |step|_2 <= parameter_tolerance * ( |x|_2 + parameter_tolerance)
PARAMETER_TOLERANCE,
// The minimizer terminated because it encountered a numerical error
// that it could not recover from.
NUMERICAL_FAILURE,
// The minimizer terminated because of an error. The user's
// parameter blocks will not be updated.
FAILURE,
// Using an IterationCallback object, user code can control the
// minimizer. The following enums indicate that the user code was
// responsible for termination.
//
// Minimizer terminated successfully because a user
// IterationCallback returned SOLVER_TERMINATE_SUCCESSFULLY.
//
// The user's parameter blocks will be updated with the solution.
USER_SUCCESS,
// User's IterationCallback returned SOLVER_ABORT.
USER_ABORT,
// User's IterationCallback returned SOLVER_TERMINATE_SUCCESSFULLY
USER_SUCCESS
// Minimizer terminated because because a user IterationCallback
// returned SOLVER_ABORT.
//
// The user's parameter blocks will not be updated.
USER_FAILURE
};
// Enums used by the IterationCallback instances to indicate to the
@ -459,7 +460,7 @@ bool StringToCovarianceAlgorithmType(
string value,
CovarianceAlgorithmType* type);
const char* SolverTerminationTypeToString(SolverTerminationType type);
const char* TerminationTypeToString(TerminationType type);
bool IsSchurType(LinearSolverType type);
bool IsSparseLinearAlgebraLibraryTypeAvailable(

View File

@ -31,6 +31,7 @@
#include "ceres/compressed_row_sparse_matrix.h"
#include <algorithm>
#include <numeric>
#include <vector>
#include "ceres/crs_matrix.h"
#include "ceres/internal/port.h"
@ -215,11 +216,28 @@ void CompressedRowSparseMatrix::DeleteRows(int delta_rows) {
num_rows_ -= delta_rows;
rows_.resize(num_rows_ + 1);
// Walk the list of row blocks untill we reach the new number of
// rows and then drop the rest of the row blocks.
int num_row_blocks = 0;
int num_rows = 0;
while (num_row_blocks < row_blocks_.size() && num_rows < num_rows_) {
num_rows += row_blocks_[num_row_blocks];
++num_row_blocks;
}
row_blocks_.resize(num_row_blocks);
}
void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) {
CHECK_EQ(m.num_cols(), num_cols_);
CHECK(row_blocks_.size() == 0 || m.row_blocks().size() !=0)
<< "Cannot append a matrix with row blocks to one without and vice versa."
<< "This matrix has : " << row_blocks_.size() << " row blocks."
<< "The matrix being appended has: " << m.row_blocks().size()
<< " row blocks.";
if (cols_.size() < num_nonzeros() + m.num_nonzeros()) {
cols_.resize(num_nonzeros() + m.num_nonzeros());
values_.resize(num_nonzeros() + m.num_nonzeros());
@ -239,6 +257,7 @@ void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) {
}
num_rows_ += m.num_rows();
row_blocks_.insert(row_blocks_.end(), m.row_blocks().begin(), m.row_blocks().end());
}
void CompressedRowSparseMatrix::ToTextFile(FILE* file) const {

View File

@ -441,23 +441,23 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSparseCholesky() {
cholmod_jacobian_view.sorted = 1;
cholmod_jacobian_view.packed = 1;
string status;
cholmod_factor* factor = ss.AnalyzeCholesky(&cholmod_jacobian_view, &status);
string message;
cholmod_factor* factor = ss.AnalyzeCholesky(&cholmod_jacobian_view, &message);
event_logger.AddEvent("Symbolic Factorization");
if (factor == NULL) {
LOG(ERROR) << "Covariance estimation failed. "
<< "CHOLMOD symbolic cholesky factorization returned with: "
<< status;
<< message;
return false;
}
LinearSolverTerminationType termination_type =
ss.Cholesky(&cholmod_jacobian_view, factor, &status);
ss.Cholesky(&cholmod_jacobian_view, factor, &message);
event_logger.AddEvent("Numeric Factorization");
if (termination_type != LINEAR_SOLVER_SUCCESS) {
LOG(ERROR) << "Covariance estimation failed. "
<< "CHOLMOD numeric cholesky factorization returned with: "
<< status;
<< message;
ss.Free(factor);
return false;
}
@ -470,7 +470,7 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSparseCholesky() {
LOG(ERROR) << "Cholesky factorization of J'J is not reliable. "
<< "Reciprocal condition number: "
<< reciprocal_condition_number << " "
<< "min_reciprocal_condition_number : "
<< "min_reciprocal_condition_number: "
<< options_.min_reciprocal_condition_number;
ss.Free(factor);
return false;
@ -506,7 +506,7 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSparseCholesky() {
}
rhs_x[r] = 1.0;
cholmod_dense* solution = ss.Solve(factor, rhs);
cholmod_dense* solution = ss.Solve(factor, rhs, &message);
double* solution_x = reinterpret_cast<double*>(solution->x);
for (int idx = row_begin; idx < row_end; ++idx) {
const int c = cols[idx];
@ -822,7 +822,7 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() {
LOG(ERROR) << "Cholesky factorization of J'J is not reliable. "
<< "Reciprocal condition number: "
<< singular_value_ratio * singular_value_ratio << " "
<< "min_reciprocal_condition_number : "
<< "min_reciprocal_condition_number: "
<< options_.min_reciprocal_condition_number;
return false;
}

View File

@ -70,7 +70,7 @@ LinearSolverTerminationType LAPACK::SolveInPlaceUsingCholesky(
int num_rows,
const double* in_lhs,
double* rhs_and_solution,
string* status) {
string* message) {
#ifdef CERES_NO_LAPACK
LOG(FATAL) << "Ceres was built without a BLAS library.";
return LINEAR_SOLVER_FATAL_ERROR;
@ -91,10 +91,10 @@ LinearSolverTerminationType LAPACK::SolveInPlaceUsingCholesky(
}
if (info > 0) {
*status =
*message =
StringPrintf(
"LAPACK::dpotrf numerical failure. "
"The leading minor of order %d is not positive definite.", info);
"The leading minor of order %d is not positive definite.", info);
return LINEAR_SOLVER_FAILURE;
}
@ -107,7 +107,7 @@ LinearSolverTerminationType LAPACK::SolveInPlaceUsingCholesky(
return LINEAR_SOLVER_FATAL_ERROR;
}
*status = "Success";
*message = "Success";
return LINEAR_SOLVER_SUCCESS;
#endif
};
@ -138,7 +138,7 @@ int LAPACK::EstimateWorkSizeForQR(int num_rows, int num_cols) {
LOG(FATAL) << "Congratulations, you found a bug in Ceres."
<< "Please report it."
<< "LAPACK::dgels fatal error."
<< "Argument: " << info << " is invalid.";
<< "Argument: " << -info << " is invalid.";
}
return static_cast<int>(work);
#endif
@ -151,7 +151,7 @@ LinearSolverTerminationType LAPACK::SolveInPlaceUsingQR(
int work_size,
double* work,
double* rhs_and_solution,
string* status) {
string* message) {
#ifdef CERES_NO_LAPACK
LOG(FATAL) << "Ceres was built without a LAPACK library.";
return LINEAR_SOLVER_FATAL_ERROR;
@ -184,7 +184,7 @@ LinearSolverTerminationType LAPACK::SolveInPlaceUsingQR(
<< "Argument: " << -info << " is invalid.";
}
*status = "Success.";
*message = "Success.";
return LINEAR_SOLVER_SUCCESS;
#endif
}

View File

@ -51,14 +51,14 @@ class LAPACK {
//
// This function uses the LAPACK dpotrf and dpotrs routines.
//
// The return value and the status string together describe whether
// The return value and the message string together describe whether
// the solver terminated successfully or not and if so, what was the
// reason for failure.
static LinearSolverTerminationType SolveInPlaceUsingCholesky(
int num_rows,
const double* lhs,
double* rhs_and_solution,
string* status);
string* message);
// The SolveUsingQR function requires a buffer for its temporary
// computation. This function given the size of the lhs matrix will
@ -81,7 +81,7 @@ class LAPACK {
//
// This function uses the LAPACK dgels routine.
//
// The return value and the status string together describe whether
// The return value and the message string together describe whether
// the solver terminated successfully or not and if so, what was the
// reason for failure.
static LinearSolverTerminationType SolveInPlaceUsingQR(
@ -91,7 +91,7 @@ class LAPACK {
int work_size,
double* work,
double* rhs_and_solution,
string* status);
string* message);
};
} // namespace internal

View File

@ -126,9 +126,9 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
// Do initial cost and Jacobian evaluation.
if (!Evaluate(evaluator, x, &current_state)) {
summary->error = "Terminating: Cost and gradient evaluation failed.";
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->message = "Terminating: Cost and gradient evaluation failed.";
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
return;
}
@ -146,14 +146,14 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
options.gradient_tolerance * initial_gradient_max_norm;
if (iteration_summary.gradient_max_norm <= absolute_gradient_tolerance) {
summary->error =
summary->message =
StringPrintf("Terminating: Gradient tolerance reached. "
"Relative gradient max norm: %e <= %e",
iteration_summary.gradient_max_norm /
initial_gradient_max_norm,
options.gradient_tolerance);
summary->termination_type = GRADIENT_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
@ -198,10 +198,10 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
scoped_ptr<LineSearch>
line_search(LineSearch::Create(options.line_search_type,
line_search_options,
&summary->error));
&summary->message));
if (line_search.get() == NULL) {
summary->termination_type = DID_NOT_RUN;
LOG_IF(ERROR, is_not_silent) << summary->error;
summary->termination_type = FAILURE;
LOG_IF(ERROR, is_not_silent) << summary->message;
return;
}
@ -215,18 +215,18 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
iteration_start_time = WallTimeInSeconds();
if (iteration_summary.iteration >= options.max_num_iterations) {
summary->error = "Terminating: Maximum number of iterations reached.";
summary->message = "Terminating: Maximum number of iterations reached.";
summary->termination_type = NO_CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->error;
VLOG_IF(1, is_not_silent) << summary->message;
break;
}
const double total_solver_time = iteration_start_time - start_time +
summary->preprocessor_time_in_seconds;
if (total_solver_time >= options.max_solver_time_in_seconds) {
summary->error = "Terminating: Maximum solver time reached.";
summary->message = "Terminating: Maximum solver time reached.";
summary->termination_type = NO_CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->error;
VLOG_IF(1, is_not_silent) << summary->message;
break;
}
@ -251,12 +251,12 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
// Line search direction failed to generate a new direction, and we
// have already reached our specified maximum number of restarts,
// terminate optimization.
summary->error =
StringPrintf("Termination: Line search direction failure: specified "
summary->message =
StringPrintf("Terminating: Line search direction failure: specified "
"max_num_line_search_direction_restarts: %d reached.",
options.max_num_line_search_direction_restarts);
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
break;
} else if (!line_search_status) {
// Restart line search direction with gradient descent on first iteration
@ -299,14 +299,14 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
// direction in a line search, most likely cause for this being violated
// would be a numerical failure in the line search direction calculation.
if (initial_step_size < 0.0) {
summary->error =
summary->message =
StringPrintf("Numerical failure in line search, initial_step_size is "
"negative: %.5e, directional_derivative: %.5e, "
"(current_cost - previous_cost): %.5e",
initial_step_size, current_state.directional_derivative,
(current_state.cost - previous_state.cost));
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
break;
}
@ -315,15 +315,15 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
current_state.directional_derivative,
&line_search_summary);
if (!line_search_summary.success) {
summary->error =
summary->message =
StringPrintf("Numerical failure in line search, failed to find "
"a valid step size, (did not run out of iterations) "
"using initial_step_size: %.5e, initial_cost: %.5e, "
"initial_gradient: %.5e.",
initial_step_size, current_state.cost,
current_state.directional_derivative);
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
summary->termination_type = FAILURE;
break;
}
@ -355,14 +355,14 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
iteration_summary.gradient_norm = sqrt(current_state.gradient_squared_norm);
if (iteration_summary.gradient_max_norm <= absolute_gradient_tolerance) {
summary->error =
summary->message =
StringPrintf("Terminating: Gradient tolerance reached. "
"Relative gradient max norm: %e <= %e. ",
(iteration_summary.gradient_max_norm /
initial_gradient_max_norm),
options.gradient_tolerance);
summary->termination_type = GRADIENT_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
break;
}
@ -370,14 +370,14 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
const double absolute_function_tolerance =
options.function_tolerance * previous_state.cost;
if (fabs(iteration_summary.cost_change) < absolute_function_tolerance) {
summary->error =
summary->message =
StringPrintf("Terminating. Function tolerance reached. "
"|cost_change|/cost: %e <= %e",
fabs(iteration_summary.cost_change) /
previous_state.cost,
options.function_tolerance);
summary->termination_type = FUNCTION_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}

View File

@ -62,7 +62,10 @@ enum LinearSolverTerminationType {
// the linear system being poorly conditioned.
LINEAR_SOLVER_FAILURE,
// Solver failed with a fatal error that cannot be recovered from.
// Solver failed with a fatal error that cannot be recovered from,
// e.g. CHOLMOD ran out of memory when computing the symbolic or
// numeric factorization or an underlying library was called with
// the wrong arguments.
LINEAR_SOLVER_FATAL_ERROR
};

View File

@ -54,7 +54,7 @@ bool Minimizer::RunCallbacks(const vector<IterationCallback*> callbacks,
VLOG(1) << "Terminating: User callback returned USER_SUCCESS.";
return false;
case SOLVER_ABORT:
summary->termination_type = USER_ABORT;
summary->termination_type = USER_FAILURE;
VLOG(1) << "Terminating: User callback returned USER_ABORT.";
return false;
default:

View File

@ -638,6 +638,9 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
for (int i = 0; i < variable_parameter_blocks.size(); ++i) {
variable_parameter_blocks[i]->SetVarying();
}
program_->SetParameterBlockStatePtrsToUserStatePtrs();
program_->SetParameterOffsetsAndIndex();
return false;
}
@ -696,6 +699,8 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options,
}
}
program_->SetParameterBlockStatePtrsToUserStatePtrs();
program_->SetParameterOffsetsAndIndex();
return status;
}

View File

@ -140,6 +140,36 @@ void Program::SetParameterOffsetsAndIndex() {
}
}
bool Program::IsValid() const {
for (int i = 0; i < residual_blocks_.size(); ++i) {
const ResidualBlock* residual_block = residual_blocks_[i];
if (residual_block->index() != i) {
LOG(WARNING) << "Residual block: " << i
<< " has incorrect index: " << residual_block->index();
return false;
}
}
int state_offset = 0;
int delta_offset = 0;
for (int i = 0; i < parameter_blocks_.size(); ++i) {
const ParameterBlock* parameter_block = parameter_blocks_[i];
if (parameter_block->index() != i ||
parameter_block->state_offset() != state_offset ||
parameter_block->delta_offset() != delta_offset) {
LOG(WARNING) << "Parameter block: " << i
<< "has incorrect indexing information: "
<< parameter_block->ToString();
return false;
}
state_offset += parameter_blocks_[i]->Size();
delta_offset += parameter_blocks_[i]->LocalSize();
}
return true;
}
int Program::NumResidualBlocks() const {
return residual_blocks_.size();
}

View File

@ -99,6 +99,10 @@ class Program {
// position of the parameter in the state and delta vector respectively.
void SetParameterOffsetsAndIndex();
// Check if the internal state of the program (the indexing and the
// offsets) are correct.
bool IsValid() const;
// See problem.h for what these do.
int NumParameterBlocks() const;
int NumParameters() const;

View File

@ -85,8 +85,8 @@ Solver::Summary::Summary()
// Invalid values for most fields, to ensure that we are not
// accidentally reporting default values.
: minimizer_type(TRUST_REGION),
termination_type(DID_NOT_RUN),
error("ceres::Solve was not called."),
termination_type(FAILURE),
message("ceres::Solve was not called."),
initial_cost(-1.0),
final_cost(-1.0),
fixed_cost(-1.0),
@ -131,64 +131,40 @@ Solver::Summary::Summary()
max_lbfgs_rank(-1) {
}
string Solver::Summary::BriefReport() const {
string report = "Ceres Solver Report: ";
if (termination_type == DID_NOT_RUN) {
return report + "Termination: DID_NOT_RUN, because " + error;
}
internal::StringAppendF(&report, "Iterations: %d",
num_successful_steps + num_unsuccessful_steps);
internal::StringAppendF(&report, ", Initial cost: %e", initial_cost);
// If the solver failed or was aborted, then the final_cost has no
// meaning.
if (termination_type != NUMERICAL_FAILURE &&
termination_type != USER_ABORT) {
internal::StringAppendF(&report, ", Final cost: %e", final_cost);
}
internal::StringAppendF(&report, ", Termination: %s.",
SolverTerminationTypeToString(termination_type));
return report;
};
using internal::StringAppendF;
using internal::StringPrintf;
string Solver::Summary::BriefReport() const {
return StringPrintf("Ceres Solver Report: "
"Iterations: %d, "
"Initial cost: %e, "
"Final cost: %e, "
"Termination: %s",
num_successful_steps + num_unsuccessful_steps,
initial_cost,
final_cost,
TerminationTypeToString(termination_type));
};
string Solver::Summary::FullReport() const {
string report =
"\n"
"Ceres Solver Report\n"
"-------------------\n";
if (termination_type == DID_NOT_RUN) {
StringAppendF(&report, " Original\n");
StringAppendF(&report, "Parameter blocks % 10d\n", num_parameter_blocks);
StringAppendF(&report, "Parameters % 10d\n", num_parameters);
if (num_effective_parameters != num_parameters) {
StringAppendF(&report, "Effective parameters% 10d\n", num_parameters);
}
StringAppendF(&report, "Residual blocks % 10d\n",
num_residual_blocks);
StringAppendF(&report, "Residuals % 10d\n\n",
num_residuals);
} else {
StringAppendF(&report, "%45s %21s\n", "Original", "Reduced");
StringAppendF(&report, "Parameter blocks % 25d% 25d\n",
num_parameter_blocks, num_parameter_blocks_reduced);
StringAppendF(&report, "Parameters % 25d% 25d\n",
num_parameters, num_parameters_reduced);
if (num_effective_parameters_reduced != num_parameters_reduced) {
StringAppendF(&report, "Effective parameters% 25d% 25d\n",
num_effective_parameters, num_effective_parameters_reduced);
}
StringAppendF(&report, "Residual blocks % 25d% 25d\n",
num_residual_blocks, num_residual_blocks_reduced);
StringAppendF(&report, "Residual % 25d% 25d\n",
num_residuals, num_residuals_reduced);
StringAppendF(&report, "%45s %21s\n", "Original", "Reduced");
StringAppendF(&report, "Parameter blocks % 25d% 25d\n",
num_parameter_blocks, num_parameter_blocks_reduced);
StringAppendF(&report, "Parameters % 25d% 25d\n",
num_parameters, num_parameters_reduced);
if (num_effective_parameters_reduced != num_parameters_reduced) {
StringAppendF(&report, "Effective parameters% 25d% 25d\n",
num_effective_parameters, num_effective_parameters_reduced);
}
StringAppendF(&report, "Residual blocks % 25d% 25d\n",
num_residual_blocks, num_residual_blocks_reduced);
StringAppendF(&report, "Residual % 25d% 25d\n",
num_residuals, num_residuals_reduced);
if (minimizer_type == TRUST_REGION) {
// TRUST_SEARCH HEADER
@ -314,17 +290,10 @@ string Solver::Summary::FullReport() const {
num_threads_given, num_threads_used);
}
if (termination_type == DID_NOT_RUN) {
StringAppendF(&report, "Termination: %20s\n",
"DID_NOT_RUN");
StringAppendF(&report, "Reason: %s\n", error.c_str());
return report;
}
StringAppendF(&report, "\nCost:\n");
StringAppendF(&report, "Initial % 30e\n", initial_cost);
if (termination_type != NUMERICAL_FAILURE &&
termination_type != USER_ABORT) {
if (termination_type != FAILURE &&
termination_type != USER_FAILURE) {
StringAppendF(&report, "Final % 30e\n", final_cost);
StringAppendF(&report, "Change % 30e\n",
initial_cost - final_cost);
@ -376,8 +345,14 @@ string Solver::Summary::FullReport() const {
total_time_in_seconds);
StringAppendF(&report, "Termination: %25s\n",
SolverTerminationTypeToString(termination_type));
TerminationTypeToString(termination_type));
return report;
};
bool Solver::Summary::IsSolutionUsable() const {
return (termination_type == CONVERGENCE ||
termination_type == NO_CONVERGENCE ||
termination_type == USER_SUCCESS);
}
} // namespace ceres

View File

@ -208,6 +208,22 @@ void SummarizeOrdering(ParameterBlockOrdering* ordering,
}
}
void SummarizeGivenProgram(const Program& program, Solver::Summary* summary) {
summary->num_parameter_blocks = program.NumParameterBlocks();
summary->num_parameters = program.NumParameters();
summary->num_effective_parameters = program.NumEffectiveParameters();
summary->num_residual_blocks = program.NumResidualBlocks();
summary->num_residuals = program.NumResiduals();
}
void SummarizeReducedProgram(const Program& program, Solver::Summary* summary) {
summary->num_parameter_blocks_reduced = program.NumParameterBlocks();
summary->num_parameters_reduced = program.NumParameters();
summary->num_effective_parameters_reduced = program.NumEffectiveParameters();
summary->num_residual_blocks_reduced = program.NumResidualBlocks();
summary->num_residuals_reduced = program.NumResiduals();
}
} // namespace
void SolverImpl::TrustRegionMinimize(
@ -351,29 +367,10 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
*CHECK_NOTNULL(summary) = Solver::Summary();
summary->minimizer_type = TRUST_REGION;
summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
summary->num_parameters = problem_impl->NumParameters();
summary->num_effective_parameters =
original_program->NumEffectiveParameters();
summary->num_residual_blocks = problem_impl->NumResidualBlocks();
summary->num_residuals = problem_impl->NumResiduals();
// Empty programs are usually a user error.
if (summary->num_parameter_blocks == 0) {
summary->error = "Problem contains no parameter blocks.";
LOG(ERROR) << summary->error;
return;
}
if (summary->num_residual_blocks == 0) {
summary->error = "Problem contains no residual blocks.";
LOG(ERROR) << summary->error;
return;
}
SummarizeGivenProgram(*original_program, summary);
SummarizeOrdering(original_options.linear_solver_ordering,
&(summary->linear_solver_ordering_given));
SummarizeOrdering(original_options.inner_iteration_ordering,
&(summary->inner_iteration_ordering_given));
@ -404,9 +401,9 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
if (options.trust_region_minimizer_iterations_to_dump.size() > 0 &&
options.trust_region_problem_dump_format_type != CONSOLE &&
options.trust_region_problem_dump_directory.empty()) {
summary->error =
summary->message =
"Solver::Options::trust_region_problem_dump_directory is empty.";
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
@ -434,8 +431,8 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
}
if (original_options.linear_solver_ordering != NULL) {
if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
LOG(ERROR) << summary->error;
if (!IsOrderingValid(original_options, problem_impl, &summary->message)) {
LOG(ERROR) << summary->message;
return;
}
event_logger.AddEvent("CheckOrdering");
@ -466,7 +463,7 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
problem_impl,
&summary->fixed_cost,
&summary->error));
&summary->message));
event_logger.AddEvent("CreateReducedProgram");
if (reduced_program == NULL) {
@ -475,30 +472,23 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
SummarizeOrdering(options.linear_solver_ordering,
&(summary->linear_solver_ordering_used));
summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
summary->num_parameters_reduced = reduced_program->NumParameters();
summary->num_effective_parameters_reduced =
reduced_program->NumEffectiveParameters();
summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
summary->num_residuals_reduced = reduced_program->NumResiduals();
SummarizeReducedProgram(*reduced_program, summary);
if (summary->num_parameter_blocks_reduced == 0) {
summary->preprocessor_time_in_seconds =
WallTimeInSeconds() - solver_start_time;
double post_process_start_time = WallTimeInSeconds();
LOG(INFO) << "Terminating: FUNCTION_TOLERANCE reached. "
<< "No non-constant parameter blocks found.";
summary->message =
"Terminating: Function tolerance reached. "
"No non-constant parameter blocks found.";
summary->termination_type = CONVERGENCE;
VLOG(1) << summary->message;
summary->initial_cost = summary->fixed_cost;
summary->final_cost = summary->fixed_cost;
// FUNCTION_TOLERANCE is the right convergence here, as we know
// that the objective function is constant and cannot be changed
// any further.
summary->termination_type = FUNCTION_TOLERANCE;
// Ensure the program state is set to the user parameters on the way out.
original_program->SetParameterBlockStatePtrsToUserStatePtrs();
original_program->SetParameterOffsetsAndIndex();
@ -509,7 +499,7 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
}
scoped_ptr<LinearSolver>
linear_solver(CreateLinearSolver(&options, &summary->error));
linear_solver(CreateLinearSolver(&options, &summary->message));
event_logger.AddEvent("CreateLinearSolver");
if (linear_solver == NULL) {
return;
@ -536,7 +526,7 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
problem_impl->parameter_map(),
reduced_program.get(),
&summary->error));
&summary->message));
event_logger.AddEvent("CreateEvaluator");
@ -556,7 +546,7 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
problem_impl->parameter_map(),
summary));
if (inner_iteration_minimizer == NULL) {
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
}
@ -590,8 +580,8 @@ void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
// If the user aborted mid-optimization or the optimization
// terminated because of a numerical failure, then return without
// updating user state.
if (summary->termination_type == USER_ABORT ||
summary->termination_type == NUMERICAL_FAILURE) {
if (summary->termination_type == USER_FAILURE ||
summary->termination_type == FAILURE) {
return;
}
@ -642,6 +632,8 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
*CHECK_NOTNULL(summary) = Solver::Summary();
summary->minimizer_type = LINE_SEARCH;
SummarizeGivenProgram(*original_program, summary);
summary->line_search_direction_type =
original_options.line_search_direction_type;
summary->max_lbfgs_rank = original_options.max_lbfgs_rank;
@ -651,56 +643,50 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
summary->nonlinear_conjugate_gradient_type =
original_options.nonlinear_conjugate_gradient_type;
summary->num_parameter_blocks = original_program->NumParameterBlocks();
summary->num_parameters = original_program->NumParameters();
summary->num_residual_blocks = original_program->NumResidualBlocks();
summary->num_residuals = original_program->NumResiduals();
summary->num_effective_parameters =
original_program->NumEffectiveParameters();
// Validate values for configuration parameters supplied by user.
if ((original_options.line_search_direction_type == ceres::BFGS ||
original_options.line_search_direction_type == ceres::LBFGS) &&
original_options.line_search_type != ceres::WOLFE) {
summary->error =
summary->message =
string("Invalid configuration: require line_search_type == "
"ceres::WOLFE when using (L)BFGS to ensure that underlying "
"assumptions are guaranteed to be satisfied.");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
if (original_options.max_lbfgs_rank <= 0) {
summary->error =
summary->message =
string("Invalid configuration: require max_lbfgs_rank > 0");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
if (original_options.min_line_search_step_size <= 0.0) {
summary->error = "Invalid configuration: min_line_search_step_size <= 0.0.";
LOG(ERROR) << summary->error;
summary->message =
"Invalid configuration: min_line_search_step_size <= 0.0.";
LOG(ERROR) << summary->message;
return;
}
if (original_options.line_search_sufficient_function_decrease <= 0.0) {
summary->error =
summary->message =
string("Invalid configuration: require ") +
string("line_search_sufficient_function_decrease <= 0.0.");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
if (original_options.max_line_search_step_contraction <= 0.0 ||
original_options.max_line_search_step_contraction >= 1.0) {
summary->error = string("Invalid configuration: require ") +
summary->message = string("Invalid configuration: require ") +
string("0.0 < max_line_search_step_contraction < 1.0.");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
if (original_options.min_line_search_step_contraction <=
original_options.max_line_search_step_contraction ||
original_options.min_line_search_step_contraction > 1.0) {
summary->error = string("Invalid configuration: require ") +
summary->message = string("Invalid configuration: require ") +
string("max_line_search_step_contraction < ") +
string("min_line_search_step_contraction <= 1.0.");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
// Warn user if they have requested BISECTION interpolation, but constraints
@ -718,37 +704,24 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
<< original_options.min_line_search_step_contraction
<< ", prevent bisection (0.5) scaling, continuing with solve regardless.";
if (original_options.max_num_line_search_step_size_iterations <= 0) {
summary->error = string("Invalid configuration: require ") +
summary->message = string("Invalid configuration: require ") +
string("max_num_line_search_step_size_iterations > 0.");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
if (original_options.line_search_sufficient_curvature_decrease <=
original_options.line_search_sufficient_function_decrease ||
original_options.line_search_sufficient_curvature_decrease > 1.0) {
summary->error = string("Invalid configuration: require ") +
summary->message = string("Invalid configuration: require ") +
string("line_search_sufficient_function_decrease < ") +
string("line_search_sufficient_curvature_decrease < 1.0.");
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
if (original_options.max_line_search_step_expansion <= 1.0) {
summary->error = string("Invalid configuration: require ") +
summary->message = string("Invalid configuration: require ") +
string("max_line_search_step_expansion > 1.0.");
LOG(ERROR) << summary->error;
return;
}
// Empty programs are usually a user error.
if (summary->num_parameter_blocks == 0) {
summary->error = "Problem contains no parameter blocks.";
LOG(ERROR) << summary->error;
return;
}
if (summary->num_residual_blocks == 0) {
summary->error = "Problem contains no residual blocks.";
LOG(ERROR) << summary->error;
LOG(ERROR) << summary->message;
return;
}
@ -759,7 +732,6 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
// refactored to deal with the various bits of cleanups related to
// line search.
options.linear_solver_type = CGNR;
options.linear_solver_ordering = NULL;
options.inner_iteration_ordering = NULL;
@ -777,8 +749,8 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
summary->num_threads_used = options.num_threads;
if (original_options.linear_solver_ordering != NULL) {
if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
LOG(ERROR) << summary->error;
if (!IsOrderingValid(original_options, problem_impl, &summary->message)) {
LOG(ERROR) << summary->message;
return;
}
options.linear_solver_ordering =
@ -819,32 +791,23 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
problem_impl,
&summary->fixed_cost,
&summary->error));
&summary->message));
if (reduced_program == NULL) {
return;
}
summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
summary->num_parameters_reduced = reduced_program->NumParameters();
summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
summary->num_effective_parameters_reduced =
reduced_program->NumEffectiveParameters();
summary->num_residuals_reduced = reduced_program->NumResiduals();
SummarizeReducedProgram(*reduced_program, summary);
if (summary->num_parameter_blocks_reduced == 0) {
summary->preprocessor_time_in_seconds =
WallTimeInSeconds() - solver_start_time;
LOG(INFO) << "Terminating: FUNCTION_TOLERANCE reached. "
<< "No non-constant parameter blocks found.";
// FUNCTION_TOLERANCE is the right convergence here, as we know
// that the objective function is constant and cannot be changed
// any further.
summary->termination_type = FUNCTION_TOLERANCE;
summary->message =
"Terminating: Function tolerance reached. "
"No non-constant parameter blocks found.";
summary->termination_type = CONVERGENCE;
VLOG(1) << summary->message;
const double post_process_start_time = WallTimeInSeconds();
SetSummaryFinalCost(summary);
// Ensure the program state is set to the user parameters on the way out.
@ -859,7 +822,7 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
problem_impl->parameter_map(),
reduced_program.get(),
&summary->error));
&summary->message));
if (evaluator == NULL) {
return;
}
@ -886,8 +849,8 @@ void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
// If the user aborted mid-optimization or the optimization
// terminated because of a numerical failure, then return without
// updating user state.
if (summary->termination_type == USER_ABORT ||
summary->termination_type == NUMERICAL_FAILURE) {
if (summary->termination_type == USER_FAILURE ||
summary->termination_type == FAILURE) {
return;
}
@ -1456,7 +1419,7 @@ CoordinateDescentMinimizer* SolverImpl::CreateInnerIterationMinimizer(
for ( ; it != group_to_elements.end(); ++it) {
if (!IsParameterBlockSetIndependent(it->second,
program.residual_blocks())) {
summary->error =
summary->message =
StringPrintf("The user-provided "
"parameter_blocks_for_inner_iterations does not "
"form an independent set. Group Id: %d", it->first);
@ -1469,7 +1432,7 @@ CoordinateDescentMinimizer* SolverImpl::CreateInnerIterationMinimizer(
if (!inner_iteration_minimizer->Init(program,
parameter_map,
*ordering_ptr,
&summary->error)) {
&summary->message)) {
return NULL;
}

View File

@ -93,7 +93,7 @@ class SolverImpl {
static Program* CreateReducedProgram(Solver::Options* options,
ProblemImpl* problem_impl,
double* fixed_cost,
string* error);
string* message);
// Create the appropriate linear solver, taking into account any
// config changes decided by CreateTransformedProgram(). The
@ -101,7 +101,7 @@ class SolverImpl {
// selected; consider the case that the remaining elimininated
// blocks is zero after removing fixed blocks.
static LinearSolver* CreateLinearSolver(Solver::Options* options,
string* error);
string* message);
// Reorder the residuals for program, if necessary, so that the
// residuals involving e block (i.e., the first num_eliminate_block
@ -110,14 +110,14 @@ class SolverImpl {
static bool LexicographicallyOrderResidualBlocks(
const int num_eliminate_blocks,
Program* program,
string* error);
string* message);
// Create the appropriate evaluator for the transformed program.
static Evaluator* CreateEvaluator(
const Solver::Options& options,
const ProblemImpl::ParameterMap& parameter_map,
Program* program,
string* error);
string* message);
// Remove the fixed or unused parameter blocks and residuals
// depending only on fixed parameters from the program.
@ -131,17 +131,17 @@ class SolverImpl {
// fixed_cost.
//
// If a failure is encountered, the function returns false with a
// description of the failure in error.
// description of the failure in message.
static bool RemoveFixedBlocksFromProgram(
Program* program,
ParameterBlockOrdering* linear_solver_ordering,
ParameterBlockOrdering* inner_iteration_ordering,
double* fixed_cost,
string* error);
string* message);
static bool IsOrderingValid(const Solver::Options& options,
const ProblemImpl* problem_impl,
string* error);
string* message);
static bool IsParameterBlockSetIndependent(
const set<double*>& parameter_block_ptrs,
@ -176,7 +176,7 @@ class SolverImpl {
const ProblemImpl::ParameterMap& parameter_map,
const ParameterBlockOrdering* parameter_block_ordering,
Program* program,
string* error);
string* message);
// Sparse cholesky factorization routines when doing the sparse
// cholesky factorization of the Jacobian matrix, reorders its
@ -192,7 +192,7 @@ class SolverImpl {
const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
const ParameterBlockOrdering* parameter_block_ordering,
Program* program,
string* error);
string* message);
// Schur type solvers require that all parameter blocks eliminated
// by the Schur eliminator occur before others and the residuals be
@ -216,7 +216,7 @@ class SolverImpl {
const ProblemImpl::ParameterMap& parameter_map,
ParameterBlockOrdering* parameter_block_ordering,
Program* program,
string* error);
string* message);
// array contains a list of (possibly repeating) non-negative
// integers. Let us assume that we have constructed another array

View File

@ -112,8 +112,15 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingCXSparse(
if (per_solve_options.D != NULL) {
// Temporarily append a diagonal block to the A matrix, but undo
// it before returning the matrix to the user.
CompressedRowSparseMatrix D(per_solve_options.D, num_cols);
A->AppendRows(D);
scoped_ptr<CompressedRowSparseMatrix> regularizer;
if (A->col_blocks().size() > 0) {
regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
per_solve_options.D, A->col_blocks()));
} else {
regularizer.reset(new CompressedRowSparseMatrix(
per_solve_options.D, num_cols));
}
A->AppendRows(*regularizer);
}
VectorRef(x, num_cols).setZero();
@ -196,8 +203,15 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingSuiteSparse(
if (per_solve_options.D != NULL) {
// Temporarily append a diagonal block to the A matrix, but undo
// it before returning the matrix to the user.
CompressedRowSparseMatrix D(per_solve_options.D, num_cols);
A->AppendRows(D);
scoped_ptr<CompressedRowSparseMatrix> regularizer;
if (A->col_blocks().size() > 0) {
regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
per_solve_options.D, A->col_blocks()));
} else {
regularizer.reset(new CompressedRowSparseMatrix(
per_solve_options.D, num_cols));
}
A->AppendRows(*regularizer);
}
VectorRef(x, num_cols).setZero();

View File

@ -122,7 +122,7 @@ cholmod_dense* SuiteSparse::CreateDenseVector(const double* x,
}
cholmod_factor* SuiteSparse::AnalyzeCholesky(cholmod_sparse* A,
string* status) {
string* message) {
// Cholmod can try multiple re-ordering strategies to find a fill
// reducing ordering. Here we just tell it use AMD with automatic
// matrix dependence choice of supernodal versus simplicial
@ -137,8 +137,8 @@ cholmod_factor* SuiteSparse::AnalyzeCholesky(cholmod_sparse* A,
}
if (cc_.status != CHOLMOD_OK) {
*status = StringPrintf("cholmod_analyze failed. error code: %d",
cc_.status);
*message = StringPrintf("cholmod_analyze failed. error code: %d",
cc_.status);
return NULL;
}
@ -149,18 +149,18 @@ cholmod_factor* SuiteSparse::BlockAnalyzeCholesky(
cholmod_sparse* A,
const vector<int>& row_blocks,
const vector<int>& col_blocks,
string* status) {
string* message) {
vector<int> ordering;
if (!BlockAMDOrdering(A, row_blocks, col_blocks, &ordering)) {
return NULL;
}
return AnalyzeCholeskyWithUserOrdering(A, ordering, status);
return AnalyzeCholeskyWithUserOrdering(A, ordering, message);
}
cholmod_factor* SuiteSparse::AnalyzeCholeskyWithUserOrdering(
cholmod_sparse* A,
const vector<int>& ordering,
string* status) {
string* message) {
CHECK_EQ(ordering.size(), A->nrow);
cc_.nmethods = 1;
@ -172,8 +172,8 @@ cholmod_factor* SuiteSparse::AnalyzeCholeskyWithUserOrdering(
cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
}
if (cc_.status != CHOLMOD_OK) {
*status = StringPrintf("cholmod_analyze failed. error code: %d",
cc_.status);
*message = StringPrintf("cholmod_analyze failed. error code: %d",
cc_.status);
return NULL;
}
@ -182,7 +182,7 @@ cholmod_factor* SuiteSparse::AnalyzeCholeskyWithUserOrdering(
cholmod_factor* SuiteSparse::AnalyzeCholeskyWithNaturalOrdering(
cholmod_sparse* A,
string* status) {
string* message) {
cc_.nmethods = 1;
cc_.method[0].ordering = CHOLMOD_NATURAL;
cc_.postorder = 0;
@ -192,8 +192,8 @@ cholmod_factor* SuiteSparse::AnalyzeCholeskyWithNaturalOrdering(
cholmod_print_common(const_cast<char*>("Symbolic Analysis"), &cc_);
}
if (cc_.status != CHOLMOD_OK) {
*status = StringPrintf("cholmod_analyze failed. error code: %d",
cc_.status);
*message = StringPrintf("cholmod_analyze failed. error code: %d",
cc_.status);
return NULL;
}
@ -244,7 +244,7 @@ bool SuiteSparse::BlockAMDOrdering(const cholmod_sparse* A,
LinearSolverTerminationType SuiteSparse::Cholesky(cholmod_sparse* A,
cholmod_factor* L,
string* status) {
string* message) {
CHECK_NOTNULL(A);
CHECK_NOTNULL(L);
@ -268,22 +268,22 @@ LinearSolverTerminationType SuiteSparse::Cholesky(cholmod_sparse* A,
// (e.g. out of memory).
switch (cc_.status) {
case CHOLMOD_NOT_INSTALLED:
*status = "CHOLMOD failure: Method not installed.";
*message = "CHOLMOD failure: Method not installed.";
return LINEAR_SOLVER_FATAL_ERROR;
case CHOLMOD_OUT_OF_MEMORY:
*status = "CHOLMOD failure: Out of memory.";
*message = "CHOLMOD failure: Out of memory.";
return LINEAR_SOLVER_FATAL_ERROR;
case CHOLMOD_TOO_LARGE:
*status = "CHOLMOD failure: Integer overflow occured.";
*message = "CHOLMOD failure: Integer overflow occured.";
return LINEAR_SOLVER_FATAL_ERROR;
case CHOLMOD_INVALID:
*status = "CHOLMOD failure: Invalid input.";
*message = "CHOLMOD failure: Invalid input.";
return LINEAR_SOLVER_FATAL_ERROR;
case CHOLMOD_NOT_POSDEF:
*status = "CHOLMOD warning: Matrix not positive definite.";
*message = "CHOLMOD warning: Matrix not positive definite.";
return LINEAR_SOLVER_FAILURE;
case CHOLMOD_DSMALL:
*status = "CHOLMOD warning: D for LDL' or diag(L) or "
*message = "CHOLMOD warning: D for LDL' or diag(L) or "
"LL' has tiny absolute value.";
return LINEAR_SOLVER_FAILURE;
case CHOLMOD_OK:
@ -291,12 +291,12 @@ LinearSolverTerminationType SuiteSparse::Cholesky(cholmod_sparse* A,
return LINEAR_SOLVER_SUCCESS;
}
*status = "CHOLMOD failure: cholmod_factorize returned false "
"but cholmod_common::status is CHOLMOD_OK."
"Please report this to ceres-solver@googlegroups.com.";
*message = "CHOLMOD failure: cholmod_factorize returned false "
"but cholmod_common::status is CHOLMOD_OK."
"Please report this to ceres-solver@googlegroups.com.";
return LINEAR_SOLVER_FATAL_ERROR;
default:
*status =
*message =
StringPrintf("Unknown cholmod return code: %d. "
"Please report this to ceres-solver@googlegroups.com.",
cc_.status);
@ -308,9 +308,9 @@ LinearSolverTerminationType SuiteSparse::Cholesky(cholmod_sparse* A,
cholmod_dense* SuiteSparse::Solve(cholmod_factor* L,
cholmod_dense* b,
string* status) {
string* message) {
if (cc_.status != CHOLMOD_OK) {
*status = "cholmod_solve failed. CHOLMOD status is not CHOLMOD_OK";
*message = "cholmod_solve failed. CHOLMOD status is not CHOLMOD_OK";
return NULL;
}

View File

@ -139,15 +139,15 @@ class SuiteSparse {
// A is not modified, only the pattern of non-zeros of A is used,
// the actual numerical values in A are of no consequence.
//
// status contains an explanation of the failures if any.
// message contains an explanation of the failures if any.
//
// Caller owns the result.
cholmod_factor* AnalyzeCholesky(cholmod_sparse* A, string* status);
cholmod_factor* AnalyzeCholesky(cholmod_sparse* A, string* message);
cholmod_factor* BlockAnalyzeCholesky(cholmod_sparse* A,
const vector<int>& row_blocks,
const vector<int>& col_blocks,
string* status);
string* message);
// If A is symmetric, then compute the symbolic Cholesky
// factorization of A(ordering, ordering). If A is unsymmetric, then
@ -157,38 +157,38 @@ class SuiteSparse {
// A is not modified, only the pattern of non-zeros of A is used,
// the actual numerical values in A are of no consequence.
//
// status contains an explanation of the failures if any.
// message contains an explanation of the failures if any.
//
// Caller owns the result.
cholmod_factor* AnalyzeCholeskyWithUserOrdering(cholmod_sparse* A,
const vector<int>& ordering,
string* status);
string* message);
// Perform a symbolic factorization of A without re-ordering A. No
// postordering of the elimination tree is performed. This ensures
// that the symbolic factor does not introduce an extra permutation
// on the matrix. See the documentation for CHOLMOD for more details.
//
// status contains an explanation of the failures if any.
// message contains an explanation of the failures if any.
cholmod_factor* AnalyzeCholeskyWithNaturalOrdering(cholmod_sparse* A,
string* status);
string* message);
// Use the symbolic factorization in L, to find the numerical
// factorization for the matrix A or AA^T. Return true if
// successful, false otherwise. L contains the numeric factorization
// on return.
//
// status contains an explanation of the failures if any.
// message contains an explanation of the failures if any.
LinearSolverTerminationType Cholesky(cholmod_sparse* A,
cholmod_factor* L,
string* status);
string* message);
// Given a Cholesky factorization of a matrix A = LL^T, solve the
// linear system Ax = b, and return the result. If the Solve fails
// NULL is returned. Caller owns the result.
//
// status contains an explanation of the failures if any.
cholmod_dense* Solve(cholmod_factor* L, cholmod_dense* b, string* solve);
// message contains an explanation of the failures if any.
cholmod_dense* Solve(cholmod_factor* L, cholmod_dense* b, string* message);
// By virtue of the modeling layer in Ceres being block oriented,
// all the matrices used by Ceres are also block oriented. When

View File

@ -128,9 +128,9 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
residuals.data(),
gradient.data(),
jacobian)) {
summary->error = "Terminating: Residual and Jacobian evaluation failed.";
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->message = "Terminating: Residual and Jacobian evaluation failed.";
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
return;
}
@ -154,13 +154,13 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
options_.gradient_tolerance * initial_gradient_max_norm;
if (iteration_summary.gradient_max_norm <= absolute_gradient_tolerance) {
summary->error = StringPrintf("Terminating: Gradient tolerance reached. "
"Relative gradient max norm: %e <= %e",
(iteration_summary.gradient_max_norm /
initial_gradient_max_norm),
options_.gradient_tolerance);
summary->termination_type = GRADIENT_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->message = StringPrintf("Terminating: Gradient tolerance reached. "
"Relative gradient max norm: %e <= %e",
(iteration_summary.gradient_max_norm /
initial_gradient_max_norm),
options_.gradient_tolerance);
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
@ -188,18 +188,18 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
iteration_start_time = WallTimeInSeconds();
if (iteration_summary.iteration >= options_.max_num_iterations) {
summary->error = "Terminating: Maximum number of iterations reached.";
summary->message = "Terminating: Maximum number of iterations reached.";
summary->termination_type = NO_CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->error;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
const double total_solver_time = iteration_start_time - start_time +
summary->preprocessor_time_in_seconds;
if (total_solver_time >= options_.max_solver_time_in_seconds) {
summary->error = "Terminating: Maximum solver time reached.";
summary->message = "Terminating: Maximum solver time reached.";
summary->termination_type = NO_CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->error;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
@ -228,11 +228,11 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
trust_region_step.data());
if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) {
summary->error =
summary->message =
"Terminating. Linear solver failed due to unrecoverable "
"non-numeric causes. Please see the error log for clues. ";
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
return;
}
@ -245,15 +245,6 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
iteration_summary.step_is_valid = false;
iteration_summary.step_is_successful = false;
if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) {
summary->error =
"Terminating. Linear solver failed due to unrecoverable "
"non-numeric causes. Please see the error log for clues. ";
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
return;
}
double model_cost_change = 0.0;
if (strategy_summary.termination_type != LINEAR_SOLVER_FAILURE) {
// new_model_cost
@ -281,15 +272,15 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
if (!iteration_summary.step_is_valid) {
// Invalid steps can happen due to a number of reasons, and we
// allow a limited number of successive failures, and return with
// NUMERICAL_FAILURE if this limit is exceeded.
// FAILURE if this limit is exceeded.
if (++num_consecutive_invalid_steps >=
options_.max_num_consecutive_invalid_steps) {
summary->error = StringPrintf(
summary->message = StringPrintf(
"Terminating. Number of successive invalid steps more "
"than Solver::Options::max_num_consecutive_invalid_steps: %d",
options_.max_num_consecutive_invalid_steps);
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
return;
}
@ -376,14 +367,14 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
const double step_size_tolerance = options_.parameter_tolerance *
(x_norm + options_.parameter_tolerance);
if (iteration_summary.step_norm <= step_size_tolerance) {
summary->error =
summary->message =
StringPrintf("Terminating. Parameter tolerance reached. "
"relative step_norm: %e <= %e.",
(iteration_summary.step_norm /
(x_norm + options_.parameter_tolerance)),
options_.parameter_tolerance);
summary->termination_type = PARAMETER_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
@ -391,13 +382,13 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
const double absolute_function_tolerance =
options_.function_tolerance * cost;
if (fabs(iteration_summary.cost_change) < absolute_function_tolerance) {
summary->error =
summary->message =
StringPrintf("Terminating. Function tolerance reached. "
"|cost_change|/cost: %e <= %e",
fabs(iteration_summary.cost_change) / cost,
options_.function_tolerance);
summary->termination_type = FUNCTION_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
@ -494,10 +485,10 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
residuals.data(),
gradient.data(),
jacobian)) {
summary->error =
summary->message =
"Terminating: Residual and Jacobian evaluation failed.";
summary->termination_type = NUMERICAL_FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->error;
summary->termination_type = FAILURE;
LOG_IF(WARNING, is_not_silent) << summary->message;
return;
}
@ -505,14 +496,14 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
iteration_summary.gradient_norm = gradient.norm();
if (iteration_summary.gradient_max_norm <= absolute_gradient_tolerance) {
summary->error =
summary->message =
StringPrintf("Terminating: Gradient tolerance reached. "
"Relative gradient max norm: %e <= %e",
(iteration_summary.gradient_max_norm /
initial_gradient_max_norm),
options_.gradient_tolerance);
summary->termination_type = GRADIENT_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}
@ -575,9 +566,9 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
iteration_summary.trust_region_radius = strategy->Radius();
if (iteration_summary.trust_region_radius <
options_.min_trust_region_radius) {
summary->error = "Termination. Minimum trust region radius reached.";
summary->termination_type = PARAMETER_TOLERANCE;
VLOG_IF(1, is_not_silent) << summary->error;
summary->message = "Termination. Minimum trust region radius reached.";
summary->termination_type = CONVERGENCE;
VLOG_IF(1, is_not_silent) << summary->message;
return;
}

View File

@ -297,16 +297,13 @@ bool StringToVisibilityClusteringType(
return false;
}
const char* SolverTerminationTypeToString(SolverTerminationType type) {
const char* TerminationTypeToString(TerminationType type) {
switch (type) {
CASESTR(CONVERGENCE);
CASESTR(NO_CONVERGENCE);
CASESTR(FUNCTION_TOLERANCE);
CASESTR(GRADIENT_TOLERANCE);
CASESTR(PARAMETER_TOLERANCE);
CASESTR(NUMERICAL_FAILURE);
CASESTR(USER_ABORT);
CASESTR(FAILURE);
CASESTR(USER_SUCCESS);
CASESTR(DID_NOT_RUN);
CASESTR(USER_FAILURE);
default:
return "UNKNOWN";
}