Refactor Program related functions.

Move

ParameterBlocksAreFinite
IsBoundsConstrained
IsFeasible
RemoveFixedBlocks
IsParameterBlockSetIndependent
CreateJacobianBlockSparsity

from being static methods in SolverImpl to member functions
in the Program class.

Change-Id: I80fa4a429a716ea4371ad6c67864adad438e1553
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 18ff92b..a54db8a 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -261,6 +261,7 @@
   CERES_TEST(partitioned_matrix_view)
   CERES_TEST(polynomial)
   CERES_TEST(problem)
+  CERES_TEST(program)
   CERES_TEST(residual_block)
   CERES_TEST(residual_block_utils)
   CERES_TEST(rotation)
diff --git a/internal/ceres/program.cc b/internal/ceres/program.cc
index 9e5c51b..3a88b13 100644
--- a/internal/ceres/program.cc
+++ b/internal/ceres/program.cc
@@ -32,6 +32,7 @@
 
 #include <map>
 #include <vector>
+#include "ceres/array_utils.h"
 #include "ceres/casts.h"
 #include "ceres/compressed_row_sparse_matrix.h"
 #include "ceres/cost_function.h"
@@ -44,6 +45,7 @@
 #include "ceres/problem.h"
 #include "ceres/residual_block.h"
 #include "ceres/stl_util.h"
+#include "ceres/triplet_sparse_matrix.h"
 
 namespace ceres {
 namespace internal {
@@ -170,6 +172,241 @@
   return true;
 }
 
+bool Program::ParameterBlocksAreFinite(string* message) const {
+  CHECK_NOTNULL(message);
+  for (int i = 0; i < parameter_blocks_.size(); ++i) {
+    const ParameterBlock* parameter_block = parameter_blocks_[i];
+    const double* array = parameter_block->user_state();
+    const int size = parameter_block->Size();
+    const int invalid_index = FindInvalidValue(size, array);
+    if (invalid_index != size) {
+      *message = StringPrintf(
+          "ParameterBlock: %p with size %d has at least one invalid value.\n"
+          "First invalid value is at index: %d.\n"
+          "Parameter block values: ",
+          array, size, invalid_index);
+      AppendArrayToString(size, array, message);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool Program::IsBoundsConstrained() const {
+  for (int i = 0; i < parameter_blocks_.size(); ++i) {
+    const ParameterBlock* parameter_block = parameter_blocks_[i];
+    if (parameter_block->IsConstant()) {
+      continue;
+    }
+    const int size = parameter_block->Size();
+    for (int j = 0; j < size; ++j) {
+      const double lower_bound = parameter_block->LowerBoundForParameter(j);
+      const double upper_bound = parameter_block->UpperBoundForParameter(j);
+      if (lower_bound > -std::numeric_limits<double>::max() ||
+          upper_bound < std::numeric_limits<double>::max()) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool Program::IsFeasible(string* message) const {
+  CHECK_NOTNULL(message);
+  for (int i = 0; i < parameter_blocks_.size(); ++i) {
+    const ParameterBlock* parameter_block = parameter_blocks_[i];
+    const double* parameters = parameter_block->user_state();
+    const int size = parameter_block->Size();
+    if (parameter_block->IsConstant()) {
+      // Constant parameter blocks must start in the feasible region
+      // to ultimately produce a feasible solution, since Ceres cannot
+      // change them.
+      for (int j = 0; j < size; ++j) {
+        const double lower_bound = parameter_block->LowerBoundForParameter(j);
+        const double upper_bound = parameter_block->UpperBoundForParameter(j);
+        if (parameters[j] < lower_bound || parameters[j] > upper_bound) {
+          *message = StringPrintf(
+              "ParameterBlock: %p with size %d has at least one infeasible "
+              "value."
+              "\nFirst infeasible value is at index: %d."
+              "\nLower bound: %e, value: %e, upper bound: %e"
+              "\nParameter block values: ",
+              parameters, size, j, lower_bound, parameters[j], upper_bound);
+          AppendArrayToString(size, parameters, message);
+          return false;
+        }
+      }
+    } else {
+      // Variable parameter blocks must have non-empty feasible
+      // regions, otherwise there is no way to produce a feasible
+      // solution.
+      for (int j = 0; j < size; ++j) {
+        const double lower_bound = parameter_block->LowerBoundForParameter(j);
+        const double upper_bound = parameter_block->UpperBoundForParameter(j);
+        if (lower_bound >= upper_bound) {
+          *message = StringPrintf(
+              "ParameterBlock: %p with size %d has at least one infeasible "
+              "bound."
+              "\nFirst infeasible bound is at index: %d."
+              "\nLower bound: %e, upper bound: %e"
+              "\nParameter block values: ",
+              parameters, size, j, lower_bound, upper_bound);
+          AppendArrayToString(size, parameters, message);
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks,
+                                double* fixed_cost,
+                                string* error) {
+  CHECK_NOTNULL(removed_parameter_blocks);
+  CHECK_NOTNULL(fixed_cost);
+  CHECK_NOTNULL(error);
+
+  scoped_array<double> residual_block_evaluate_scratch;
+  residual_block_evaluate_scratch.reset(
+      new double[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) {
+    parameter_blocks_[i]->set_index(-1);
+  }
+
+  // Filter out residual that have all-constant parameters, and mark
+  // all the parameter blocks that appear in residuals.
+  int num_active_residual_blocks = 0;
+  for (int i = 0; i < residual_blocks_.size(); ++i) {
+    ResidualBlock* residual_block = residual_blocks_[i];
+    int num_parameter_blocks = residual_block->NumParameterBlocks();
+
+    // Determine if the residual block is fixed, and also mark varying
+    // parameters that appear in the residual block.
+    bool all_constant = true;
+    for (int k = 0; k < num_parameter_blocks; k++) {
+      ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
+      if (!parameter_block->IsConstant()) {
+        all_constant = false;
+        parameter_block->set_index(1);
+      }
+    }
+
+    if (!all_constant) {
+      residual_blocks_[num_active_residual_blocks++] = residual_block;
+      continue;
+    }
+
+    // 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(true,
+                                  &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(num_active_residual_blocks);
+
+  // Filter out unused or fixed parameter blocks.
+  int num_active_parameter_blocks = 0;
+  removed_parameter_blocks->clear();
+  for (int i = 0; i < parameter_blocks_.size(); ++i) {
+    ParameterBlock* parameter_block = parameter_blocks_[i];
+    if (parameter_block->index() == -1) {
+      removed_parameter_blocks->push_back(parameter_block->mutable_user_state());
+    } else {
+      parameter_blocks_[num_active_parameter_blocks++] = parameter_block;
+    }
+  }
+  parameter_blocks_.resize(num_active_parameter_blocks);
+
+  if (!(((NumResidualBlocks() == 0) &&
+         (NumParameterBlocks() == 0)) ||
+        ((NumResidualBlocks() != 0) &&
+         (NumParameterBlocks() != 0)))) {
+    *error =  "Congratulations, you found a bug in Ceres. Please report it.";
+    return false;
+  }
+
+  return true;
+}
+
+bool Program::IsParameterBlockSetIndependent(const set<double*>& independent_set) const {
+  // Loop over each residual block and ensure that no two parameter
+  // blocks in the same residual block are part of
+  // parameter_block_ptrs as that would violate the assumption that it
+  // is an independent set in the Hessian matrix.
+  for (vector<ResidualBlock*>::const_iterator it = residual_blocks_.begin();
+       it != residual_blocks_.end();
+       ++it) {
+    ParameterBlock* const* parameter_blocks = (*it)->parameter_blocks();
+    const int num_parameter_blocks = (*it)->NumParameterBlocks();
+    int count = 0;
+    for (int i = 0; i < num_parameter_blocks; ++i) {
+      count += independent_set.count(
+          parameter_blocks[i]->mutable_user_state());
+    }
+    if (count > 1) {
+      return false;
+    }
+  }
+  return true;
+}
+
+TripletSparseMatrix* Program::CreateJacobianBlockSparsityTranspose() const {
+  // Matrix to store the block sparsity structure of the Jacobian.
+  TripletSparseMatrix* tsm =
+      new TripletSparseMatrix(NumParameterBlocks(),
+                              NumResidualBlocks(),
+                              10 * NumResidualBlocks());
+  int num_nonzeros = 0;
+  int* rows = tsm->mutable_rows();
+  int* cols = tsm->mutable_cols();
+  double* values = tsm->mutable_values();
+
+  for (int c = 0; c < residual_blocks_.size(); ++c) {
+    const ResidualBlock* residual_block = residual_blocks_[c];
+    const int num_parameter_blocks = residual_block->NumParameterBlocks();
+    ParameterBlock* const* parameter_blocks =
+        residual_block->parameter_blocks();
+
+    for (int j = 0; j < num_parameter_blocks; ++j) {
+      if (parameter_blocks[j]->IsConstant()) {
+        continue;
+      }
+
+      // Re-size the matrix if needed.
+      if (num_nonzeros >= tsm->max_num_nonzeros()) {
+        tsm->set_num_nonzeros(num_nonzeros);
+        tsm->Reserve(2 * num_nonzeros);
+        rows = tsm->mutable_rows();
+        cols = tsm->mutable_cols();
+        values = tsm->mutable_values();
+      }
+
+      const int r = parameter_blocks[j]->index();
+      rows[num_nonzeros] = r;
+      cols[num_nonzeros] = c;
+      values[num_nonzeros] = 1.0;
+      ++num_nonzeros;
+    }
+  }
+
+  tsm->set_num_nonzeros(num_nonzeros);
+  return tsm;
+}
+
 int Program::NumResidualBlocks() const {
   return residual_blocks_.size();
 }
diff --git a/internal/ceres/program.h b/internal/ceres/program.h
index 4288f60..7f2fc9d 100644
--- a/internal/ceres/program.h
+++ b/internal/ceres/program.h
@@ -31,6 +31,7 @@
 #ifndef CERES_INTERNAL_PROGRAM_H_
 #define CERES_INTERNAL_PROGRAM_H_
 
+#include <set>
 #include <string>
 #include <vector>
 #include "ceres/internal/port.h"
@@ -41,6 +42,7 @@
 class ParameterBlock;
 class ProblemImpl;
 class ResidualBlock;
+class TripletSparseMatrix;
 
 // A nonlinear least squares optimization problem. This is different from the
 // similarly-named "Problem" object, which offers a mutation interface for
@@ -103,6 +105,37 @@
   // offsets) are correct.
   bool IsValid() const;
 
+  bool ParameterBlocksAreFinite(string* message) const;
+
+  // Returns true if the program has any non-constant parameter blocks
+  // which have non-trivial bounds constraints.
+  bool IsBoundsConstrained() const;
+
+  // Returns false, if the program has any constant parameter blocks
+  // which are not feasible, or any variable parameter blocks which
+  // have a lower bound greater than or equal to the upper bound.
+  bool IsFeasible(string* message) const;
+
+  // Loop over each residual block and ensure that no two parameter
+  // blocks in the same residual block are part of
+  // parameter_blocks as that would violate the assumption that it
+  // is an independent set in the Hessian matrix.
+  bool IsParameterBlockSetIndependent(const set<double*>& independent_set) const;
+
+  // Create a TripletSparseMatrix which contains the zero-one
+  // structure corresponding to the block sparsity of the transpose of
+  // the Jacobian matrix.
+  //
+  // Caller owns the result.
+  TripletSparseMatrix* CreateJacobianBlockSparsityTranspose() const;
+
+  // Removes constant parameter blocks and residual blocks with no
+  // varying parameter blocks while preserving order.
+  // TODO(sameeragarwal): Update message here.
+  bool RemoveFixedBlocks(vector<double*>* removed_parameter_blocks,
+                         double* fixed_cost,
+                         string* message);
+
   // See problem.h for what these do.
   int NumParameterBlocks() const;
   int NumParameters() const;
diff --git a/internal/ceres/program_test.cc b/internal/ceres/program_test.cc
new file mode 100644
index 0000000..79adfa6
--- /dev/null
+++ b/internal/ceres/program_test.cc
@@ -0,0 +1,418 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2014 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors may be
+//   used to endorse or promote products derived from this software without
+//   specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: sameeragarwal@google.com (Sameer Agarwal)
+
+#include "ceres/program.h"
+
+#include <limits>
+#include <cmath>
+#include <vector>
+#include "ceres/sized_cost_function.h"
+#include "ceres/problem_impl.h"
+#include "ceres/residual_block.h"
+#include "ceres/triplet_sparse_matrix.h"
+#include "gtest/gtest.h"
+
+namespace ceres {
+namespace internal {
+
+// A cost function that simply 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
+SizedCostFunction<kNumResiduals, N0, N1, N2> {
+ public:
+  virtual bool Evaluate(double const* const* parameters,
+                        double* residuals,
+                        double** jacobians) const {
+    for (int i = 0; i < kNumResiduals; ++i) {
+      residuals[i] = kNumResiduals +  N0 + N1 + N2;
+    }
+    return true;
+  }
+};
+
+class UnaryCostFunction : public MockCostFunctionBase<2, 1, 0, 0> {};
+class BinaryCostFunction : public MockCostFunctionBase<2, 1, 1, 0> {};
+class TernaryCostFunction : public MockCostFunctionBase<2, 1, 1, 1> {};
+
+TEST(Program, RemoveFixedBlocksNothingConstant) {
+  ProblemImpl problem;
+  double x;
+  double y;
+  double z;
+
+  problem.AddParameterBlock(&x, 1);
+  problem.AddParameterBlock(&y, 1);
+  problem.AddParameterBlock(&z, 1);
+  problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
+  problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
+  problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
+
+  Program* program = problem.mutable_program();
+  vector<double*> removed_parameter_blocks;
+  double fixed_cost = 0.0;
+  string message;
+  EXPECT_TRUE(program->RemoveFixedBlocks(&removed_parameter_blocks,
+                                         &fixed_cost,
+                                         &message));
+  EXPECT_EQ(program->NumParameterBlocks(), 3);
+  EXPECT_EQ(program->NumResidualBlocks(), 3);
+  EXPECT_EQ(removed_parameter_blocks.size(), 0);
+  EXPECT_EQ(fixed_cost, 0.0);
+}
+
+TEST(Program, RemoveFixedBlocksAllParameterBlocksConstant) {
+  ProblemImpl problem;
+  double x = 1.0;
+
+  problem.AddParameterBlock(&x, 1);
+  problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
+  problem.SetParameterBlockConstant(&x);
+
+  Program* program = problem.mutable_program();
+  vector<double*> removed_parameter_blocks;
+  double fixed_cost = 0.0;
+  string message;
+  EXPECT_TRUE(program->RemoveFixedBlocks(&removed_parameter_blocks,
+                                         &fixed_cost,
+                                         &message));
+  EXPECT_EQ(program->NumParameterBlocks(), 0);
+  EXPECT_EQ(program->NumResidualBlocks(), 0);
+  EXPECT_EQ(removed_parameter_blocks.size(), 1);
+  EXPECT_EQ(removed_parameter_blocks[0], &x);
+  EXPECT_EQ(fixed_cost, 9.0);
+}
+
+
+TEST(Program, RemoveFixedBlocksNoResidualBlocks) {
+  ProblemImpl problem;
+  double x;
+  double y;
+  double z;
+
+  problem.AddParameterBlock(&x, 1);
+  problem.AddParameterBlock(&y, 1);
+  problem.AddParameterBlock(&z, 1);
+
+  Program* program = problem.mutable_program();
+  vector<double*> removed_parameter_blocks;
+  double fixed_cost = 0.0;
+  string message;
+  EXPECT_TRUE(program->RemoveFixedBlocks(&removed_parameter_blocks,
+                                         &fixed_cost,
+                                         &message));
+  EXPECT_EQ(program->NumParameterBlocks(), 0);
+  EXPECT_EQ(program->NumResidualBlocks(), 0);
+  EXPECT_EQ(removed_parameter_blocks.size(), 3);
+  EXPECT_EQ(fixed_cost, 0.0);
+}
+
+TEST(Program, RemoveFixedBlocksOneParameterBlockConstant) {
+  ProblemImpl problem;
+  double x;
+  double y;
+  double z;
+
+  problem.AddParameterBlock(&x, 1);
+  problem.AddParameterBlock(&y, 1);
+  problem.AddParameterBlock(&z, 1);
+
+  problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
+  problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
+  problem.SetParameterBlockConstant(&x);
+
+  Program* program = problem.mutable_program();
+  vector<double*> removed_parameter_blocks;
+  double fixed_cost = 0.0;
+  string message;
+  EXPECT_TRUE(program->RemoveFixedBlocks(&removed_parameter_blocks,
+                                         &fixed_cost,
+                                         &message));
+  EXPECT_EQ(program->NumParameterBlocks(), 1);
+  EXPECT_EQ(program->NumResidualBlocks(), 1);
+}
+
+TEST(Program, RemoveFixedBlocksNumEliminateBlocks) {
+  ProblemImpl problem;
+  double x;
+  double y;
+  double z;
+
+  problem.AddParameterBlock(&x, 1);
+  problem.AddParameterBlock(&y, 1);
+  problem.AddParameterBlock(&z, 1);
+  problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
+  problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
+  problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
+  problem.SetParameterBlockConstant(&x);
+
+  Program* program = problem.mutable_program();
+  vector<double*> removed_parameter_blocks;
+  double fixed_cost = 0.0;
+  string message;
+  EXPECT_TRUE(program->RemoveFixedBlocks(&removed_parameter_blocks,
+                                         &fixed_cost,
+                                         &message));
+  EXPECT_EQ(program->NumParameterBlocks(), 2);
+  EXPECT_EQ(program->NumResidualBlocks(), 2);
+}
+
+TEST(Program, 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);
+
+  Program* program = problem.mutable_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(true,
+                                   &expected_fixed_cost,
+                                   NULL,
+                                   NULL,
+                                   scratch.get());
+
+  vector<double*> removed_parameter_blocks;
+  double fixed_cost = 0.0;
+  string message;
+  EXPECT_TRUE(program->RemoveFixedBlocks(&removed_parameter_blocks,
+                                         &fixed_cost,
+                                         &message));
+
+  EXPECT_EQ(program->NumParameterBlocks(), 2);
+  EXPECT_EQ(program->NumResidualBlocks(), 2);
+  EXPECT_DOUBLE_EQ(fixed_cost, expected_fixed_cost);
+}
+
+TEST(Program, CreateJacobianBlockSparsityTranspose) {
+  ProblemImpl problem;
+  double x[2];
+  double y[3];
+  double z;
+
+  problem.AddParameterBlock(x, 2);
+  problem.AddParameterBlock(y, 3);
+  problem.AddParameterBlock(&z, 1);
+
+  problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 0, 0>(), NULL, x);
+  problem.AddResidualBlock(new MockCostFunctionBase<3, 1, 2, 0>(), NULL, &z, x);
+  problem.AddResidualBlock(new MockCostFunctionBase<4, 1, 3, 0>(), NULL, &z, y);
+  problem.AddResidualBlock(new MockCostFunctionBase<5, 1, 3, 0>(), NULL, &z, y);
+  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 1, 0>(), NULL, x, &z);
+  problem.AddResidualBlock(new MockCostFunctionBase<2, 1, 3, 0>(), NULL, &z, y);
+  problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 1, 0>(), NULL, x, &z);
+  problem.AddResidualBlock(new MockCostFunctionBase<1, 3, 0, 0>(), NULL, y);
+
+  TripletSparseMatrix expected_block_sparse_jacobian(3, 8, 14);
+  {
+    int* rows = expected_block_sparse_jacobian.mutable_rows();
+    int* cols = expected_block_sparse_jacobian.mutable_cols();
+    double* values = expected_block_sparse_jacobian.mutable_values();
+    rows[0] = 0;
+    cols[0] = 0;
+
+    rows[1] = 2;
+    cols[1] = 1;
+    rows[2] = 0;
+    cols[2] = 1;
+
+    rows[3] = 2;
+    cols[3] = 2;
+    rows[4] = 1;
+    cols[4] = 2;
+
+    rows[5] = 2;
+    cols[5] = 3;
+    rows[6] = 1;
+    cols[6] = 3;
+
+    rows[7] = 0;
+    cols[7] = 4;
+    rows[8] = 2;
+    cols[8] = 4;
+
+    rows[9] = 2;
+    cols[9] = 5;
+    rows[10] = 1;
+    cols[10] = 5;
+
+    rows[11] = 0;
+    cols[11] = 6;
+    rows[12] = 2;
+    cols[12] = 6;
+
+    rows[13] = 1;
+    cols[13] = 7;
+    fill(values, values + 14, 1.0);
+    expected_block_sparse_jacobian.set_num_nonzeros(14);
+  }
+
+  Program* program = problem.mutable_program();
+  program->SetParameterOffsetsAndIndex();
+
+  scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
+      program->CreateJacobianBlockSparsityTranspose());
+
+  Matrix expected_dense_jacobian;
+  expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
+
+  Matrix actual_dense_jacobian;
+  actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
+  EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
+}
+
+template <int kNumResiduals, int kNumParameterBlocks>
+class NumParameterBlocksCostFunction : public CostFunction {
+ public:
+  NumParameterBlocksCostFunction() {
+    set_num_residuals(kNumResiduals);
+    for (int i = 0; i < kNumParameterBlocks; ++i) {
+      mutable_parameter_block_sizes()->push_back(1);
+    }
+  }
+
+  virtual ~NumParameterBlocksCostFunction() {
+  }
+
+  virtual bool Evaluate(double const* const* parameters,
+                        double* residuals,
+                        double** jacobians) const {
+    return true;
+  }
+};
+
+TEST(Program, ReallocationInCreateJacobianBlockSparsityTranspose) {
+  // CreateJacobianBlockSparsityTranspose starts with a conservative
+  // estimate of the size of the sparsity pattern. This test ensures
+  // that when those estimates are violated, the reallocation/resizing
+  // logic works correctly.
+
+  ProblemImpl problem;
+  double x[20];
+
+  vector<double*> parameter_blocks;
+  for (int i = 0; i < 20; ++i) {
+    problem.AddParameterBlock(x + i, 1);
+    parameter_blocks.push_back(x + i);
+  }
+
+  problem.AddResidualBlock(new NumParameterBlocksCostFunction<1, 20>(),
+                           NULL,
+                           parameter_blocks);
+
+  TripletSparseMatrix expected_block_sparse_jacobian(20, 1, 20);
+  {
+    int* rows = expected_block_sparse_jacobian.mutable_rows();
+    int* cols = expected_block_sparse_jacobian.mutable_cols();
+    for (int i = 0; i < 20; ++i) {
+      rows[i] = i;
+      cols[i] = 0;
+    }
+
+    double* values = expected_block_sparse_jacobian.mutable_values();
+    fill(values, values + 20, 1.0);
+    expected_block_sparse_jacobian.set_num_nonzeros(20);
+  }
+
+  Program* program = problem.mutable_program();
+  program->SetParameterOffsetsAndIndex();
+
+  scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
+      program->CreateJacobianBlockSparsityTranspose());
+
+  Matrix expected_dense_jacobian;
+  expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
+
+  Matrix actual_dense_jacobian;
+  actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
+  EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
+}
+
+TEST(Program, ProblemHasNanParameterBlocks) {
+  ProblemImpl problem;
+  double x[2];
+  x[0] = 1.0;
+  x[1] = std::numeric_limits<double>::quiet_NaN();
+  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
+  string error;
+  EXPECT_FALSE(problem.program().ParameterBlocksAreFinite(&error));
+  EXPECT_NE(error.find("has at least one invalid value"),
+            string::npos) << error;
+}
+
+TEST(Program, InfeasibleParameterBlock) {
+  ProblemImpl problem;
+  double x[] = {0.0, 0.0};
+  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
+  problem.SetParameterLowerBound(x, 0, 2.0);
+  problem.SetParameterUpperBound(x, 0, 1.0);
+  string error;
+  EXPECT_FALSE(problem.program().IsFeasible(&error));
+  EXPECT_NE(error.find("infeasible bound"), string::npos) << error;
+}
+
+TEST(Program, InfeasibleConstantParameterBlock) {
+  ProblemImpl problem;
+  double x[] = {0.0, 0.0};
+  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
+  problem.SetParameterLowerBound(x, 0, 1.0);
+  problem.SetParameterUpperBound(x, 0, 2.0);
+  problem.SetParameterBlockConstant(x);
+  string error;
+  EXPECT_FALSE(problem.program().IsFeasible(&error));
+  EXPECT_NE(error.find("infeasible value"), string::npos) << error;
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 101439c..5555135 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -106,28 +106,6 @@
   summary->num_residuals_reduced = program.NumResiduals();
 }
 
-bool ParameterBlocksAreFinite(const ProblemImpl* problem,
-                              string* message) {
-  CHECK_NOTNULL(message);
-  const Program& program = problem->program();
-  const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
-  for (int i = 0; i < parameter_blocks.size(); ++i) {
-    const double* array = parameter_blocks[i]->user_state();
-    const int size = parameter_blocks[i]->Size();
-    const int invalid_index = FindInvalidValue(size, array);
-    if (invalid_index != size) {
-      *message = StringPrintf(
-          "ParameterBlock: %p with size %d has at least one invalid value.\n"
-          "First invalid value is at index: %d.\n"
-          "Parameter block values: ",
-          array, size, invalid_index);
-      AppendArrayToString(size, array, message);
-      return false;
-    }
-  }
-  return true;
-}
-
 bool LineSearchOptionsAreValid(const Solver::Options& options,
                                string* message) {
   // Validate values for configuration parameters supplied by user.
@@ -205,84 +183,6 @@
   return true;
 }
 
-// Returns true if the program has any non-constant parameter blocks
-// which have non-trivial bounds constraints.
-bool IsBoundsConstrained(const Program& program) {
-  const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
-  for (int i = 0; i < parameter_blocks.size(); ++i) {
-    const ParameterBlock* parameter_block = parameter_blocks[i];
-    if (parameter_block->IsConstant()) {
-      continue;
-    }
-    const int size = parameter_block->Size();
-    for (int j = 0; j < size; ++j) {
-      const double lower_bound = parameter_block->LowerBoundForParameter(j);
-      const double upper_bound = parameter_block->UpperBoundForParameter(j);
-      if (lower_bound > -std::numeric_limits<double>::max() ||
-          upper_bound < std::numeric_limits<double>::max()) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-// Returns false, if the problem has any constant parameter blocks
-// which are not feasible, or any variable parameter blocks which have
-// a lower bound greater than or equal to the upper bound.
-bool ParameterBlocksAreFeasible(const ProblemImpl* problem, string* message) {
-  CHECK_NOTNULL(message);
-  const Program& program = problem->program();
-  const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
-  for (int i = 0; i < parameter_blocks.size(); ++i) {
-    const ParameterBlock* parameter_block = parameter_blocks[i];
-    const double* parameters = parameter_block->user_state();
-    const int size = parameter_block->Size();
-    if (parameter_block->IsConstant()) {
-      // Constant parameter blocks must start in the feasible region
-      // to ultimately produce a feasible solution, since Ceres cannot
-      // change them.
-      for (int j = 0; j < size; ++j) {
-        const double lower_bound = parameter_block->LowerBoundForParameter(j);
-        const double upper_bound = parameter_block->UpperBoundForParameter(j);
-        if (parameters[j] < lower_bound || parameters[j] > upper_bound) {
-          *message = StringPrintf(
-              "ParameterBlock: %p with size %d has at least one infeasible "
-              "value."
-              "\nFirst infeasible value is at index: %d."
-              "\nLower bound: %e, value: %e, upper bound: %e"
-              "\nParameter block values: ",
-              parameters, size, j, lower_bound, parameters[j], upper_bound);
-          AppendArrayToString(size, parameters, message);
-          return false;
-        }
-      }
-    } else {
-      // Variable parameter blocks must have non-empty feasible
-      // regions, otherwise there is no way to produce a feasible
-      // solution.
-      for (int j = 0; j < size; ++j) {
-        const double lower_bound = parameter_block->LowerBoundForParameter(j);
-        const double upper_bound = parameter_block->UpperBoundForParameter(j);
-        if (lower_bound >= upper_bound) {
-          *message = StringPrintf(
-              "ParameterBlock: %p with size %d has at least one infeasible "
-              "bound."
-              "\nFirst infeasible bound is at index: %d."
-              "\nLower bound: %e, upper bound: %e"
-              "\nParameter block values: ",
-              parameters, size, j, lower_bound, upper_bound);
-          AppendArrayToString(size, parameters, message);
-          return false;
-        }
-      }
-    }
-  }
-
-  return true;
-}
-
-
 }  // namespace
 
 void SolverImpl::TrustRegionMinimize(
@@ -293,7 +193,7 @@
     LinearSolver* linear_solver,
     Solver::Summary* summary) {
   Minimizer::Options minimizer_options(options);
-  minimizer_options.is_constrained = IsBoundsConstrained(*program);
+  minimizer_options.is_constrained = program->IsBoundsConstrained();
 
   // The optimizer works on contiguous parameter vectors; allocate
   // some.
@@ -470,12 +370,12 @@
     return;
   }
 
-  if (!ParameterBlocksAreFinite(problem_impl, &summary->message)) {
+  if (!original_program->ParameterBlocksAreFinite(&summary->message)) {
     LOG(ERROR) << "Terminating: " << summary->message;
     return;
   }
 
-  if (!ParameterBlocksAreFeasible(problem_impl, &summary->message)) {
+  if (!original_program->IsFeasible(&summary->message)) {
     LOG(ERROR) << "Terminating: " << summary->message;
     return;
   }
@@ -683,7 +583,7 @@
     return;
   }
 
-  if (IsBoundsConstrained(problem_impl->program())) {
+  if (original_program->IsBoundsConstrained()) {
     summary->message =  "LINE_SEARCH Minimizer does not support bounds.";
     LOG(ERROR) << "Terminating: " << summary->message;
     return;
@@ -711,7 +611,7 @@
   summary->num_threads_given = original_options.num_threads;
   summary->num_threads_used = options.num_threads;
 
-  if (!ParameterBlocksAreFinite(problem_impl, &summary->message)) {
+  if (original_program->ParameterBlocksAreFinite(&summary->message)) {
     LOG(ERROR) << "Terminating: " << summary->message;
     return;
   }
@@ -1464,54 +1364,6 @@
   return true;
 }
 
-
-TripletSparseMatrix* SolverImpl::CreateJacobianBlockSparsityTranspose(
-    const Program* program) {
-
-  // Matrix to store the block sparsity structure of the Jacobian.
-  TripletSparseMatrix* tsm =
-      new TripletSparseMatrix(program->NumParameterBlocks(),
-                              program->NumResidualBlocks(),
-                              10 * program->NumResidualBlocks());
-  int num_nonzeros = 0;
-  int* rows = tsm->mutable_rows();
-  int* cols = tsm->mutable_cols();
-  double* values = tsm->mutable_values();
-
-  const vector<ResidualBlock*>& residual_blocks = program->residual_blocks();
-  for (int c = 0; c < residual_blocks.size(); ++c) {
-    const ResidualBlock* residual_block = residual_blocks[c];
-    const int num_parameter_blocks = residual_block->NumParameterBlocks();
-    ParameterBlock* const* parameter_blocks =
-        residual_block->parameter_blocks();
-
-    for (int j = 0; j < num_parameter_blocks; ++j) {
-      if (parameter_blocks[j]->IsConstant()) {
-        continue;
-      }
-
-      // Re-size the matrix if needed.
-      if (num_nonzeros >= tsm->max_num_nonzeros()) {
-        tsm->set_num_nonzeros(num_nonzeros);
-        tsm->Reserve(2 * num_nonzeros);
-        rows = tsm->mutable_rows();
-        cols = tsm->mutable_cols();
-        values = tsm->mutable_values();
-      }
-      CHECK_LT(num_nonzeros,  tsm->max_num_nonzeros());
-
-      const int r = parameter_blocks[j]->index();
-      rows[num_nonzeros] = r;
-      cols[num_nonzeros] = c;
-      values[num_nonzeros] = 1.0;
-      ++num_nonzeros;
-    }
-  }
-
-  tsm->set_num_nonzeros(num_nonzeros);
-  return tsm;
-}
-
 bool SolverImpl::ReorderProgramForSchurTypeLinearSolver(
     const LinearSolverType linear_solver_type,
     const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
@@ -1579,7 +1431,7 @@
     program->SetParameterOffsetsAndIndex();
     // Compute a block sparse presentation of J'.
     scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
-        SolverImpl::CreateJacobianBlockSparsityTranspose(program));
+        program->CreateJacobianBlockSparsityTranspose());
 
     SuiteSparse ss;
     cholmod_sparse* block_jacobian_transpose =
@@ -1617,7 +1469,7 @@
   program->SetParameterOffsetsAndIndex();
   // Compute a block sparse presentation of J'.
   scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
-      SolverImpl::CreateJacobianBlockSparsityTranspose(program));
+      program->CreateJacobianBlockSparsityTranspose());
 
   vector<int> ordering(program->NumParameterBlocks(), 0);
   vector<ParameterBlock*>& parameter_blocks =
diff --git a/internal/ceres/solver_impl.h b/internal/ceres/solver_impl.h
index 0085898..e9a3bf5 100644
--- a/internal/ceres/solver_impl.h
+++ b/internal/ceres/solver_impl.h
@@ -159,14 +159,6 @@
   static void AlternateLinearSolverForSchurTypeLinearSolver(
       Solver::Options* options);
 
-  // Create a TripletSparseMatrix which contains the zero-one
-  // structure corresponding to the block sparsity of the transpose of
-  // the Jacobian matrix.
-  //
-  // Caller owns the result.
-  static TripletSparseMatrix* CreateJacobianBlockSparsityTranspose(
-      const Program* program);
-
   // Reorder the parameter blocks in program using the ordering
   static bool ApplyUserOrdering(
       const ProblemImpl::ParameterMap& parameter_map,
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc
index 1f15628..9e14d7e 100644
--- a/internal/ceres/solver_impl_test.cc
+++ b/internal/ceres/solver_impl_test.cc
@@ -894,209 +894,5 @@
   EXPECT_EQ(options.preconditioner_type, JACOBI);
 }
 
-TEST(SolverImpl, CreateJacobianBlockSparsityTranspose) {
-  ProblemImpl problem;
-  double x[2];
-  double y[3];
-  double z;
-
-  problem.AddParameterBlock(x, 2);
-  problem.AddParameterBlock(y, 3);
-  problem.AddParameterBlock(&z, 1);
-
-  problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 0, 0>(), NULL, x);
-  problem.AddResidualBlock(new MockCostFunctionBase<3, 1, 2, 0>(), NULL, &z, x);
-  problem.AddResidualBlock(new MockCostFunctionBase<4, 1, 3, 0>(), NULL, &z, y);
-  problem.AddResidualBlock(new MockCostFunctionBase<5, 1, 3, 0>(), NULL, &z, y);
-  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 1, 0>(), NULL, x, &z);
-  problem.AddResidualBlock(new MockCostFunctionBase<2, 1, 3, 0>(), NULL, &z, y);
-  problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 1, 0>(), NULL, x, &z);
-  problem.AddResidualBlock(new MockCostFunctionBase<1, 3, 0, 0>(), NULL, y);
-
-  TripletSparseMatrix expected_block_sparse_jacobian(3, 8, 14);
-  {
-    int* rows = expected_block_sparse_jacobian.mutable_rows();
-    int* cols = expected_block_sparse_jacobian.mutable_cols();
-    double* values = expected_block_sparse_jacobian.mutable_values();
-    rows[0] = 0;
-    cols[0] = 0;
-
-    rows[1] = 2;
-    cols[1] = 1;
-    rows[2] = 0;
-    cols[2] = 1;
-
-    rows[3] = 2;
-    cols[3] = 2;
-    rows[4] = 1;
-    cols[4] = 2;
-
-    rows[5] = 2;
-    cols[5] = 3;
-    rows[6] = 1;
-    cols[6] = 3;
-
-    rows[7] = 0;
-    cols[7] = 4;
-    rows[8] = 2;
-    cols[8] = 4;
-
-    rows[9] = 2;
-    cols[9] = 5;
-    rows[10] = 1;
-    cols[10] = 5;
-
-    rows[11] = 0;
-    cols[11] = 6;
-    rows[12] = 2;
-    cols[12] = 6;
-
-    rows[13] = 1;
-    cols[13] = 7;
-    fill(values, values + 14, 1.0);
-    expected_block_sparse_jacobian.set_num_nonzeros(14);
-  }
-
-  Program* program = problem.mutable_program();
-  program->SetParameterOffsetsAndIndex();
-
-  scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
-      SolverImpl::CreateJacobianBlockSparsityTranspose(program));
-
-  Matrix expected_dense_jacobian;
-  expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
-
-  Matrix actual_dense_jacobian;
-  actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
-  EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
-}
-
-template <int kNumResiduals, int kNumParameterBlocks>
-class NumParameterBlocksCostFunction : public CostFunction {
- public:
-  NumParameterBlocksCostFunction() {
-    set_num_residuals(kNumResiduals);
-    for (int i = 0; i < kNumParameterBlocks; ++i) {
-      mutable_parameter_block_sizes()->push_back(1);
-    }
-  }
-
-  virtual ~NumParameterBlocksCostFunction() {
-  }
-
-  virtual bool Evaluate(double const* const* parameters,
-                        double* residuals,
-                        double** jacobians) const {
-    return true;
-  }
-};
-
-TEST(SolverImpl, ReallocationInCreateJacobianBlockSparsityTranspose) {
-  // CreateJacobianBlockSparsityTranspose starts with a conservative
-  // estimate of the size of the sparsity pattern. This test ensures
-  // that when those estimates are violated, the reallocation/resizing
-  // logic works correctly.
-
-  ProblemImpl problem;
-  double x[20];
-
-  vector<double*> parameter_blocks;
-  for (int i = 0; i < 20; ++i) {
-    problem.AddParameterBlock(x + i, 1);
-    parameter_blocks.push_back(x + i);
-  }
-
-  problem.AddResidualBlock(new NumParameterBlocksCostFunction<1, 20>(),
-                           NULL,
-                           parameter_blocks);
-
-  TripletSparseMatrix expected_block_sparse_jacobian(20, 1, 20);
-  {
-    int* rows = expected_block_sparse_jacobian.mutable_rows();
-    int* cols = expected_block_sparse_jacobian.mutable_cols();
-    for (int i = 0; i < 20; ++i) {
-      rows[i] = i;
-      cols[i] = 0;
-    }
-
-    double* values = expected_block_sparse_jacobian.mutable_values();
-    fill(values, values + 20, 1.0);
-    expected_block_sparse_jacobian.set_num_nonzeros(20);
-  }
-
-  Program* program = problem.mutable_program();
-  program->SetParameterOffsetsAndIndex();
-
-  scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
-      SolverImpl::CreateJacobianBlockSparsityTranspose(program));
-
-  Matrix expected_dense_jacobian;
-  expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
-
-  Matrix actual_dense_jacobian;
-  actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
-  EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
-}
-
-TEST(SolverImpl, ProblemHasNanParameterBlocks) {
-  Problem problem;
-  double x[2];
-  x[0] = 1.0;
-  x[1] = std::numeric_limits<double>::quiet_NaN();
-  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
-  Solver::Options options;
-  Solver::Summary summary;
-  Solve(options, &problem, &summary);
-  EXPECT_EQ(summary.termination_type, FAILURE);
-  EXPECT_NE(summary.message.find("has at least one invalid value"),
-            string::npos)
-      << summary.message;
-}
-
-TEST(SolverImpl, BoundsConstrainedProblemWithLineSearchMinimizerDoesNotWork) {
-  Problem problem;
-  double x[] = {0.0, 0.0};
-  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
-  problem.SetParameterUpperBound(x, 0, 1.0);
-  Solver::Options options;
-  options.minimizer_type = LINE_SEARCH;
-  Solver::Summary summary;
-  Solve(options, &problem, &summary);
-  EXPECT_EQ(summary.termination_type, FAILURE);
-  EXPECT_NE(summary.message.find(
-                "LINE_SEARCH Minimizer does not support bounds"),
-            string::npos)
-      << summary.message;
-}
-
-TEST(SolverImpl, InfeasibleParameterBlock) {
-  Problem problem;
-  double x[] = {0.0, 0.0};
-  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
-  problem.SetParameterLowerBound(x, 0, 2.0);
-  problem.SetParameterUpperBound(x, 0, 1.0);
-  Solver::Options options;
-  Solver::Summary summary;
-  Solve(options, &problem, &summary);
-  EXPECT_EQ(summary.termination_type, FAILURE);
-  EXPECT_NE(summary.message.find("infeasible bound"), string::npos)
-      << summary.message;
-}
-
-TEST(SolverImpl, InfeasibleConstantParameterBlock) {
-  Problem problem;
-  double x[] = {0.0, 0.0};
-  problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
-  problem.SetParameterLowerBound(x, 0, 1.0);
-  problem.SetParameterUpperBound(x, 0, 2.0);
-  problem.SetParameterBlockConstant(x);
-  Solver::Options options;
-  Solver::Summary summary;
-  Solve(options, &problem, &summary);
-  EXPECT_EQ(summary.termination_type, FAILURE);
-  EXPECT_NE(summary.message.find("infeasible value"), string::npos)
-      << summary.message;
-}
-
 }  // namespace internal
 }  // namespace ceres