Various cleanups to nist.cc.
More flexible testing.
Read and parse the certified cost value from the data file.
Remove the ugly hack for computing the certified cost.
Refactored the flags parsing logic
Change-Id: I8f2e6be183b758b2453302fcdc6696bfa0db5eb8
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index f5c1ac1..e5f9100 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -66,27 +66,30 @@
#include "snavely_reprojection_error.h"
DEFINE_string(input, "", "Input File name");
+DEFINE_string(trust_region_strategy, "levenberg_marquardt",
+ "Options are: levenberg_marquardt, dogleg");
+DEFINE_string(linear_solver, "sparse_schur", "Options are: "
+ "sparse_schur, dense_schur, iterative_schur, sparse_normal_cholesky, "
+ "dense_qr, dense_normal_cholesky and cgnr");
+DEFINE_string(preconditioner, "jacobi", "Options are: "
+ "identity, jacobi, schur_jacobi, cluster_jacobi, "
+ "cluster_tridiagonal");
+DEFINE_string(sparse_linear_algebra_library, "suite_sparse",
+ "Options are: suite_sparse and cx_sparse");
+DEFINE_string(ordering, "schur", "Options are: schur, user, natural");
+DEFINE_string(dogleg, "traditional_dogleg", "Options are: traditional_dogleg,"
+ "subspace_dogleg");
+
DEFINE_bool(use_quaternions, false, "If true, uses quaternions to represent "
"rotations. If false, angle axis is used");
DEFINE_bool(use_local_parameterization, false, "For quaternions, use a local "
"parameterization.");
DEFINE_bool(robustify, false, "Use a robust loss function");
-DEFINE_string(trust_region_strategy, "lm", "Options are: lm, dogleg");
DEFINE_double(eta, 1e-2, "Default value for eta. Eta determines the "
"accuracy of each linear solve of the truncated newton step. "
"Changing this parameter can affect solve performance ");
-DEFINE_string(solver_type, "sparse_schur", "Options are: "
- "sparse_schur, dense_schur, iterative_schur, sparse_cholesky, "
- "dense_qr, dense_cholesky and conjugate_gradients");
-DEFINE_string(preconditioner_type, "jacobi", "Options are: "
- "identity, jacobi, schur_jacobi, cluster_jacobi, "
- "cluster_tridiagonal");
-DEFINE_string(sparse_linear_algebra_library, "suitesparse",
- "Options are: suitesparse and cxsparse");
-DEFINE_string(ordering_type, "schur", "Options are: schur, user, natural");
-DEFINE_string(dogleg_type, "traditional", "Options are: traditional, subspace");
DEFINE_bool(use_block_amd, true, "Use a block oriented fill reducing "
"ordering.");
@@ -111,88 +114,19 @@
namespace examples {
void SetLinearSolver(Solver::Options* options) {
- if (FLAGS_solver_type == "sparse_schur") {
- options->linear_solver_type = ceres::SPARSE_SCHUR;
- } else if (FLAGS_solver_type == "dense_schur") {
- options->linear_solver_type = ceres::DENSE_SCHUR;
- } else if (FLAGS_solver_type == "iterative_schur") {
- options->linear_solver_type = ceres::ITERATIVE_SCHUR;
- } else if (FLAGS_solver_type == "sparse_cholesky") {
- options->linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
- } else if (FLAGS_solver_type == "cgnr") {
- options->linear_solver_type = ceres::CGNR;
- } else if (FLAGS_solver_type == "dense_qr") {
- // DENSE_QR is included here for completeness, but actually using
- // this option is a bad idea due to the amount of memory needed
- // to store even the smallest of the bundle adjustment jacobian
- // arrays
- options->linear_solver_type = ceres::DENSE_QR;
- } else if (FLAGS_solver_type == "dense_cholesky") {
- // DENSE_NORMAL_CHOLESKY is included here for completeness, but
- // actually using this option is a bad idea due to the amount of
- // memory needed to store even the smallest of the bundle
- // adjustment jacobian arrays
- options->linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;
- } else {
- LOG(FATAL) << "Unknown ceres solver type: "
- << FLAGS_solver_type;
- }
-
- if (options->linear_solver_type == ceres::CGNR) {
- options->linear_solver_min_num_iterations = 5;
- if (FLAGS_preconditioner_type == "identity") {
- options->preconditioner_type = ceres::IDENTITY;
- } else if (FLAGS_preconditioner_type == "jacobi") {
- options->preconditioner_type = ceres::JACOBI;
- } else {
- LOG(FATAL) << "For CGNR, only identity and jacobian "
- << "preconditioners are supported. Got: "
- << FLAGS_preconditioner_type;
- }
- }
-
- if (options->linear_solver_type == ceres::ITERATIVE_SCHUR) {
- options->linear_solver_min_num_iterations = 5;
- if (FLAGS_preconditioner_type == "identity") {
- options->preconditioner_type = ceres::IDENTITY;
- } else if (FLAGS_preconditioner_type == "jacobi") {
- options->preconditioner_type = ceres::JACOBI;
- } else if (FLAGS_preconditioner_type == "schur_jacobi") {
- options->preconditioner_type = ceres::SCHUR_JACOBI;
- } else if (FLAGS_preconditioner_type == "cluster_jacobi") {
- options->preconditioner_type = ceres::CLUSTER_JACOBI;
- } else if (FLAGS_preconditioner_type == "cluster_tridiagonal") {
- options->preconditioner_type = ceres::CLUSTER_TRIDIAGONAL;
- } else {
- LOG(FATAL) << "Unknown ceres preconditioner type: "
- << FLAGS_preconditioner_type;
- }
- }
-
- if (FLAGS_sparse_linear_algebra_library == "suitesparse") {
- options->sparse_linear_algebra_library = SUITE_SPARSE;
- } else if (FLAGS_sparse_linear_algebra_library == "cxsparse") {
- options->sparse_linear_algebra_library = CX_SPARSE;
- } else {
- LOG(FATAL) << "Unknown sparse linear algebra library type.";
- }
-
+ CHECK(StringToLinearSolverType(FLAGS_linear_solver,
+ &options->linear_solver_type));
+ CHECK(StringToPreconditionerType(FLAGS_preconditioner,
+ &options->preconditioner_type));
+ CHECK(StringToSparseLinearAlgebraLibraryType(
+ FLAGS_sparse_linear_algebra_library,
+ &options->sparse_linear_algebra_library));
options->num_linear_solver_threads = FLAGS_num_threads;
}
void SetOrdering(BALProblem* bal_problem, Solver::Options* options) {
options->use_block_amd = FLAGS_use_block_amd;
-
- // Only non-Schur solvers support the natural ordering for this
- // problem.
- if (FLAGS_ordering_type == "natural") {
- if (options->linear_solver_type == SPARSE_SCHUR ||
- options->linear_solver_type == DENSE_SCHUR ||
- options->linear_solver_type == ITERATIVE_SCHUR) {
- LOG(FATAL) << "Natural ordering with Schur type solver does not work.";
- }
- return;
- }
+ CHECK(StringToOrderingType(FLAGS_ordering, &options->ordering_type));
// Bundle adjustment problems have a sparsity structure that makes
// them amenable to more specialized and much more efficient
@@ -208,8 +142,7 @@
// the right ParameterBlock ordering, or by manually specifying a
// suitable ordering vector and defining
// Options::num_eliminate_blocks.
- if (FLAGS_ordering_type == "schur") {
- options->ordering_type = ceres::SCHUR;
+ if (options->ordering_type == ceres::SCHUR) {
return;
}
@@ -248,22 +181,9 @@
options->eta = FLAGS_eta;
options->max_solver_time_in_seconds = FLAGS_max_solver_time;
options->use_nonmonotonic_steps = FLAGS_nonmonotonic_steps;
- if (FLAGS_trust_region_strategy == "lm") {
- options->trust_region_strategy_type = LEVENBERG_MARQUARDT;
- } else if (FLAGS_trust_region_strategy == "dogleg") {
- options->trust_region_strategy_type = DOGLEG;
- } else {
- LOG(FATAL) << "Unknown trust region strategy: "
- << FLAGS_trust_region_strategy;
- }
- if (FLAGS_dogleg_type == "traditional") {
- options->dogleg_type = TRADITIONAL_DOGLEG;
- } else if (FLAGS_dogleg_type == "subspace") {
- options->dogleg_type = SUBSPACE_DOGLEG;
- } else {
- LOG(FATAL) << "Unknown dogleg type: "
- << FLAGS_dogleg_type;
- }
+ CHECK(StringToTrustRegionStrategyType(FLAGS_trust_region_strategy,
+ &options->trust_region_strategy_type));
+ CHECK(StringToDoglegType(FLAGS_dogleg, &options->dogleg_type));
}
void SetSolverOptionsFromFlags(BALProblem* bal_problem,
diff --git a/examples/nist.cc b/examples/nist.cc
index 2c1a4c5..0f5951f 100644
--- a/examples/nist.cc
+++ b/examples/nist.cc
@@ -51,6 +51,18 @@
DEFINE_string(nist_data_dir, "", "Directory containing the NIST non-linear"
"regression examples");
+DEFINE_string(trust_region_strategy, "levenberg_marquardt",
+ "Options are: levenberg_marquardt, dogleg");
+DEFINE_string(dogleg, "traditional_dogleg",
+ "Options are: traditional_dogleg, subspace_dogleg");
+DEFINE_string(linear_solver, "dense_qr", "Options are: "
+ "sparse_cholesky, dense_qr, dense_normal_cholesky and"
+ "cgnr");
+DEFINE_string(preconditioner, "jacobi", "Options are: "
+ "identity, jacobi");
+DEFINE_int32(num_iterations, 10000, "Number of iterations");
+DEFINE_bool(nonmonotonic_steps, false, "Trust region algorithm can use"
+ " nonmonotic steps");
using Eigen::Dynamic;
using Eigen::RowMajor;
@@ -120,8 +132,13 @@
final_parameters_(0, parameter_id) = std::atof(pieces[2 + kNumTries].c_str());
}
+ // Certfied cost
+ SkipLines(ifs, 1);
+ GetAndSplitLine(ifs, &pieces);
+ certified_cost_ = std::atof(pieces[4].c_str()) / 2.0;
+
// Read the observations.
- SkipLines(ifs, 20 - kNumParameters);
+ SkipLines(ifs, 18 - kNumParameters);
for (int i = 0; i < kNumObservations; ++i) {
GetAndSplitLine(ifs, &pieces);
// Response.
@@ -145,12 +162,14 @@
int response_size() const { return response_.cols(); }
int num_parameters() const { return initial_parameters_.cols(); }
int num_starts() const { return initial_parameters_.rows(); }
+ double certified_cost() const { return certified_cost_; }
private:
Matrix predictor_;
Matrix response_;
Matrix initial_parameters_;
Matrix final_parameters_;
+ double certified_cost_;
};
#define NIST_BEGIN(CostFunctionName) \
@@ -340,27 +359,7 @@
Solve(options, &problem, &summaries[start]);
}
- // Ugly hack to get the objective function value at the certified
- // optimal parameter values. So we build the problem and call Ceres
- // with zero iterations to get the initial_cost.
- {
- Matrix initial_parameters = nist_problem.final_parameters();
- ceres::Problem problem;
- for (int i = 0; i < nist_problem.num_observations(); ++i) {
- problem.AddResidualBlock(
- new ceres::AutoDiffCostFunction<Model, num_residuals, num_parameters>(
- new Model(predictor.data() + nist_problem.predictor_size() * i,
- response.data() + nist_problem.response_size() * i)),
- NULL,
- initial_parameters.data());
- }
-
- ceres::Solver::Options options;
- options.max_num_iterations = 0;
- Solve(options, &problem, &summaries[nist_problem.num_starts()]);
- }
-
- double certified_cost = summaries[nist_problem.num_starts()].initial_cost;
+ const double certified_cost = nist_problem.certified_cost();
int num_success = 0;
const int kMinNumMatchingDigits = 4;
@@ -380,6 +379,7 @@
std::cerr << "FAILURE";
std::cerr << " summary: "
<< summary.BriefReport()
+ << " Certified cost: " << certified_cost
<< std::endl;
} else {
++num_success;
@@ -389,7 +389,31 @@
return num_success;
}
-void SolveNISTProblems(const ceres::Solver::Options& options) {
+void SetMinimizerOptions(ceres::Solver::Options* options) {
+ CHECK(ceres::StringToLinearSolverType(FLAGS_linear_solver,
+ &options->linear_solver_type));
+ CHECK(ceres::StringToPreconditionerType(FLAGS_preconditioner,
+ &options->preconditioner_type));
+ CHECK(ceres::StringToTrustRegionStrategyType(
+ FLAGS_trust_region_strategy,
+ &options->trust_region_strategy_type));
+ CHECK(ceres::StringToDoglegType(FLAGS_dogleg, &options->dogleg_type));
+
+ options->max_num_iterations = FLAGS_num_iterations;
+ options->use_nonmonotonic_steps = FLAGS_nonmonotonic_steps;
+ options->function_tolerance = 1e-18;
+ options->gradient_tolerance = 1e-18;
+ options->parameter_tolerance = 1e-18;
+}
+
+void SolveNISTProblems() {
+ if (FLAGS_nist_data_dir.empty()) {
+ LOG(FATAL) << "Must specify the directory containing the NIST problems";
+ }
+
+ ceres::Solver::Options options;
+ SetMinimizerOptions(&options);
+
std::cerr << "Lower Difficulty\n";
int easy_success = 0;
easy_success += RegressionDriver<Misra1a, 1, 2>("Misra1a.dat", options);
@@ -437,50 +461,6 @@
int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
-
- // TODO(sameeragarwal): Test more combinations of non-linear and
- // linear solvers.
- ceres::Solver::Options options;
- options.max_num_iterations = 10000;
- options.function_tolerance *= 1e-10;
- options.gradient_tolerance *= 1e-10;
- options.parameter_tolerance *= 1e-10;
-
- options.linear_solver_type = ceres::DENSE_QR;
- options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
- std::cerr << "Levenberg-Marquardt - DENSE_QR\n";
- SolveNISTProblems(options);
-
- options.trust_region_strategy_type = ceres::DOGLEG;
- options.dogleg_type = ceres::TRADITIONAL_DOGLEG;
- std::cerr << "\n\nTraditional Dogleg - DENSE_QR\n\n";
- SolveNISTProblems(options);
-
- options.trust_region_strategy_type = ceres::DOGLEG;
- options.dogleg_type = ceres::SUBSPACE_DOGLEG;
- std::cerr << "\n\nSubspace Dogleg - DENSE_QR\n\n";
- SolveNISTProblems(options);
-
- options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;
- options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
- std::cerr << "Levenberg-Marquardt - DENSE_NORMAL_CHOLESKY\n";
- SolveNISTProblems(options);
-
- options.trust_region_strategy_type = ceres::DOGLEG;
- options.dogleg_type = ceres::TRADITIONAL_DOGLEG;
- std::cerr << "\n\nTraditional Dogleg - DENSE_NORMAL_CHOLESKY\n\n";
- SolveNISTProblems(options);
-
- options.trust_region_strategy_type = ceres::DOGLEG;
- options.dogleg_type = ceres::SUBSPACE_DOGLEG;
- std::cerr << "\n\nSubspace Dogleg - DENSE_NORMAL_CHOLESKY\n\n";
- SolveNISTProblems(options);
-
- options.linear_solver_type = ceres::CGNR;
- options.preconditioner_type = ceres::JACOBI;
- options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
- std::cerr << "Levenberg-Marquardt - CGNR + JACOBI\n";
- SolveNISTProblems(options);
-
+ SolveNISTProblems();
return 0;
};
diff --git a/include/ceres/types.h b/include/ceres/types.h
index 3980885..2c0b7bd 100644
--- a/include/ceres/types.h
+++ b/include/ceres/types.h
@@ -37,6 +37,8 @@
#ifndef CERES_PUBLIC_TYPES_H_
#define CERES_PUBLIC_TYPES_H_
+#include "ceres/internal/port.h"
+
namespace ceres {
// Basic integer types. These typedefs are in the Ceres namespace to avoid
@@ -297,16 +299,31 @@
};
const char* LinearSolverTypeToString(LinearSolverType type);
+bool StringToLinearSolverType(string value, LinearSolverType* type);
+
const char* PreconditionerTypeToString(PreconditionerType type);
+bool StringToPreconditionerType(string value, PreconditionerType* type);
+
const char* SparseLinearAlgebraLibraryTypeToString(
SparseLinearAlgebraLibraryType type);
+bool StringToSparseLinearAlgebraLibraryType(
+ string value,
+ SparseLinearAlgebraLibraryType* type);
+
+const char* OrderingTypeToString(OrderingType type);
+bool StringToOrderingType(string value, OrderingType* type);
+
+const char* TrustRegionStrategyTypeToString(TrustRegionStrategyType type);
+bool StringToTrustRegionStrategyType(string value,
+ TrustRegionStrategyType* type);
+
+const char* DoglegTypeToString(DoglegType type);
+bool StringToDoglegType(string value, DoglegType* type);
+
const char* LinearSolverTerminationTypeToString(
LinearSolverTerminationType type);
-const char* OrderingTypeToString(OrderingType type);
+
const char* SolverTerminationTypeToString(SolverTerminationType type);
-const char* SparseLinearAlgebraLibraryTypeToString(
- SparseLinearAlgebraLibraryType type);
-const char* TrustRegionStrategyTypeToString( TrustRegionStrategyType type);
bool IsSchurType(LinearSolverType type);
} // namespace ceres
diff --git a/internal/ceres/types.cc b/internal/ceres/types.cc
index 2e950c5..9339bc4 100644
--- a/internal/ceres/types.cc
+++ b/internal/ceres/types.cc
@@ -28,13 +28,21 @@
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
+#include <algorithm>
#include <string>
#include "ceres/types.h"
+#include "glog/logging.h"
namespace ceres {
#define CASESTR(x) case x: return #x
+#define STRENUM(x) if (value == #x) { *type = x; return true;}
+
+void UpperCase(string* input) {
+ std::transform(input->begin(), input->end(), input->begin(), ::toupper);
+}
+
const char* LinearSolverTypeToString(LinearSolverType solver_type) {
switch (solver_type) {
CASESTR(DENSE_NORMAL_CHOLESKY);
@@ -49,6 +57,18 @@
}
}
+bool StringToLinearSolverType(string value, LinearSolverType* type) {
+ UpperCase(&value);
+ STRENUM(DENSE_NORMAL_CHOLESKY);
+ STRENUM(DENSE_QR);
+ STRENUM(SPARSE_NORMAL_CHOLESKY);
+ STRENUM(DENSE_SCHUR);
+ STRENUM(SPARSE_SCHUR);
+ STRENUM(ITERATIVE_SCHUR);
+ STRENUM(CGNR);
+ return false;
+}
+
const char* PreconditionerTypeToString(
PreconditionerType preconditioner_type) {
switch (preconditioner_type) {
@@ -62,6 +82,16 @@
}
}
+bool StringToPreconditionerType(string value, PreconditionerType* type) {
+ UpperCase(&value);
+ STRENUM(IDENTITY);
+ STRENUM(JACOBI);
+ STRENUM(SCHUR_JACOBI);
+ STRENUM(CLUSTER_JACOBI);
+ STRENUM(CLUSTER_TRIDIAGONAL);
+ return false;
+}
+
const char* SparseLinearAlgebraLibraryTypeToString(
SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type) {
switch (sparse_linear_algebra_library_type) {
@@ -72,6 +102,16 @@
}
}
+
+bool StringToSparseLinearAlgebraLibraryType(
+ string value,
+ SparseLinearAlgebraLibraryType* type) {
+ UpperCase(&value);
+ STRENUM(SUITE_SPARSE);
+ STRENUM(CX_SPARSE);
+ return false;
+}
+
const char* OrderingTypeToString(OrderingType ordering_type) {
switch (ordering_type) {
CASESTR(NATURAL);
@@ -82,6 +122,48 @@
}
}
+bool StringToOrderingType(string value, OrderingType* type) {
+ UpperCase(&value);
+ STRENUM(NATURAL);
+ STRENUM(USER);
+ STRENUM(SCHUR);
+ return false;
+}
+
+const char* TrustRegionStrategyTypeToString(
+ TrustRegionStrategyType trust_region_strategy_type) {
+ switch (trust_region_strategy_type) {
+ CASESTR(LEVENBERG_MARQUARDT);
+ CASESTR(DOGLEG);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+bool StringToTrustRegionStrategyType(string value,
+ TrustRegionStrategyType* type) {
+ UpperCase(&value);
+ STRENUM(LEVENBERG_MARQUARDT);
+ STRENUM(DOGLEG);
+ return false;
+}
+
+const char* DoglegTypeToString(DoglegType dogleg_type) {
+ switch (dogleg_type) {
+ CASESTR(TRADITIONAL_DOGLEG);
+ CASESTR(SUBSPACE_DOGLEG);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+bool StringToDoglegType(string value, DoglegType* type) {
+ UpperCase(&value);
+ STRENUM(TRADITIONAL_DOGLEG);
+ STRENUM(SUBSPACE_DOGLEG);
+ return false;
+}
+
const char* SolverTerminationTypeToString(
SolverTerminationType termination_type) {
switch (termination_type) {
@@ -98,27 +180,20 @@
}
}
-const char* SparseLinearAlgebraTypeToString(
- SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type) {
- switch (sparse_linear_algebra_library_type) {
- CASESTR(CX_SPARSE);
- CASESTR(SUITE_SPARSE);
- default:
- return "UNKNOWN";
- }
-}
-
-const char* TrustRegionStrategyTypeToString(
- TrustRegionStrategyType trust_region_strategy_type) {
- switch (trust_region_strategy_type) {
- CASESTR(LEVENBERG_MARQUARDT);
- CASESTR(DOGLEG);
+const char* LinearSolverTerminationTypeToString(
+ LinearSolverTerminationType termination_type) {
+ switch (termination_type) {
+ CASESTR(TOLERANCE);
+ CASESTR(MAX_ITERATIONS);
+ CASESTR(STAGNATION);
+ CASESTR(FAILURE);
default:
return "UNKNOWN";
}
}
#undef CASESTR
+#undef STRENUM
bool IsSchurType(LinearSolverType type) {
return ((type == SPARSE_SCHUR) ||