Start of the new ordering API.

Change-Id: I37b0f39011f590d54962ad3e1da1f42712008f82
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index 3e13c86..2bbffcf 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -126,7 +126,6 @@
 
 void SetOrdering(BALProblem* bal_problem, Solver::Options* options) {
   options->use_block_amd = FLAGS_use_block_amd;
-  CHECK(StringToOrderingType(FLAGS_ordering, &options->ordering_type));
 
   // Bundle adjustment problems have a sparsity structure that makes
   // them amenable to more specialized and much more efficient
@@ -142,11 +141,11 @@
   // the right ParameterBlock ordering, or by manually specifying a
   // suitable ordering vector and defining
   // Options::num_eliminate_blocks.
-  if (options->ordering_type == ceres::SCHUR) {
+  options->use_new_ordering_api = true;
+  if (FLAGS_ordering == "schur") {
     return;
   }
 
-  options->ordering_type = ceres::USER;
   const int num_points = bal_problem->num_points();
   const int point_block_size = bal_problem->point_block_size();
   double* points = bal_problem->mutable_points();
@@ -154,24 +153,26 @@
   const int camera_block_size = bal_problem->camera_block_size();
   double* cameras = bal_problem->mutable_cameras();
 
+  ceres::Ordering* ordering = new ceres::Ordering;
+
   // The points come before the cameras.
   for (int i = 0; i < num_points; ++i) {
-    options->ordering.push_back(points + point_block_size * i);
+    ordering->AddParameterBlockToGroup(points + point_block_size * i, 0);
   }
 
   for (int i = 0; i < num_cameras; ++i) {
     // When using axis-angle, there is a single parameter block for
     // the entire camera.
-    options->ordering.push_back(cameras + camera_block_size * i);
-
+    ordering->AddParameterBlockToGroup(cameras + camera_block_size * i, 1);
     // If quaternions are used, there are two blocks, so add the
     // second block to the ordering.
     if (FLAGS_use_quaternions) {
-      options->ordering.push_back(cameras + camera_block_size * i + 4);
+      ordering->AddParameterBlockToGroup(
+          cameras + camera_block_size * i + 4, 1);
     }
   }
 
-  options->num_eliminate_blocks = num_points;
+  options->ordering_new_api = ordering;
 }
 
 void SetMinimizerOptions(Solver::Options* options) {
diff --git a/include/ceres/ceres.h b/include/ceres/ceres.h
index 2db19cb..1ea782a 100644
--- a/include/ceres/ceres.h
+++ b/include/ceres/ceres.h
@@ -43,6 +43,7 @@
 #include "ceres/local_parameterization.h"
 #include "ceres/loss_function.h"
 #include "ceres/numeric_diff_cost_function.h"
+#include "ceres/ordering.h"
 #include "ceres/problem.h"
 #include "ceres/sized_cost_function.h"
 #include "ceres/solver.h"
diff --git a/include/ceres/ordering.h b/include/ceres/ordering.h
new file mode 100644
index 0000000..b3cd1b1
--- /dev/null
+++ b/include/ceres/ordering.h
@@ -0,0 +1,126 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2012 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 <map>
+#include <set>
+#include "ceres/internal/port.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
+// The order in which variables are eliminated in a linear solver can
+// have a significant of impact on the efficiency and accuracy of the
+// method. e.g., when doing sparse Cholesky factorization, there are
+// matrices for which a good ordering will give a Cholesky factor with
+// O(n) storage, where as a bad ordering will result in an completely
+// dense factor.
+//
+// Ceres allows the user to provide varying amounts of hints to the
+// solver about the variable elimination ordering to use. This can
+// range from no hints, where the solver is free to decide the best
+// possible ordering based on the user's choices like the linear
+// solver being used, to an exact order in which the variables should
+// be eliminated, and a variety of possibilities in between. Instances
+// of the Ordering class are used to communicate this infornation to
+// Ceres.
+//
+// Formally an ordering is an ordered partitioning of the parameter
+// blocks, i.e, each parameter block belongs to exactly one group, and
+// each group has a unique integer associated with it, that determines
+// its order in the set of groups.
+//
+// Given such an ordering, Ceres ensures that the parameter blocks in
+// the lowest numbered group are eliminated first, and then the
+// parmeter blocks in the next lowest numbered group and so on. Within
+// each group, Ceres is free to order the parameter blocks as it
+// chooses.
+// e.g. Consider the linear system
+//
+//   x + y = 3
+//   2x + 3y = 7
+//
+// There are two ways in which it can be solved. First eliminating x
+// from the two equations, solving for y and then back substituting
+// for x, or first eliminating y, solving for x and back substituting
+// for y. The user can construct three orderings here.
+//
+//   {0: x}, {1: y} - eliminate x first.
+//   {0: y}, {1: x} - eliminate y first.
+//   {0: x, y}      - Solver gets to decide the elimination order.
+//
+// Thus, to have Ceres determine the ordering automatically using
+// heuristics, put all the variables in group 0 and to control the
+// ordering for every variable, create groups 0..N-1, one per
+// variable, in the desired order.
+//
+// Bundle Adjustment
+// -----------------
+//
+// A particular case of interest is bundle adjustment, where the user
+// has two options. The default is to not specify an ordering at all,
+// the solver will see that the user wants to use a Schur type solver
+// and figure out the right elimination ordering.
+//
+// But if the user already knows what parameter blocks are points and
+// what are cameras, they can save preprocessing time by partitioning
+// the parameter blocks into two groups, one for the points and one
+// for the cameras, where the group containing the points has an id
+// smaller than the group containing cameras.
+class Ordering {
+ public:
+  // Add a parameter block to a group with id group_id. If a group
+  // with this id does not exist, one is created. This method can be
+  // called any number of times for a parameter block.
+  void AddParameterBlockToGroup(double* parameter_block, int group_id);
+
+  // Remove the parameter block from the ordering, no matter what
+  // group it is in. If the parameter block is not known to the
+  // ordering, calling this method will result in a crash.
+  void RemoveParameterBlock(double* parameter_block);
+
+  // Return the group id for the parameter block. If the parameter
+  // block is not known to the Ordering, calling this method results
+  // in a crash.
+  int GroupIdForParameterBlock(double* parameter_block) const;
+  int NumParameterBlocks() const;
+  int NumGroups() const;
+
+  // This function always succeeds. For a group_id unknown to the
+  // ordering is treated as empty groups and the function returns
+  // zero.
+  int GroupSize(int group_id) const;
+  const map<int, set<double*> >& group_id_to_parameter_blocks() const;
+
+ private:
+  map<int, set<double*> > group_id_to_parameter_blocks_;
+  map<double*, int>  parameter_block_to_group_id_;
+};
+
+}  // namespace ceres
diff --git a/include/ceres/solver.h b/include/ceres/solver.h
index 31d5e8d..0e94ee8 100644
--- a/include/ceres/solver.h
+++ b/include/ceres/solver.h
@@ -42,6 +42,7 @@
 
 namespace ceres {
 
+class Ordering;
 class Problem;
 
 // Interface for non-linear least squares solvers.
@@ -89,6 +90,8 @@
 #endif
 
       num_linear_solver_threads = 1;
+      use_new_ordering_api = false;
+      ordering_new_api = NULL;
       num_eliminate_blocks = 0;
       ordering_type = NATURAL;
 
@@ -229,6 +232,18 @@
     // using this setting.
     int num_linear_solver_threads;
 
+    // Whether the old or the new ordering API should be used. If this
+    // is true, all assignments to Solve::Options::ordering_type,
+    // Solver::Options::ordering and
+    // Solver::Options::num_eliminate_blocks are ignored.
+    bool use_new_ordering_api;
+
+    // Ordering object for the new ordering API. If NULL, then all
+    // parameter blocks are assumed to be in the same group and the
+    // solver is free to decide the best ordering. (See ordering.h for
+    // more details).
+    Ordering* ordering_new_api;
+
     // For Schur reduction based methods, the first 0 to num blocks are
     // eliminated using the Schur reduction. For example, when solving
     // traditional structure from motion problems where the parameters are in
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index cd4e6ac..862b7cf 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -63,6 +63,7 @@
     local_parameterization.cc
     loss_function.cc
     normal_prior.cc
+    ordering.cc
     partitioned_matrix_view.cc
     polynomial_solver.cc
     problem.cc
@@ -232,6 +233,7 @@
   CERES_TEST(minimizer)
   CERES_TEST(normal_prior)
   CERES_TEST(numeric_diff_cost_function)
+  CERES_TEST(ordering)
   CERES_TEST(parameter_block)
   CERES_TEST(partitioned_matrix_view)
   CERES_TEST(polynomial_solver)
diff --git a/internal/ceres/ordering.cc b/internal/ceres/ordering.cc
new file mode 100644
index 0000000..8b53ea5
--- /dev/null
+++ b/internal/ceres/ordering.cc
@@ -0,0 +1,89 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2012 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 <map>
+#include <set>
+#include "ceres/internal/port.h"
+#include "ceres/ordering.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
+void Ordering::AddParameterBlockToGroup(double* parameter_block, int group_id) {
+  const map<double*, int>::const_iterator it =
+      parameter_block_to_group_id_.find(parameter_block);
+
+  if (it != parameter_block_to_group_id_.end()) {
+    group_id_to_parameter_blocks_[it->second].erase(parameter_block);
+    if (group_id_to_parameter_blocks_[it->second].size() == 0) {
+      group_id_to_parameter_blocks_.erase(it->second);
+    }
+  }
+
+  parameter_block_to_group_id_[parameter_block] = group_id;
+  group_id_to_parameter_blocks_[group_id].insert(parameter_block);
+}
+
+void Ordering::RemoveParameterBlock(double* parameter_block) {
+  const int current_group_id = GroupIdForParameterBlock(parameter_block);
+  group_id_to_parameter_blocks_[current_group_id].erase(parameter_block);
+  if (group_id_to_parameter_blocks_[current_group_id].size() == 0) {
+    group_id_to_parameter_blocks_.erase(current_group_id);
+  }
+
+  parameter_block_to_group_id_.erase(parameter_block);
+}
+
+int Ordering::GroupIdForParameterBlock(double* parameter_block) const {
+  const map<double*, int>::const_iterator it =
+      parameter_block_to_group_id_.find(parameter_block);
+  CHECK(it !=  parameter_block_to_group_id_.end())
+      << "Tried finding the group id of a parameter block unknown to "
+      << "the ordering: " << parameter_block;
+  return it->second;
+}
+
+int Ordering::NumParameterBlocks() const {
+  return parameter_block_to_group_id_.size();
+}
+
+int Ordering::NumGroups() const { return group_id_to_parameter_blocks_.size(); }
+
+int Ordering::GroupSize(int group_id) const {
+  map<int, set<double*> >::const_iterator it =
+      group_id_to_parameter_blocks_.find(group_id);
+  return (it ==  group_id_to_parameter_blocks_.end()) ? 0 : it->second.size();
+}
+
+const map<int, set<double*> >& Ordering::group_id_to_parameter_blocks() const {
+  return group_id_to_parameter_blocks_;
+}
+
+}  // namespace ceres
diff --git a/internal/ceres/ordering_test.cc b/internal/ceres/ordering_test.cc
new file mode 100644
index 0000000..9bd43b3
--- /dev/null
+++ b/internal/ceres/ordering_test.cc
@@ -0,0 +1,140 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2012 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/ordering.h"
+
+#include <cstddef>
+#include <vector>
+#include "gtest/gtest.h"
+#include "ceres/collections_port.h"
+
+namespace ceres {
+namespace internal {
+
+TEST(Ordering, EmptyOrderingBehavesCorrectly) {
+  Ordering ordering;
+  EXPECT_EQ(ordering.NumGroups(), 0);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 0);
+  EXPECT_EQ(ordering.GroupSize(1), 0);
+  double x;
+  EXPECT_DEATH_IF_SUPPORTED(ordering.GroupIdForParameterBlock(&x),
+                            "Tried finding");
+  EXPECT_DEATH_IF_SUPPORTED(ordering.RemoveParameterBlock(&x),
+                            "Tried finding");
+}
+
+TEST(Ordering, EverythingInOneGroup) {
+  Ordering ordering;
+  double x[3];
+  ordering.AddParameterBlockToGroup(x, 1);
+  ordering.AddParameterBlockToGroup(x + 1, 1);
+  ordering.AddParameterBlockToGroup(x + 2, 1);
+  ordering.AddParameterBlockToGroup(x, 1);
+
+  EXPECT_EQ(ordering.NumGroups(), 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 3);
+  EXPECT_EQ(ordering.GroupSize(1), 3);
+  EXPECT_EQ(ordering.GroupSize(0), 0);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 1), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 2), 1);
+
+  ordering.RemoveParameterBlock(x);
+  EXPECT_EQ(ordering.NumGroups(), 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 2);
+  EXPECT_EQ(ordering.GroupSize(1), 2);
+  EXPECT_EQ(ordering.GroupSize(0), 0);
+
+  EXPECT_DEATH_IF_SUPPORTED(ordering.GroupIdForParameterBlock(x),
+                            "Tried finding");
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 1), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 2), 1);
+}
+
+TEST(Ordering, StartInOneGroupAndThenSplit) {
+  Ordering ordering;
+  double x[3];
+  ordering.AddParameterBlockToGroup(x, 1);
+  ordering.AddParameterBlockToGroup(x + 1, 1);
+  ordering.AddParameterBlockToGroup(x + 2, 1);
+  ordering.AddParameterBlockToGroup(x, 1);
+
+  EXPECT_EQ(ordering.NumGroups(), 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 3);
+  EXPECT_EQ(ordering.GroupSize(1), 3);
+  EXPECT_EQ(ordering.GroupSize(0), 0);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 1), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 2), 1);
+
+  ordering.AddParameterBlockToGroup(x, 5);
+  EXPECT_EQ(ordering.NumGroups(), 2);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 3);
+  EXPECT_EQ(ordering.GroupSize(1), 2);
+  EXPECT_EQ(ordering.GroupSize(5), 1);
+  EXPECT_EQ(ordering.GroupSize(0), 0);
+
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x), 5);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 1), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 2), 1);
+}
+
+TEST(Ordering, AddAndRemoveEveryThingFromOneGroup) {
+  Ordering ordering;
+  double x[3];
+  ordering.AddParameterBlockToGroup(x, 1);
+  ordering.AddParameterBlockToGroup(x + 1, 1);
+  ordering.AddParameterBlockToGroup(x + 2, 1);
+  ordering.AddParameterBlockToGroup(x, 1);
+
+  EXPECT_EQ(ordering.NumGroups(), 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 3);
+  EXPECT_EQ(ordering.GroupSize(1), 3);
+  EXPECT_EQ(ordering.GroupSize(0), 0);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 1), 1);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 2), 1);
+
+  ordering.AddParameterBlockToGroup(x, 5);
+  ordering.AddParameterBlockToGroup(x + 1, 5);
+  ordering.AddParameterBlockToGroup(x + 2, 5);
+  EXPECT_EQ(ordering.NumGroups(), 1);
+  EXPECT_EQ(ordering.NumParameterBlocks(), 3);
+  EXPECT_EQ(ordering.GroupSize(1), 0);
+  EXPECT_EQ(ordering.GroupSize(5), 3);
+  EXPECT_EQ(ordering.GroupSize(0), 0);
+
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x), 5);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 1), 5);
+  EXPECT_EQ(ordering.GroupIdForParameterBlock(x + 2), 5);
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 0ece8ee..8f2f38b 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -40,6 +40,7 @@
 #include "ceres/linear_solver.h"
 #include "ceres/map_util.h"
 #include "ceres/minimizer.h"
+#include "ceres/ordering.h"
 #include "ceres/parameter_block.h"
 #include "ceres/problem.h"
 #include "ceres/problem_impl.h"
@@ -201,6 +202,51 @@
                        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.
diff --git a/jni/Android.mk b/jni/Android.mk
index eaccb29..98e16f5 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -123,6 +123,7 @@
                    $(CERES_SRC_PATH)/local_parameterization.cc \
                    $(CERES_SRC_PATH)/loss_function.cc \
                    $(CERES_SRC_PATH)/normal_prior.cc \
+                   $(CERES_SRC_PATH)/ordering.cc \
                    $(CERES_SRC_PATH)/partitioned_matrix_view.cc \
                    $(CERES_SRC_PATH)/polynomial_solver.cc \
                    $(CERES_SRC_PATH)/problem.cc \