Add ability to log solver execution to file.
Change-Id: I9996ba2fed5229fe5d621fbb1a027d4c360cd59d
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index 8f1b38b..1ef0766 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -54,6 +54,7 @@
#include <algorithm>
#include <cmath>
#include <cstdio>
+#include <cstdlib>
#include <string>
#include <vector>
@@ -103,6 +104,7 @@
DEFINE_int32(random_seed, 38401, "Random seed used to set the state "
"of the pseudo random number generator used to generate "
"the pertubations.");
+DEFINE_string(solver_log, "", "File to record the solver execution to.");
namespace ceres {
namespace examples {
@@ -256,8 +258,28 @@
SetOrdering(bal_problem, options);
}
+// Uniform random numbers between 0 and 1.
+double UniformRandom() {
+ return static_cast<double>(random()) / static_cast<double>(RAND_MAX);
+}
+
+// Normal random numbers using the Box-Mueller algorithm. Its a bit
+// wasteful, as it generates two but only returns one.
+double RandNormal() {
+ double x1, x2, w, y1, y2;
+ do {
+ x1 = 2.0 * UniformRandom() - 1.0;
+ x2 = 2.0 * UniformRandom() - 1.0;
+ w = x1 * x1 + x2 * x2;
+ } while ( w >= 1.0 );
+
+ w = sqrt((-2.0 * log(w)) / w);
+ y1 = x1 * w;
+ y2 = x2 * w;
+ return y1;
+}
+
void BuildProblem(BALProblem* bal_problem, Problem* problem) {
- SetRandomState(FLAGS_random_seed);
const int point_block_size = bal_problem->point_block_size();
const int camera_block_size = bal_problem->camera_block_size();
double* points = bal_problem->mutable_points();
@@ -332,6 +354,7 @@
BuildProblem(&bal_problem, &problem);
Solver::Options options;
SetSolverOptionsFromFlags(&bal_problem, &options);
+ options.solver_log = FLAGS_solver_log;
Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.FullReport() << "\n";
diff --git a/include/ceres/solver.h b/include/ceres/solver.h
index 8a58cb1..ef6c617 100644
--- a/include/ceres/solver.h
+++ b/include/ceres/solver.h
@@ -376,6 +376,10 @@
//
// The solver does NOT take ownership of these pointers.
vector<IterationCallback*> callbacks;
+
+ // If non-empty, a summary of the execution of the solver is
+ // recorded to this file.
+ string solver_log;
};
struct Summary {
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 8e72537..c6df656 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -30,6 +30,7 @@
#include "ceres/solver_impl.h"
+#include <cstdio>
#include <iostream> // NOLINT
#include <numeric>
#include "ceres/evaluator.h"
@@ -108,6 +109,34 @@
const bool log_to_stdout_;
};
+// Basic callback to record the execution of the solver to a file for
+// offline analysis.
+class FileLoggingCallback : public IterationCallback {
+ public:
+ explicit FileLoggingCallback(const string& filename)
+ : fptr_(NULL) {
+ fptr_ = fopen(filename.c_str(), "w");
+ CHECK_NOTNULL(fptr_);
+ }
+
+ virtual ~FileLoggingCallback() {
+ if (fptr_ != NULL) {
+ fclose(fptr_);
+ }
+ }
+
+ virtual CallbackReturnType operator()(const IterationSummary& summary) {
+ fprintf(fptr_,
+ "%4d %e %e\n",
+ summary.iteration,
+ summary.cost,
+ summary.cumulative_time_in_seconds);
+ return SOLVER_CONTINUE;
+ }
+ private:
+ FILE* fptr_;
+};
+
} // namespace
void SolverImpl::Minimize(const Solver::Options& options,
@@ -117,6 +146,16 @@
double* parameters,
Solver::Summary* summary) {
Minimizer::Options minimizer_options(options);
+
+ // TODO(sameeragarwal): Add support for logging the configuration
+ // and more detailed stats.
+ scoped_ptr<IterationCallback> file_logging_callback;
+ if (!options.solver_log.empty()) {
+ file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
+ minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
+ file_logging_callback.get());
+ }
+
LoggingCallback logging_callback(options.minimizer_progress_to_stdout);
if (options.logging_type != SILENT) {
minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),