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