Solver::Options::ordering* are dead.

Remove the old ordering API, and modify solver_impl.cc
to use the new API everywhere.

In the process also clean up the linear solver instantion
logic in solver_impl.cc a bit too.

Change-Id: Ia66898abc7f622070b184b21fce8cc6140c4cebf
diff --git a/internal/ceres/minimizer.h b/internal/ceres/minimizer.h
index cfc98a3..667d80a 100644
--- a/internal/ceres/minimizer.h
+++ b/internal/ceres/minimizer.h
@@ -74,7 +74,6 @@
       lsqp_dump_directory = options.lsqp_dump_directory;
       lsqp_iterations_to_dump = options.lsqp_iterations_to_dump;
       lsqp_dump_format_type = options.lsqp_dump_format_type;
-      num_eliminate_blocks = options.num_eliminate_blocks;
       max_num_consecutive_invalid_steps =
           options.max_num_consecutive_invalid_steps;
       min_trust_region_radius = options.min_trust_region_radius;
@@ -104,7 +103,6 @@
     vector<int> lsqp_iterations_to_dump;
     DumpFormatType lsqp_dump_format_type;
     string lsqp_dump_directory;
-    int num_eliminate_blocks;
     int max_num_consecutive_invalid_steps;
     int min_trust_region_radius;
 
diff --git a/internal/ceres/ordering.cc b/internal/ceres/ordering.cc
index 8b53ea5..4b3ec25 100644
--- a/internal/ceres/ordering.cc
+++ b/internal/ceres/ordering.cc
@@ -70,6 +70,12 @@
   return it->second;
 }
 
+bool Ordering::ContainsParameterBlock(double* parameter_block) const {
+  const map<double*, int>::const_iterator it =
+      parameter_block_to_group_id_.find(parameter_block);
+  return (it != parameter_block_to_group_id_.end());
+}
+
 int Ordering::NumParameterBlocks() const {
   return parameter_block_to_group_id_.size();
 }
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index 84c2756..5a84cfc 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -82,8 +82,6 @@
       num_parameters_reduced(-1),
       num_residual_blocks_reduced(-1),
       num_residuals_reduced(-1),
-      num_eliminate_blocks_given(-1),
-      num_eliminate_blocks_used(-1),
       num_threads_given(-1),
       num_threads_used(-1),
       num_linear_solver_threads_given(-1),
@@ -91,7 +89,6 @@
       linear_solver_type_given(SPARSE_NORMAL_CHOLESKY),
       linear_solver_type_used(SPARSE_NORMAL_CHOLESKY),
       preconditioner_type(IDENTITY),
-      ordering_type(NATURAL),
       trust_region_strategy_type(LEVENBERG_MARQUARDT),
       sparse_linear_algebra_library(SUITE_SPARSE) {
 }
@@ -165,22 +162,7 @@
                             "N/A", "N/A");
   }
 
-  internal::StringAppendF(&report, "Ordering            %25s%25s\n",
-                          OrderingTypeToString(ordering_type),
-                          OrderingTypeToString(ordering_type));
-
-  if (IsSchurType(linear_solver_type_given)) {
-    if (ordering_type == SCHUR) {
-      internal::StringAppendF(&report, "num_eliminate_blocks%25s% 25d\n",
-                              "N/A",
-                              num_eliminate_blocks_used);
-    } else {
-      internal::StringAppendF(&report, "num_eliminate_blocks% 25d% 25d\n",
-                              num_eliminate_blocks_given,
-                              num_eliminate_blocks_used);
-    }
-  }
-
+  // TODO(sameeragarwal): Add support for logging the ordering object.
   internal::StringAppendF(&report, "Threads:            % 25d% 25d\n",
                           num_threads_given, num_threads_used);
   internal::StringAppendF(&report, "Linear solver threads % 23d% 25d\n",
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 4bedbe3..7e90c9b 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -52,6 +52,13 @@
 #include "ceres/wall_time.h"
 
 namespace ceres {
+
+Solver::Options::~Options() {
+  if (ordering != NULL) {
+    delete ordering;
+  }
+}
+
 namespace internal {
 namespace {
 
@@ -202,57 +209,11 @@
                        Solver::Summary* summary) {
   double solver_start_time = WallTimeInSeconds();
   Solver::Options options(original_options);
-
-  // Code for supporting both the old and the new ordering API. This
-  // code will get refactored and re-written as the old API is
-  // steadily deprecated.
-
-  // Clobber all the input parameters from the old api parameters.
-  if (options.use_new_ordering_api) {
-    // For linear solvers which are not of Schur type, do nothing.
-    options.ordering.clear();
-    options.num_eliminate_blocks = 0;
-    options.ordering_type = NATURAL;
-
-    if (IsSchurType(options.linear_solver_type)) {
-      if (options.ordering_new_api == NULL) {
-        // User says we are free to find the independent set, and order
-        // any which way.
-        options.ordering_type = SCHUR;
-      } else {
-        // User has given an ordering and asked for a Schur type solver.
-        options.ordering_type = USER;
-
-        // The lowest numbered group corresponds to
-        // num_eliminate_blocks e_blocks.
-        const map<int, set<double*> >& group_id_to_parameter_block
-            = options.ordering_new_api->group_id_to_parameter_blocks();
-
-        map<int, set<double*> >::const_iterator it =
-            group_id_to_parameter_block.begin();
-        CHECK(it != group_id_to_parameter_block.end());
-
-        options.num_eliminate_blocks = it->second.size();
-        for (; it != group_id_to_parameter_block.end(); ++it) {
-          options.ordering.insert(options.ordering.end(),
-                                  it->second.begin(),
-                                  it->second.end());
-        }
-        CHECK_EQ(options.ordering.size(),
-                 original_problem_impl->NumParameterBlocks());
-      }
-    }
-  } else {
-    CHECK(options.ordering_new_api == NULL);
-  }
-
-
   Program* original_program = original_problem_impl->mutable_program();
   ProblemImpl* problem_impl = original_problem_impl;
   // Reset the summary object to its default values.
   *CHECK_NOTNULL(summary) = Solver::Summary();
 
-
 #ifndef CERES_USE_OPENMP
   if (options.num_threads > 1) {
     LOG(WARNING)
@@ -270,25 +231,10 @@
   }
 #endif
 
-  if (IsSchurType(options.linear_solver_type) &&
-      options.ordering_type != SCHUR &&
-      options.num_eliminate_blocks < 1) {
-    summary->error =
-        StringPrintf("Using a Schur type solver with %s"
-                     " ordering requires that"
-                     " Solver::Options::num_eliminate_blocks"
-                     " be set to a positive integer.",
-                     OrderingTypeToString(options.ordering_type));
-    LOG(WARNING) << summary->error;
-    return;
-  }
-
   summary->linear_solver_type_given = options.linear_solver_type;
-  summary->num_eliminate_blocks_given = original_options.num_eliminate_blocks;
   summary->num_threads_given = original_options.num_threads;
   summary->num_linear_solver_threads_given =
       original_options.num_linear_solver_threads;
-  summary->ordering_type = original_options.ordering_type;
 
   summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
   summary->num_parameters = problem_impl->NumParameters();
@@ -301,6 +247,23 @@
   summary->trust_region_strategy_type = options.trust_region_strategy_type;
   summary->dogleg_type = options.dogleg_type;
 
+  if (original_options.ordering != NULL) {
+    if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
+      LOG(WARNING) << summary->error;
+      return;
+    }
+    options.ordering = new Ordering(*original_options.ordering);
+  } else {
+    options.ordering = new Ordering;
+    const ProblemImpl::ParameterMap& parameter_map =
+        problem_impl->parameter_map();
+    for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
+         it != parameter_map.end();
+         ++it) {
+      options.ordering->AddParameterBlockToGroup(it->first, 0);
+    }
+  }
+
   // Evaluate the initial cost, residual vector and the jacobian
   // matrix if requested by the user. The initial cost needs to be
   // computed on the original unpreprocessed problem, as it is used to
@@ -313,13 +276,14 @@
       options.return_initial_residuals ? &summary->initial_residuals : NULL,
       options.return_initial_gradient ? &summary->initial_gradient : NULL,
       options.return_initial_jacobian ? &summary->initial_jacobian : NULL);
-   original_program->SetParameterBlockStatePtrsToUserStatePtrs();
+  original_program->SetParameterBlockStatePtrsToUserStatePtrs();
 
   // If the user requests gradient checking, construct a new
   // ProblemImpl by wrapping the CostFunctions of problem_impl inside
   // GradientCheckingCostFunction and replacing problem_impl with
   // gradient_checking_problem_impl.
   scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
+
   // Save the original problem impl so we don't use the gradient
   // checking one when computing the residuals.
   if (options.check_gradients) {
@@ -353,7 +317,6 @@
       linear_solver(CreateLinearSolver(&options, &summary->error));
   summary->linear_solver_type_used = options.linear_solver_type;
   summary->preconditioner_type = options.preconditioner_type;
-  summary->num_eliminate_blocks_used = options.num_eliminate_blocks;
   summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
 
   if (linear_solver == NULL) {
@@ -421,13 +384,67 @@
       WallTimeInSeconds() - post_process_start_time;
 }
 
+bool SolverImpl::IsOrderingValid(const Solver::Options& options,
+                                 const ProblemImpl* problem_impl,
+                                 string* error) {
+  if (options.ordering->NumParameterBlocks() !=
+      problem_impl->NumParameterBlocks()) {
+      *error = "Number of parameter blocks in user supplied ordering "
+          "does not match the number of parameter blocks in the problem";
+    return false;
+  }
+
+  const Program& program = problem_impl->program();
+  const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
+  const vector<ResidualBlock*>& residual_blocks = program.residual_blocks();
+  for (vector<ParameterBlock*>::const_iterator it = parameter_blocks.begin();
+       it != parameter_blocks.end();
+       ++it) {
+    if (!options.ordering->ContainsParameterBlock(
+            const_cast<double*>((*it)->user_state()))) {
+      *error = "Problem contains a parameter block that is not in "
+          "the user specified ordering.";
+      return false;
+    }
+  }
+
+  if (IsSchurType(options.linear_solver_type) &&
+      options.ordering->NumGroups() > 1) {
+    const set<double*>& e_blocks  =
+        options.ordering->group_id_to_parameter_blocks().begin()->second;
+
+    // Loop over each residual block and ensure that no two parameter
+    // blocks in the same residual block are part of the first
+    // elimination group, 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 += e_blocks.count(const_cast<double*>(
+                                    parameter_blocks[i]->user_state()));
+      }
+
+      if (count > 1) {
+        *error = "The user requested the use of a Schur type solver. "
+            "But the first elimination group in the ordering is not an "
+            "independent set.";
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 // Strips varying parameters and residuals, maintaining order, and updating
 // num_eliminate_blocks.
 bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
-                                              int* num_eliminate_blocks,
+                                              Ordering* ordering,
                                               double* fixed_cost,
                                               string* error) {
-  int original_num_eliminate_blocks = *num_eliminate_blocks;
   vector<ParameterBlock*>* parameter_blocks =
       program->mutable_parameter_blocks();
 
@@ -484,7 +501,7 @@
   }
 
   // Filter out unused or fixed parameter blocks, and update
-  // num_eliminate_blocks as necessary.
+  // the ordering.
   {
     vector<ParameterBlock*>* parameter_blocks =
         program->mutable_parameter_blocks();
@@ -493,8 +510,8 @@
       ParameterBlock* parameter_block = (*parameter_blocks)[i];
       if (parameter_block->index() == 1) {
         (*parameter_blocks)[j++] = parameter_block;
-      } else if (i < original_num_eliminate_blocks) {
-        (*num_eliminate_blocks)--;
+      } else {
+        ordering->RemoveParameterBlock(parameter_block->mutable_user_state());
       }
     }
     parameter_blocks->resize(j);
@@ -512,35 +529,17 @@
                                           ProblemImpl* problem_impl,
                                           double* fixed_cost,
                                           string* error) {
+  CHECK_NOTNULL(options->ordering);
   Program* original_program = problem_impl->mutable_program();
   scoped_ptr<Program> transformed_program(new Program(*original_program));
+  Ordering* ordering = options->ordering;
 
-  if (options->ordering_type == USER &&
-      !ApplyUserOrdering(*problem_impl,
-                         options->ordering,
-                         transformed_program.get(),
-                         error)) {
-    return NULL;
-  }
-
-  if (options->ordering_type == SCHUR && options->num_eliminate_blocks != 0) {
-    *error = "Can't specify SCHUR ordering and num_eliminate_blocks "
-        "at the same time; SCHUR ordering determines "
-        "num_eliminate_blocks automatically.";
-    return NULL;
-  }
-
-  if (options->ordering_type == SCHUR && options->ordering.size() != 0) {
-    *error = "Can't specify SCHUR ordering type and the ordering "
-        "vector at the same time; SCHUR ordering determines "
-        "a suitable parameter ordering automatically.";
-    return NULL;
-  }
-
-  int num_eliminate_blocks = options->num_eliminate_blocks;
+  const int min_group_id =
+      ordering->group_id_to_parameter_blocks().begin()->first;
+  const int original_num_groups = ordering->NumGroups();
 
   if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
-                                    &num_eliminate_blocks,
+                                    ordering,
                                     fixed_cost,
                                     error)) {
     return NULL;
@@ -552,21 +551,70 @@
     return transformed_program.release();
   }
 
-  if (options->ordering_type == SCHUR) {
+  // If the user supplied an ordering with just one group, it is
+  // equivalent to the user supplying NULL as ordering. Ceres is
+  // completely free to choose the parameter block ordering as it sees
+  // fit. For Schur type solvers, this means that the user wishes for
+  // Ceres to identify the e_blocks, which we do by computing a
+  // maximal independent set.
+  if (original_num_groups == 1 &&
+      IsSchurType(options->linear_solver_type)) {
     vector<ParameterBlock*> schur_ordering;
-    num_eliminate_blocks = ComputeSchurOrdering(*transformed_program,
-                                                &schur_ordering);
+    const int num_eliminate_blocks = ComputeSchurOrdering(*original_program,
+                                                          &schur_ordering);
     CHECK_EQ(schur_ordering.size(), transformed_program->NumParameterBlocks())
         << "Congratulations, you found a Ceres bug! Please report this error "
         << "to the developers.";
 
-    // Replace the transformed program's ordering with the schur ordering.
-    swap(*transformed_program->mutable_parameter_blocks(), schur_ordering);
+    for (int i = 0; i < schur_ordering.size(); ++i) {
+      ordering->AddParameterBlockToGroup(schur_ordering[i]->mutable_user_state(),
+                                         (i < num_eliminate_blocks) ? 0 : 1);
+    }
   }
-  options->num_eliminate_blocks = num_eliminate_blocks;
-  CHECK_GE(options->num_eliminate_blocks, 0)
-      << "Congratulations, you found a Ceres bug! Please report this error "
-      << "to the developers.";
+
+  if (!ApplyUserOrdering(
+          *problem_impl, ordering, transformed_program.get(), error)) {
+    return NULL;
+  }
+
+  // If the user requested the use of a Schur type solver, and
+  // supplied a non-NULL ordering object with more than one
+  // elimimation group, then it can happen that after all the
+  // parameter blocks which are fixed or unused have been removed from
+  // the program and the ordering, there are no more parameter blocks
+  // in the first elimination group.
+  //
+  // In such a case, the use of a Schur type solver is not possible,
+  // as they assume there is at least one e_block. Thus, we
+  // automatically switch to one of the other solvers, depending on
+  // the user's indicated preferences.
+  if (IsSchurType(options->linear_solver_type) &&
+      original_num_groups > 1 &&
+      ordering->GroupSize(min_group_id) == 0) {
+    string msg = "No e_blocks remaining. Switching from ";
+    if (options->linear_solver_type == SPARSE_SCHUR) {
+      options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+      msg += "SPARSE_SCHUR to SPARSE_NORMAL_CHOLESKY.";
+    } else if (options->linear_solver_type == DENSE_SCHUR) {
+      // TODO(sameeragarwal): This is probably not a great choice.
+      // Ideally, we should have a DENSE_NORMAL_CHOLESKY, that can
+      // take a BlockSparseMatrix as input.
+      options->linear_solver_type = DENSE_QR;
+      msg += "DENSE_SCHUR to DENSE_QR.";
+    } else if (options->linear_solver_type == ITERATIVE_SCHUR) {
+      msg += StringPrintf("ITERATIVE_SCHUR with %s preconditioner "
+                          "to CGNR with JACOBI preconditioner.",
+                          PreconditionerTypeToString(
+                              options->preconditioner_type));
+      options->linear_solver_type = CGNR;
+      if (options->preconditioner_type != IDENTITY) {
+        // CGNR currently only supports the JACOBI preconditioner.
+        options->preconditioner_type = JACOBI;
+      }
+    }
+
+    LOG(WARNING) << msg;
+  }
 
   // Since the transformed program is the "active" program, and it is mutated,
   // update the parameter offsets and indices.
@@ -576,6 +624,10 @@
 
 LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
                                              string* error) {
+  CHECK_NOTNULL(options);
+  CHECK_NOTNULL(options->ordering);
+  CHECK_NOTNULL(error);
+
   if (options->trust_region_strategy_type == DOGLEG) {
     if (options->linear_solver_type == ITERATIVE_SCHUR ||
         options->linear_solver_type == CGNR) {
@@ -593,6 +645,24 @@
              "SuiteSparse was not enabled when Ceres was built.";
     return NULL;
   }
+
+  if (linear_solver_options.preconditioner_type == SCHUR_JACOBI) {
+    *error =  "SCHUR_JACOBI preconditioner not suppored. Please build Ceres "
+        "with SuiteSparse support.";
+    return NULL;
+  }
+
+  if (linear_solver_options.preconditioner_type == CLUSTER_JACOBI) {
+    *error =  "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
+        "with SuiteSparse support.";
+    return NULL;
+  }
+
+  if (linear_solver_options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
+    *error =  "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
+        "Ceres with SuiteSparse support.";
+    return NULL;
+  }
 #endif
 
 #ifdef CERES_NO_CXSPARSE
@@ -604,6 +674,13 @@
   }
 #endif
 
+#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
+  if (linear_solver_options.type == SPARSE_SCHUR) {
+    *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
+        "CXSparse was enabled when Ceres was compiled.";
+    return NULL;
+  }
+#endif
 
   if (options->linear_solver_max_num_iterations <= 0) {
     *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
@@ -629,61 +706,8 @@
   linear_solver_options.preconditioner_type = options->preconditioner_type;
   linear_solver_options.sparse_linear_algebra_library =
       options->sparse_linear_algebra_library;
-  linear_solver_options.use_block_amd = options->use_block_amd;
-
-#ifdef CERES_NO_SUITESPARSE
-  if (linear_solver_options.preconditioner_type == SCHUR_JACOBI) {
-    *error =  "SCHUR_JACOBI preconditioner not suppored. Please build Ceres "
-        "with SuiteSparse support.";
-    return NULL;
-  }
-
-  if (linear_solver_options.preconditioner_type == CLUSTER_JACOBI) {
-    *error =  "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
-        "with SuiteSparse support.";
-    return NULL;
-  }
-
-  if (linear_solver_options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
-    *error =  "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
-        "Ceres with SuiteSparse support.";
-    return NULL;
-  }
-#endif
 
   linear_solver_options.num_threads = options->num_linear_solver_threads;
-  if (options->num_eliminate_blocks > 0) {
-    linear_solver_options
-        .elimination_groups.push_back(options->num_eliminate_blocks);
-  }
-
-  // TODO(sameeragarwal): Fix this. Right now these are dummy values
-  // and violate the contract that elimination_groups should sum to
-  // the number of parameter blocks, but fixing this requires a bit
-  // more refactoring in solver_impl.cc, which is better done as we
-  // start deprecating the old API.
-  linear_solver_options.elimination_groups.push_back(1);
-
-  if (linear_solver_options.elimination_groups.size() == 1 &&
-      IsSchurType(linear_solver_options.type)) {
-#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
-    LOG(INFO) << "No elimination block remaining switching to DENSE_QR.";
-    linear_solver_options.type = DENSE_QR;
-#else
-    LOG(INFO) << "No elimination block remaining "
-              << "switching to SPARSE_NORMAL_CHOLESKY.";
-    linear_solver_options.type = SPARSE_NORMAL_CHOLESKY;
-#endif
-  }
-
-#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
-  if (linear_solver_options.type == SPARSE_SCHUR) {
-    *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
-             "CXSparse was enabled when Ceres was compiled.";
-    return NULL;
-  }
-#endif
-
   // The matrix used for storing the dense Schur complement has a
   // single lock guarding the whole matrix. Running the
   // SchurComplementSolver with multiple threads leads to maximum
@@ -698,56 +722,68 @@
                  << "switching to single-threaded.";
     linear_solver_options.num_threads = 1;
   }
-
-  options->linear_solver_type = linear_solver_options.type;
   options->num_linear_solver_threads = linear_solver_options.num_threads;
 
+  linear_solver_options.use_block_amd = options->use_block_amd;
+  const map<int, set<double*> >& groups =
+      options->ordering->group_id_to_parameter_blocks();
+  for (map<int, set<double*> >::const_iterator it = groups.begin();
+       it != groups.end();
+       ++it) {
+    linear_solver_options.elimination_groups.push_back(it->second.size());
+  }
+  // Schur type solvers, expect at least two elimination groups. If
+  // there is only one elimination group, then CreateReducedProblem
+  // guarantees that this group only contains e_blocks. Thus we add a
+  // dummy elimination group with zero blocks in it.
+  if (IsSchurType(linear_solver_options.type) &&
+      linear_solver_options.elimination_groups.size() == 1) {
+    linear_solver_options.elimination_groups.push_back(0);
+  }
+
   return LinearSolver::Create(linear_solver_options);
 }
 
 bool SolverImpl::ApplyUserOrdering(const ProblemImpl& problem_impl,
-                                   vector<double*>& ordering,
+                                   const Ordering* ordering,
                                    Program* program,
                                    string* error) {
-  if (ordering.size() != program->NumParameterBlocks()) {
+  if (ordering->NumParameterBlocks() != program->NumParameterBlocks()) {
     *error = StringPrintf("User specified ordering does not have the same "
                           "number of parameters as the problem. The problem"
-                          "has %d blocks while the ordering has %ld blocks.",
+                          "has %d blocks while the ordering has %d blocks.",
                           program->NumParameterBlocks(),
-                          ordering.size());
+                          ordering->NumParameterBlocks());
     return false;
   }
 
-  // Ensure that there are no duplicates in the user's ordering.
-  {
-    vector<double*> ordering_copy(ordering);
-    sort(ordering_copy.begin(), ordering_copy.end());
-    if (unique(ordering_copy.begin(), ordering_copy.end())
-        != ordering_copy.end()) {
-      *error = "User specified ordering contains duplicates.";
-      return false;
-    }
-  }
-
   vector<ParameterBlock*>* parameter_blocks =
       program->mutable_parameter_blocks();
+  parameter_blocks->clear();
 
-  fill(parameter_blocks->begin(),
-       parameter_blocks->end(),
-       static_cast<ParameterBlock*>(NULL));
+  const ProblemImpl::ProblemImpl::ParameterMap& parameter_map =
+      problem_impl.parameter_map();
+  const map<int, set<double*> >& groups =
+      ordering->group_id_to_parameter_blocks();
 
-  const ProblemImpl::ParameterMap& parameter_map = problem_impl.parameter_map();
-  for (int i = 0; i < ordering.size(); ++i) {
-    ProblemImpl::ParameterMap::const_iterator it =
-        parameter_map.find(ordering[i]);
-    if (it == parameter_map.end()) {
-      *error = StringPrintf("User specified ordering contains a pointer "
-                            "to a double that is not a parameter block in the "
-                            "problem. The invalid double is at position %d "
-                            " in options.ordering.", i);
-      return false;
+  for (map<int, set<double*> >::const_iterator group_it = groups.begin();
+       group_it != groups.end();
+       ++group_it) {
+    const set<double*>& group = group_it->second;
+    for (set<double*>::const_iterator parameter_block_ptr_it = group.begin();
+         parameter_block_ptr_it != group.end();
+         ++parameter_block_ptr_it) {
+      ProblemImpl::ParameterMap::const_iterator parameter_block_it =
+          parameter_map.find(*parameter_block_ptr_it);
+      if (parameter_block_it == parameter_map.end()) {
+        *error = StringPrintf("User specified ordering contains a pointer "
+                              "to a double that is not a parameter block in the "
+                              "problem. The invalid double is in group: %d",
+                              group_it->first);
+        return false;
+      }
+      parameter_blocks->push_back(parameter_block_it->second);
     }
-    (*parameter_blocks)[i] = it->second;
   }
   return true;
 }
@@ -782,28 +818,26 @@
     return true;
   }
 
-  CHECK_NE(0, options.num_eliminate_blocks)
-        << "Congratulations, you found a Ceres bug! Please report this error "
-        << "to the developers.";
+  const int num_eliminate_blocks =
+      options.ordering->group_id_to_parameter_blocks().begin()->second.size();
 
   // Create a histogram of the number of residuals for each E block. There is an
   // extra bucket at the end to catch all non-eliminated F blocks.
-  vector<int> residual_blocks_per_e_block(options.num_eliminate_blocks + 1);
+  vector<int> residual_blocks_per_e_block(num_eliminate_blocks + 1);
   vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
   vector<int> min_position_per_residual(residual_blocks->size());
   for (int i = 0; i < residual_blocks->size(); ++i) {
     ResidualBlock* residual_block = (*residual_blocks)[i];
-    int position = MinParameterBlock(residual_block,
-                                     options.num_eliminate_blocks);
+    int position = MinParameterBlock(residual_block, num_eliminate_blocks);
     min_position_per_residual[i] = position;
-    DCHECK_LE(position, options.num_eliminate_blocks);
+    DCHECK_LE(position, num_eliminate_blocks);
     residual_blocks_per_e_block[position]++;
   }
 
   // Run a cumulative sum on the histogram, to obtain offsets to the start of
   // each histogram bucket (where each bucket is for the residuals for that
   // E-block).
-  vector<int> offsets(options.num_eliminate_blocks + 1);
+  vector<int> offsets(num_eliminate_blocks + 1);
   std::partial_sum(residual_blocks_per_e_block.begin(),
                    residual_blocks_per_e_block.end(),
                    offsets.begin());
@@ -842,7 +876,7 @@
 
   // Sanity check #1: The difference in bucket offsets should match the
   // histogram sizes.
-  for (int i = 0; i < options.num_eliminate_blocks; ++i) {
+  for (int i = 0; i < num_eliminate_blocks; ++i) {
     CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
         << "Congratulations, you found a Ceres bug! Please report this error "
         << "to the developers.";
@@ -864,7 +898,12 @@
                                        string* error) {
   Evaluator::Options evaluator_options;
   evaluator_options.linear_solver_type = options.linear_solver_type;
-  evaluator_options.num_eliminate_blocks = options.num_eliminate_blocks;
+
+  evaluator_options.num_eliminate_blocks =
+      (options.ordering->NumGroups() > 0 &&
+       IsSchurType(options.linear_solver_type))
+       ? options.ordering->group_id_to_parameter_blocks().begin()->second.size()
+       : 0;
   evaluator_options.num_threads = options.num_threads;
   return Evaluator::Create(evaluator_options, program, error);
 }
diff --git a/internal/ceres/solver_impl.h b/internal/ceres/solver_impl.h
index 11b44de..076a371 100644
--- a/internal/ceres/solver_impl.h
+++ b/internal/ceres/solver_impl.h
@@ -37,6 +37,8 @@
 #include "ceres/solver.h"
 
 namespace ceres {
+class Ordering;
+
 namespace internal {
 
 class Evaluator;
@@ -76,7 +78,7 @@
   // indicates an error was encountered whose cause is logged to
   // LOG(ERROR).
   static bool ApplyUserOrdering(const ProblemImpl& problem_impl,
-                                vector<double*>& ordering,
+                                const Ordering* ordering,
                                 Program* program,
                                 string* error);
 
@@ -108,9 +110,13 @@
   // 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,
+                                           Ordering* ordering,
                                            double* fixed_cost,
                                            string* error);
+
+  static bool IsOrderingValid(const Solver::Options& options,
+                              const ProblemImpl* problem_impl,
+                              string* error);
 };
 
 }  // namespace internal
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc
index 7d7740a..a19fca0 100644
--- a/internal/ceres/solver_impl_test.cc
+++ b/internal/ceres/solver_impl_test.cc
@@ -31,6 +31,7 @@
 #include "gtest/gtest.h"
 #include "ceres/autodiff_cost_function.h"
 #include "ceres/linear_solver.h"
+#include "ceres/ordering.h"
 #include "ceres/parameter_block.h"
 #include "ceres/problem_impl.h"
 #include "ceres/program.h"
@@ -87,29 +88,19 @@
 
   string error;
   {
-    int num_eliminate_blocks = 0;
+    Ordering ordering;
+    ordering.AddParameterBlockToGroup(&x, 0);
+    ordering.AddParameterBlockToGroup(&y, 0);
+    ordering.AddParameterBlockToGroup(&z, 0);
+
     Program program(*problem.mutable_program());
     EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                         &num_eliminate_blocks,
+                                                         &ordering,
                                                          NULL,
                                                          &error));
     EXPECT_EQ(program.NumParameterBlocks(), 3);
     EXPECT_EQ(program.NumResidualBlocks(), 3);
-    EXPECT_EQ(num_eliminate_blocks, 0);
-  }
-
-  // Check that num_eliminate_blocks is preserved, when it contains
-  // all blocks.
-  {
-    int num_eliminate_blocks = 3;
-    Program program(problem.program());
-    EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                         &num_eliminate_blocks,
-                                                         NULL,
-                                                         &error));
-    EXPECT_EQ(program.NumParameterBlocks(), 3);
-    EXPECT_EQ(program.NumResidualBlocks(), 3);
-    EXPECT_EQ(num_eliminate_blocks, 3);
+    EXPECT_EQ(ordering.NumParameterBlocks(), 3);
   }
 }
 
@@ -121,16 +112,18 @@
   problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
   problem.SetParameterBlockConstant(&x);
 
-  int num_eliminate_blocks = 0;
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+
   Program program(problem.program());
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                       &num_eliminate_blocks,
+                                                       &ordering,
                                                        NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 0);
   EXPECT_EQ(program.NumResidualBlocks(), 0);
-  EXPECT_EQ(num_eliminate_blocks, 0);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 0);
 }
 
 TEST(SolverImpl, RemoveFixedBlocksNoResidualBlocks) {
@@ -143,16 +136,21 @@
   problem.AddParameterBlock(&y, 1);
   problem.AddParameterBlock(&z, 1);
 
-  int num_eliminate_blocks = 0;
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 0);
+  ordering.AddParameterBlockToGroup(&z, 0);
+
+
   Program program(problem.program());
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                       &num_eliminate_blocks,
+                                                       &ordering,
                                                        NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 0);
   EXPECT_EQ(program.NumResidualBlocks(), 0);
-  EXPECT_EQ(num_eliminate_blocks, 0);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 0);
 }
 
 TEST(SolverImpl, RemoveFixedBlocksOneParameterBlockConstant) {
@@ -164,20 +162,26 @@
   problem.AddParameterBlock(&x, 1);
   problem.AddParameterBlock(&y, 1);
   problem.AddParameterBlock(&z, 1);
+
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 0);
+  ordering.AddParameterBlockToGroup(&z, 0);
+
   problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
   problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
   problem.SetParameterBlockConstant(&x);
 
-  int num_eliminate_blocks = 0;
+
   Program program(problem.program());
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                       &num_eliminate_blocks,
+                                                       &ordering,
                                                        NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 1);
   EXPECT_EQ(program.NumResidualBlocks(), 1);
-  EXPECT_EQ(num_eliminate_blocks, 0);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 1);
 }
 
 TEST(SolverImpl, RemoveFixedBlocksNumEliminateBlocks) {
@@ -194,16 +198,22 @@
   problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
   problem.SetParameterBlockConstant(&x);
 
-  int num_eliminate_blocks = 2;
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 0);
+  ordering.AddParameterBlockToGroup(&z, 1);
+
   Program program(problem.program());
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                       &num_eliminate_blocks,
+                                                       &ordering,
                                                        NULL,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 2);
   EXPECT_EQ(program.NumResidualBlocks(), 2);
-  EXPECT_EQ(num_eliminate_blocks, 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 2);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(&y), 0);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(&z), 1);
 }
 
 TEST(SolverImpl, RemoveFixedBlocksFixedCost) {
@@ -220,7 +230,11 @@
   problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
   problem.SetParameterBlockConstant(&x);
 
-  int num_eliminate_blocks = 2;
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 0);
+  ordering.AddParameterBlockToGroup(&z, 1);
+
   double fixed_cost = 0.0;
   Program program(problem.program());
 
@@ -231,12 +245,14 @@
 
   string error;
   EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
-                                                       &num_eliminate_blocks,
+                                                       &ordering,
                                                        &fixed_cost,
                                                        &error));
   EXPECT_EQ(program.NumParameterBlocks(), 2);
   EXPECT_EQ(program.NumResidualBlocks(), 2);
-  EXPECT_EQ(num_eliminate_blocks, 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 2);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(&y), 0);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(&z), 1);
   EXPECT_DOUBLE_EQ(fixed_cost, expected_fixed_cost);
 }
 
@@ -253,6 +269,11 @@
   problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
   problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
 
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 0);
+  ordering.AddParameterBlockToGroup(&z, 0);
+
   const vector<ResidualBlock*>& residual_blocks =
       problem.program().residual_blocks();
   vector<ResidualBlock*> current_residual_blocks(residual_blocks);
@@ -270,31 +291,6 @@
   }
 }
 
-TEST(SolverImpl, ReorderResidualBlockNumEliminateBlockDeathTest) {
-  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);
-
-  Solver::Options options;
-  options.linear_solver_type = DENSE_SCHUR;
-  options.num_eliminate_blocks = 0;
-  string error;
-#ifndef _WIN32
-  EXPECT_DEATH(
-      SolverImpl::MaybeReorderResidualBlocks(
-          options, problem.mutable_program(), &error),
-      "Congratulations");
-#endif  // _WIN32
-}
-
 TEST(SolverImpl, ReorderResidualBlockNormalFunction) {
   ProblemImpl problem;
   double x;
@@ -312,9 +308,14 @@
   problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
   problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);
 
+  Ordering* ordering = new Ordering;
+  ordering->AddParameterBlockToGroup(&x, 0);
+  ordering->AddParameterBlockToGroup(&y, 0);
+  ordering->AddParameterBlockToGroup(&z, 1);
+
   Solver::Options options;
   options.linear_solver_type = DENSE_SCHUR;
-  options.num_eliminate_blocks = 2;
+  options.ordering = ordering;
 
   const vector<ResidualBlock*>& residual_blocks =
       problem.program().residual_blocks();
@@ -368,9 +369,14 @@
   problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z);  // 6 x
   problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);       // 7
 
+  Ordering* ordering = new Ordering;
+  ordering->AddParameterBlockToGroup(&x, 0);
+  ordering->AddParameterBlockToGroup(&z, 0);
+  ordering->AddParameterBlockToGroup(&y, 1);
+
   Solver::Options options;
   options.linear_solver_type = DENSE_SCHUR;
-  options.num_eliminate_blocks = 2;
+  options.ordering = ordering;
 
   // Create the reduced program. This should remove the fixed block "z",
   // marking the index to -1 at the same time. x and y also get indices.
@@ -423,42 +429,18 @@
   problem.AddParameterBlock(&y, 1);
   problem.AddParameterBlock(&z, 1);
 
-  vector<double*> ordering;
-  ordering.push_back(&x);
-  ordering.push_back(&z);
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 1);
 
   Program program(problem.program());
   string error;
   EXPECT_FALSE(SolverImpl::ApplyUserOrdering(problem,
-                                             ordering,
+                                             &ordering,
                                              &program,
                                              &error));
 }
 
-TEST(SolverImpl, ApplyUserOrderingHasDuplicates) {
-  ProblemImpl problem;
-  double x;
-  double y;
-  double z;
-
-  problem.AddParameterBlock(&x, 1);
-  problem.AddParameterBlock(&y, 1);
-  problem.AddParameterBlock(&z, 1);
-
-  vector<double*> ordering;
-  ordering.push_back(&x);
-  ordering.push_back(&z);
-  ordering.push_back(&z);
-
-  Program program(problem.program());
-  string error;
-  EXPECT_FALSE(SolverImpl::ApplyUserOrdering(problem,
-                                             ordering,
-                                             &program,
-                                             &error));
-}
-
-
 TEST(SolverImpl, ApplyUserOrderingNormal) {
   ProblemImpl problem;
   double x;
@@ -469,16 +451,16 @@
   problem.AddParameterBlock(&y, 1);
   problem.AddParameterBlock(&z, 1);
 
-  vector<double*> ordering;
-  ordering.push_back(&x);
-  ordering.push_back(&z);
-  ordering.push_back(&y);
+  Ordering ordering;
+  ordering.AddParameterBlockToGroup(&x, 0);
+  ordering.AddParameterBlockToGroup(&y, 2);
+  ordering.AddParameterBlockToGroup(&z, 1);
 
   Program* program = problem.mutable_program();
   string error;
 
   EXPECT_TRUE(SolverImpl::ApplyUserOrdering(problem,
-                                            ordering,
+                                            &ordering,
                                             program,
                                             &error));
   const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks();
@@ -502,6 +484,8 @@
   Solver::Options options;
   options.linear_solver_type = DENSE_QR;
   options.linear_solver_max_num_iterations = -1;
+  // CreateLinearSolver assumes a non-empty ordering.
+  options.ordering = new Ordering;
   string error;
   EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
             static_cast<LinearSolver*>(NULL));
@@ -511,6 +495,8 @@
   Solver::Options options;
   options.linear_solver_type = DENSE_QR;
   options.linear_solver_min_num_iterations = -1;
+  // CreateLinearSolver assumes a non-empty ordering.
+  options.ordering = new Ordering;
   string error;
   EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
             static_cast<LinearSolver*>(NULL));
@@ -521,44 +507,24 @@
   options.linear_solver_type = DENSE_QR;
   options.linear_solver_min_num_iterations = 10;
   options.linear_solver_max_num_iterations = 5;
+  options.ordering = new Ordering;
   string error;
   EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
             static_cast<LinearSolver*>(NULL));
 }
 
-TEST(SolverImpl, CreateLinearSolverZeroNumEliminateBlocks) {
-  Solver::Options options;
-  options.num_eliminate_blocks = 0;
-  options.linear_solver_type = DENSE_SCHUR;
-  string error;
-  scoped_ptr<LinearSolver> solver(
-      SolverImpl::CreateLinearSolver(&options, &error));
-  EXPECT_TRUE(solver != NULL);
-
-#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
-  EXPECT_EQ(options.linear_solver_type, DENSE_QR);
-#else
-  EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
-#endif
-}
-
-TEST(SolverImpl, ZeroNumEliminateBlocks) {
-  Solver::Options options;
-  options.num_eliminate_blocks = 0;
-  options.linear_solver_type = DENSE_SCHUR;
-  options.ordering_type = NATURAL;
-  ProblemImpl problem;
-  Solver::Summary summary;
-  SolverImpl::Solve(options, &problem, &summary);
-  EXPECT_EQ(summary.termination_type, DID_NOT_RUN);
-  EXPECT_EQ(summary.error.substr(0,25), "Using a Schur type solver");
-}
-
 TEST(SolverImpl, CreateLinearSolverDenseSchurMultipleThreads) {
   Solver::Options options;
-  options.num_eliminate_blocks = 1;
   options.linear_solver_type = DENSE_SCHUR;
   options.num_linear_solver_threads = 2;
+  // The Schur type solvers can only be created with the Ordering
+  // contains at least one elimination group.
+  options.ordering = new Ordering;
+  double x;
+  double y;
+  options.ordering->AddParameterBlockToGroup(&x, 0);
+  options.ordering->AddParameterBlockToGroup(&y, 0);
+
   string error;
   scoped_ptr<LinearSolver> solver(
       SolverImpl::CreateLinearSolver(&options, &error));
@@ -570,6 +536,8 @@
 TEST(SolverImpl, CreateIterativeLinearSolverForDogleg) {
   Solver::Options options;
   options.trust_region_strategy_type = DOGLEG;
+  // CreateLinearSolver assumes a non-empty ordering.
+  options.ordering = new Ordering;
   string error;
   options.linear_solver_type = ITERATIVE_SCHUR;
   EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
@@ -584,6 +552,8 @@
   Solver::Options options;
   scoped_ptr<LinearSolver> solver;
   options.linear_solver_type = DENSE_QR;
+  // CreateLinearSolver assumes a non-empty ordering.
+  options.ordering = new Ordering;
   string error;
   solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
   EXPECT_EQ(options.linear_solver_type, DENSE_QR);
@@ -610,14 +580,17 @@
   EXPECT_TRUE(solver.get() != NULL);
 #endif
 
+  double x;
+  double y;
+  options.ordering->AddParameterBlockToGroup(&x, 0);
+  options.ordering->AddParameterBlockToGroup(&y, 0);
+
   options.linear_solver_type = DENSE_SCHUR;
-  options.num_eliminate_blocks = 2;
   solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
   EXPECT_EQ(options.linear_solver_type, DENSE_SCHUR);
   EXPECT_TRUE(solver.get() != NULL);
 
   options.linear_solver_type = SPARSE_SCHUR;
-  options.num_eliminate_blocks = 2;
   solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
 
 #if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
@@ -628,7 +601,6 @@
 #endif
 
   options.linear_solver_type = ITERATIVE_SCHUR;
-  options.num_eliminate_blocks = 2;
   solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
   EXPECT_EQ(options.linear_solver_type, ITERATIVE_SCHUR);
   EXPECT_TRUE(solver.get() != NULL);
diff --git a/internal/ceres/system_test.cc b/internal/ceres/system_test.cc
index 3dfbc00..4d78de7 100644
--- a/internal/ceres/system_test.cc
+++ b/internal/ceres/system_test.cc
@@ -44,6 +44,7 @@
 #include <string>
 
 #include "ceres/autodiff_cost_function.h"
+#include "ceres/ordering.h"
 #include "ceres/problem.h"
 #include "ceres/rotation.h"
 #include "ceres/solver.h"
@@ -57,28 +58,30 @@
 namespace ceres {
 namespace internal {
 
+const bool kAutomaticOrdering = true;
+const bool kUserOrdering = false;
+
 // Struct used for configuring the solver.
 struct SolverConfig {
   SolverConfig(LinearSolverType linear_solver_type,
                SparseLinearAlgebraLibraryType sparse_linear_algebra_library,
-               OrderingType ordering_type)
+               bool use_automatic_ordering)
       : linear_solver_type(linear_solver_type),
         sparse_linear_algebra_library(sparse_linear_algebra_library),
-        ordering_type(ordering_type),
+        use_automatic_ordering(use_automatic_ordering),
         preconditioner_type(IDENTITY),
         num_threads(1) {
   }
 
   SolverConfig(LinearSolverType linear_solver_type,
                SparseLinearAlgebraLibraryType sparse_linear_algebra_library,
-               OrderingType ordering_type,
-               PreconditionerType preconditioner_type,
-               int num_threads)
+               bool use_automatic_ordering,
+               PreconditionerType preconditioner_type)
       : linear_solver_type(linear_solver_type),
         sparse_linear_algebra_library(sparse_linear_algebra_library),
-        ordering_type(ordering_type),
+        use_automatic_ordering(use_automatic_ordering),
         preconditioner_type(preconditioner_type),
-        num_threads(num_threads) {
+        num_threads(1) {
   }
 
   string ToString() const {
@@ -86,14 +89,14 @@
         "(%s, %s, %s, %s, %d)",
         LinearSolverTypeToString(linear_solver_type),
         SparseLinearAlgebraLibraryTypeToString(sparse_linear_algebra_library),
-        OrderingTypeToString(ordering_type),
+        use_automatic_ordering ? "AUTOMATIC" : "USER",
         PreconditionerTypeToString(preconditioner_type),
         num_threads);
   }
 
   LinearSolverType linear_solver_type;
   SparseLinearAlgebraLibraryType sparse_linear_algebra_library;
-  OrderingType ordering_type;
+  bool use_automatic_ordering;
   PreconditionerType preconditioner_type;
   int num_threads;
 };
@@ -129,18 +132,14 @@
     options.linear_solver_type = config.linear_solver_type;
     options.sparse_linear_algebra_library =
         config.sparse_linear_algebra_library;
-    options.ordering_type = config.ordering_type;
     options.preconditioner_type = config.preconditioner_type;
     options.num_threads = config.num_threads;
     options.num_linear_solver_threads = config.num_threads;
     options.return_final_residuals = true;
 
-    if (options.ordering_type == SCHUR || options.ordering_type == NATURAL) {
-      options.ordering.clear();
-    }
-
-    if (options.ordering_type == SCHUR) {
-      options.num_eliminate_blocks = 0;
+    if (config.use_automatic_ordering) {
+      delete options.ordering;
+      options.ordering = NULL;
     }
 
     LOG(INFO) << "Running solver configuration: "
@@ -279,21 +278,19 @@
                                  sparse_linear_algebra_library,           \
                                  ordering))
 
-  CONFIGURE(DENSE_QR, SUITE_SPARSE, NATURAL);
-  CONFIGURE(DENSE_NORMAL_CHOLESKY, SUITE_SPARSE, NATURAL);
-  CONFIGURE(DENSE_SCHUR, SUITE_SPARSE, SCHUR);
+  CONFIGURE(DENSE_QR,               SUITE_SPARSE, kAutomaticOrdering);
+  CONFIGURE(DENSE_NORMAL_CHOLESKY,  SUITE_SPARSE, kAutomaticOrdering);
+  CONFIGURE(DENSE_SCHUR,            SUITE_SPARSE, kAutomaticOrdering);
 
 #ifndef CERES_NO_SUITESPARSE
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, NATURAL);
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, SCHUR);
+  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, kAutomaticOrdering);
 #endif  // CERES_NO_SUITESPARSE
 
 #ifndef CERES_NO_CXSPARSE
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, CX_SPARSE, NATURAL);
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, CX_SPARSE, SCHUR);
+  CONFIGURE(SPARSE_NORMAL_CHOLESKY, CX_SPARSE,    kAutomaticOrdering);
 #endif  // CERES_NO_CXSPARSE
 
-  CONFIGURE(ITERATIVE_SCHUR, SUITE_SPARSE, SCHUR);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kAutomaticOrdering);
 
 #undef CONFIGURE
 
@@ -390,16 +387,17 @@
       problem_.AddResidualBlock(cost_function, NULL, camera, point);
     }
 
+    options_.ordering = new Ordering;
+
     // The points come before the cameras.
     for (int i = 0; i < num_points_; ++i) {
-      options_.ordering.push_back(points + 3 * i);
+      options_.ordering->AddParameterBlockToGroup(points + 3 * i, 0);
     }
 
     for (int i = 0; i < num_cameras_; ++i) {
-      options_.ordering.push_back(cameras + 9 * i);
+      options_.ordering->AddParameterBlockToGroup(cameras + 9 * i, 1);
     }
 
-    options_.num_eliminate_blocks = num_points();
     options_.max_num_iterations = 25;
     options_.function_tolerance = 1e-10;
     options_.gradient_tolerance = 1e-10;
@@ -479,46 +477,43 @@
 TEST(SystemTest, BundleAdjustmentProblem) {
   vector<SolverConfig> configs;
 
-#define CONFIGURE(linear_solver, sparse_linear_algebra_library, ordering, preconditioner, threads) \
+#define CONFIGURE(linear_solver, sparse_linear_algebra_library, ordering, preconditioner) \
   configs.push_back(SolverConfig(linear_solver,                         \
                                  sparse_linear_algebra_library,         \
                                  ordering,                              \
-                                 preconditioner,                        \
-                                 threads))
+                                 preconditioner))
 
 #ifndef CERES_NO_SUITESPARSE
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, NATURAL, IDENTITY, 1);
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, USER,    IDENTITY, 1);
-  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, SCHUR,   IDENTITY, 1);
+  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, kAutomaticOrdering, IDENTITY);
+  CONFIGURE(SPARSE_NORMAL_CHOLESKY, SUITE_SPARSE, kUserOrdering,      IDENTITY);
 
-  CONFIGURE(SPARSE_SCHUR,           SUITE_SPARSE, USER,    IDENTITY, 1);
-  CONFIGURE(SPARSE_SCHUR,           SUITE_SPARSE, SCHUR,   IDENTITY, 1);
+  CONFIGURE(SPARSE_SCHUR,           SUITE_SPARSE, kAutomaticOrdering, IDENTITY);
+  CONFIGURE(SPARSE_SCHUR,           SUITE_SPARSE, kUserOrdering,      IDENTITY);
 #endif  // CERES_NO_SUITESPARSE
 
 #ifndef CERES_NO_CXSPARSE
-  CONFIGURE(SPARSE_SCHUR,           CX_SPARSE, USER,       IDENTITY, 1);
-  CONFIGURE(SPARSE_SCHUR,           CX_SPARSE, SCHUR,      IDENTITY, 1);
+  CONFIGURE(SPARSE_SCHUR,           CX_SPARSE,    kAutomaticOrdering, IDENTITY);
+  CONFIGURE(SPARSE_SCHUR,           CX_SPARSE,    kUserOrdering,      IDENTITY);
 #endif  // CERES_NO_CXSPARSE
 
-  CONFIGURE(DENSE_SCHUR,            SUITE_SPARSE, USER,    IDENTITY, 1);
-  CONFIGURE(DENSE_SCHUR,            SUITE_SPARSE, SCHUR,   IDENTITY, 1);
+  CONFIGURE(DENSE_SCHUR,            SUITE_SPARSE, kAutomaticOrdering, IDENTITY);
+  CONFIGURE(DENSE_SCHUR,            SUITE_SPARSE, kUserOrdering,      IDENTITY);
 
-  CONFIGURE(CGNR,                   SUITE_SPARSE, USER,    JACOBI, 1);
-
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, USER,    JACOBI, 1);
+  CONFIGURE(CGNR,                   SUITE_SPARSE, kAutomaticOrdering, JACOBI);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kUserOrdering,      JACOBI);
 
 #ifndef CERES_NO_SUITESPARSE
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, USER,    SCHUR_JACOBI, 1);
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, USER,    CLUSTER_JACOBI, 1);
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, USER,    CLUSTER_TRIDIAGONAL, 1);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kUserOrdering,      SCHUR_JACOBI);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kUserOrdering,      CLUSTER_JACOBI);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kUserOrdering,      CLUSTER_TRIDIAGONAL);
 #endif  // CERES_NO_SUITESPARSE
 
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, SCHUR,   JACOBI, 1);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kAutomaticOrdering, JACOBI);
 
 #ifndef CERES_NO_SUITESPARSE
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, SCHUR,   SCHUR_JACOBI, 1);
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, SCHUR,   CLUSTER_JACOBI, 1);
-  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, SCHUR,   CLUSTER_TRIDIAGONAL, 1);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kAutomaticOrdering, SCHUR_JACOBI);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kAutomaticOrdering, CLUSTER_JACOBI);
+  CONFIGURE(ITERATIVE_SCHUR,        SUITE_SPARSE, kAutomaticOrdering, CLUSTER_TRIDIAGONAL);
 #endif  // CERES_NO_SUITESPARSE
 
 #undef CONFIGURE
diff --git a/internal/ceres/trust_region_minimizer.cc b/internal/ceres/trust_region_minimizer.cc
index 5e9fc04..9f93deb 100644
--- a/internal/ceres/trust_region_minimizer.cc
+++ b/internal/ceres/trust_region_minimizer.cc
@@ -97,22 +97,14 @@
   // moved inside TrustRegionStrategy, its not clear how we dump the
   // regularization vector/matrix anymore.
   //
-  // Doing this right requires either an API change to the
-  // TrustRegionStrategy and/or how LinearLeastSquares problems are
-  // stored on disk.
+  // Also num_eliminate_blocks is not visible to the trust region
+  // minimizer either.
   //
-  // For now, we will just not dump the regularizer.
-  return (!binary_search(options_.lsqp_iterations_to_dump.begin(),
-                         options_.lsqp_iterations_to_dump.end(),
-                         iteration) ||
-          DumpLinearLeastSquaresProblem(options_.lsqp_dump_directory,
-                                        iteration,
-                                        options_.lsqp_dump_format_type,
-                                        jacobian,
-                                        NULL,
-                                        residuals,
-                                        step,
-                                        options_.num_eliminate_blocks));
+  // Both of these indicate that this is the wrong place for this
+  // code, and going forward this should needs fixing/refactoring.
+  LOG(WARNING) << "Dumping linear least squares problem to disk is "
+               << "currently broken.";
+  return true;
 }
 
 void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
diff --git a/internal/ceres/types.cc b/internal/ceres/types.cc
index 74c6af4..225384d 100644
--- a/internal/ceres/types.cc
+++ b/internal/ceres/types.cc
@@ -112,24 +112,6 @@
   return false;
 }
 
-const char* OrderingTypeToString(OrderingType ordering_type) {
-  switch (ordering_type) {
-    CASESTR(NATURAL);
-    CASESTR(USER);
-    CASESTR(SCHUR);
-    default:
-      return "UNKNOWN";
-  }
-}
-
-bool StringToOrderingType(string value, OrderingType* type) {
-  UpperCase(&value);
-  STRENUM(NATURAL);
-  STRENUM(USER);
-  STRENUM(SCHUR);
-  return false;
-}
-
 const char* TrustRegionStrategyTypeToString(
     TrustRegionStrategyType trust_region_strategy_type) {
   switch (trust_region_strategy_type) {