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(),