Compute summary->fixed_cost while the residual blocks are removed

Change-Id: Ib598316ad21606f1a06db48c341a5f1d69915b2b
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 28627eb..493a890 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -236,7 +236,7 @@
   // evaluator, and the linear solver.
 
   scoped_ptr<Program> reduced_program(
-      CreateReducedProgram(&options, problem_impl, &summary->error));
+      CreateReducedProgram(&options, problem_impl, &summary->fixed_cost, &summary->error));
   if (reduced_program == NULL) {
     return;
   }
@@ -321,11 +321,19 @@
 // num_eliminate_blocks.
 bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
                                               int* num_eliminate_blocks,
+                                              double* fixed_cost,
                                               string* error) {
   int original_num_eliminate_blocks = *num_eliminate_blocks;
   vector<ParameterBlock*>* parameter_blocks =
       program->mutable_parameter_blocks();
 
+  scoped_array<double> residual_block_evaluate_scratch;
+  if (fixed_cost != NULL) {
+    residual_block_evalute_scratch.reset(
+        new double[program->MaxScratchDoublesNeededForEvaluate()]);
+    *fixed_cost = 0.0;
+  }
+
   // Mark all the parameters as unused. Abuse the index member of the parameter
   // blocks for the marking.
   for (int i = 0; i < parameter_blocks->size(); ++i) {
@@ -355,6 +363,17 @@
 
       if (!all_constant) {
         (*residual_blocks)[j++] = (*residual_blocks)[i];
+      } else if (fixed_cost != NULL) {
+        // The residual is constant and will be removed, so its cost is
+        // added to the variable fixed_cost.
+        double cost = 0.0;
+        if (!residual_block->Evaluate(
+              &cost, NULL, NULL, residual_block_evaluate_scratch.get())) {
+          *error = StringPrintf("Evaluation of the residual %d failed during "
+                                "removal of fixed residual blocks.", i);
+          return false;
+        }
+        *fixed_cost += cost;
       }
     }
     residual_blocks->resize(j);
@@ -387,6 +406,7 @@
 
 Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
                                           ProblemImpl* problem_impl,
+                                          double* fixed_cost,
                                           string* error) {
   Program* original_program = problem_impl->mutable_program();
   scoped_ptr<Program> transformed_program(new Program(*original_program));
@@ -417,6 +437,7 @@
 
   if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
                                     &num_eliminate_blocks,
+                                    fixed_cost,
                                     error)) {
     return NULL;
   }
diff --git a/internal/ceres/solver_impl.h b/internal/ceres/solver_impl.h
index 3ba4d3e..11b44de 100644
--- a/internal/ceres/solver_impl.h
+++ b/internal/ceres/solver_impl.h
@@ -56,8 +56,11 @@
   // and residuals eliminated, and in the case of automatic schur
   // ordering, has the E blocks first in the resulting program, with
   // options.num_eliminate_blocks set appropriately.
+  // If fixed_cost is not NULL, the residual blocks that are removed
+  // are evaluated and the sum of their cost is returned in fixed_cost.
   static Program* CreateReducedProgram(Solver::Options* options,
                                        ProblemImpl* problem_impl,
+                                       double* fixed_cost,
                                        string* error);
 
   // Create the appropriate linear solver, taking into account any
@@ -102,8 +105,11 @@
   // depending only on fixed parameters from the problem. Also updates
   // num_eliminate_blocks, since removed parameters changes the point
   // at which the eliminated blocks is valid.
+  // If fixed_cost is not NULL, the residual blocks that are removed
+  // are evaluated and the sum of their cost is returned in fixed_cost.
   static bool RemoveFixedBlocksFromProgram(Program* program,
                                            int* num_eliminate_blocks,
+                                           double* fixed_cost,
                                            string* error);
 };
 
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc
index 36dd959..a6d6aac 100644
--- a/internal/ceres/solver_impl_test.cc
+++ b/internal/ceres/solver_impl_test.cc
@@ -41,6 +41,20 @@
 namespace ceres {
 namespace internal {
 
+// A cost function that sipmply returns its argument.
+class UnaryIdentityCostFunction : public SizedCostFunction<1, 1> {
+ public:
+  virtual bool Evaluate(double const* const* parameters,
+                        double* residuals,
+                        double** jacobians) const {
+    residuals[0] = parameters[0][0];
+    if (jacobians != NULL && jacobians[0] != NULL) {
+      jacobians[0][0] = 1.0;
+    }
+    return true;
+  }
+};
+
 // Templated base class for the CostFunction signatures.
 template <int kNumResiduals, int N0, int N1, int N2>
 class MockCostFunctionBase : public
@@ -77,6 +91,7 @@
     Program program(*problem.mutable_program());
     EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
                                                          &num_eliminate_blocks,
+                                                         NULL,
                                                          &error));
     EXPECT_EQ(program.NumParameterBlocks(), 3);
     EXPECT_EQ(program.NumResidualBlocks(), 3);
@@ -90,6 +105,7 @@
     Program program(problem.program());
     EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
                                                          &num_eliminate_blocks,
+                                                         NULL,
                                                          &error));
     EXPECT_EQ(program.NumParameterBlocks(), 3);
     EXPECT_EQ(program.NumResidualBlocks(), 3);
@@ -110,6 +126,7 @@
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
                                                        &num_eliminate_blocks,
+                                                       NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 0);
   EXPECT_EQ(program.NumResidualBlocks(), 0);
@@ -131,6 +148,7 @@
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
                                                        &num_eliminate_blocks,
+                                                       NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 0);
   EXPECT_EQ(program.NumResidualBlocks(), 0);
@@ -155,6 +173,7 @@
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
                                                        &num_eliminate_blocks,
+                                                       NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 1);
   EXPECT_EQ(program.NumResidualBlocks(), 1);
@@ -180,12 +199,47 @@
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
                                                        &num_eliminate_blocks,
+                                                       NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 2);
   EXPECT_EQ(program.NumResidualBlocks(), 2);
   EXPECT_EQ(num_eliminate_blocks, 1);
 }
 
+TEST(SolverImpl, RemoveFixedBlocksFixedCost) {
+  ProblemImpl problem;
+  double x = 1.23;
+  double y = 4.56;
+  double z = 7.89;
+
+  problem.AddParameterBlock(&x, 1);
+  problem.AddParameterBlock(&y, 1);
+  problem.AddParameterBlock(&z, 1);
+  problem.AddResidualBlock(new UnaryIdentityCostFunction(), NULL, &x);
+  problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
+  problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
+  problem.SetParameterBlockConstant(&x);
+
+  int num_eliminate_blocks = 2;
+  double fixed_cost = 0.0;
+  Program program(problem.program());
+
+  double expected_fixed_cost;
+  ResidualBlock *expected_removed_block = program.residual_blocks()[0];
+  scoped_array<double> scratch(new double[expected_removed_block->NumScratchDoublesForEvaluate()]);
+  expected_removed_block->Evaluate(&expected_fixed_cost, NULL, NULL, scratch.get());
+
+  string error;
+  EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
+                                                       &num_eliminate_blocks,
+                                                       &fixed_cost,
+                                                       &error));
+  EXPECT_EQ(program.NumParameterBlocks(), 2);
+  EXPECT_EQ(program.NumResidualBlocks(), 2);
+  EXPECT_EQ(num_eliminate_blocks, 1);
+  EXPECT_DOUBLE_EQ(fixed_cost, expected_fixed_cost);
+}
+
 TEST(SolverImpl, ReorderResidualBlockNonSchurSolver) {
   ProblemImpl problem;
   double x;
@@ -322,7 +376,7 @@
   // marking the index to -1 at the same time. x and y also get indices.
   string error;
   scoped_ptr<Program> reduced_program(
-      SolverImpl::CreateReducedProgram(&options, &problem, &error));
+      SolverImpl::CreateReducedProgram(&options, &problem, NULL, &error));
 
   const vector<ResidualBlock*>& residual_blocks =
       problem.program().residual_blocks();
diff --git a/internal/ceres/trust_region_minimizer.cc b/internal/ceres/trust_region_minimizer.cc
index f990b80..357d822 100644
--- a/internal/ceres/trust_region_minimizer.cc
+++ b/internal/ceres/trust_region_minimizer.cc
@@ -167,23 +167,6 @@
     return;
   }
 
-  // Compute the fixed part of the cost.
-  //
-  // This is a poor way to do this computation. Even if fixed_cost is
-  // zero, because we are subtracting two possibly large numbers, we
-  // are depending on exact cancellation to give us a zero here. But
-  // initial_cost and cost have been computed by two different
-  // evaluators. One which runs on the whole problem (in
-  // solver_impl.cc) in single threaded mode and another which runs
-  // here on the reduced problem, so fixed_cost can (and does) contain
-  // some numerical garbage with a relative magnitude of 1e-14.
-  //
-  // The right way to do this, would be to compute the fixed cost on
-  // just the set of residual blocks which are held constant and were
-  // removed from the original problem when the reduced problem was
-  // constructed.
-  summary->fixed_cost = summary->initial_cost - cost;
-
   gradient.setZero();
   jacobian->LeftMultiply(residuals.data(), gradient.data());
   iteration_summary.gradient_max_norm = gradient.lpNorm<Eigen::Infinity>();