Add dynamic_sparsity option.

The standard sparse normal Cholesky solver assumes a fixed
sparsity pattern which is useful for a large number of problems
presented to Ceres. However, some problems are symbolically dense
but numerically sparse i.e. each residual is a function of a
large number of parameters but at any given state the residual
only depends on a sparse subset of them. For these class of
problems it is faster to re-analyse the sparsity pattern of the
jacobian at each iteration of the non-linear optimisation instead
of including all of the zero entries in the step computation.

The proposed solution adds the dynamic_sparsity option which can
be used with SPARSE_NORMAL_CHOLESKY. A
DynamicCompressedRowSparseMatrix type (which extends
CompressedRowSparseMatrix) has been introduced which allows
dynamic addition and removal of elements. A Finalize method is
provided which then consolidates the matrix so that it can be
used in place of a regular CompressedRowSparseMatrix. An
associated jacobian writer has also been provided.

Changes that were required to make this extension were adding the
SetMaxNumNonZeros method to CompressedRowSparseMatrix and adding
a JacobianFinalizer template parameter to the ProgramEvaluator.

Change-Id: Ia5a8a9523fdae8d5b027bc35e70b4611ec2a8d01
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index f388146..60e464d 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -49,6 +49,9 @@
   TARGET_LINK_LIBRARIES(curve_fitting_c m)
 ENDIF (NOT MSVC)
 
+ADD_EXECUTABLE(ellipse_approximation ellipse_approximation.cc)
+TARGET_LINK_LIBRARIES(ellipse_approximation ceres)
+
 ADD_EXECUTABLE(robust_curve_fitting robust_curve_fitting.cc)
 TARGET_LINK_LIBRARIES(robust_curve_fitting ceres)
 
diff --git a/examples/ellipse_approximation.cc b/examples/ellipse_approximation.cc
new file mode 100644
index 0000000..a5bbe02
--- /dev/null
+++ b/examples/ellipse_approximation.cc
@@ -0,0 +1,451 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+//
+// This fits points randomly distributed on an ellipse with an approximate
+// line segment contour. This is done by jointly optimizing the control points
+// of the line segment contour along with the preimage positions for the data
+// points. The purpose of this example is to show an example use case for
+// dynamic_sparsity, and how it can benefit problems which are numerically
+// dense but dynamically sparse.
+
+#include <cmath>
+#include <vector>
+#include "ceres/ceres.h"
+#include "glog/logging.h"
+
+// Data generated with the following Python code.
+//   import numpy as np
+//   np.random.seed(1337)
+//   t = np.linspace(0.0, 2.0 * np.pi, 212, endpoint=False)
+//   t += 2.0 * np.pi * 0.01 * np.random.randn(t.size)
+//   theta = np.deg2rad(15)
+//   a, b = np.cos(theta), np.sin(theta)
+//   R = np.array([[a, -b],
+//                 [b, a]])
+//   Y = np.dot(np.c_[4.0 * np.cos(t), np.sin(t)], R.T)
+
+const int kYRows = 212;
+const int kYCols = 2;
+const double kYData[kYRows * kYCols] = {
+  +3.871364e+00, +9.916027e-01,
+  +3.864003e+00, +1.034148e+00,
+  +3.850651e+00, +1.072202e+00,
+  +3.868350e+00, +1.014408e+00,
+  +3.796381e+00, +1.153021e+00,
+  +3.857138e+00, +1.056102e+00,
+  +3.787532e+00, +1.162215e+00,
+  +3.704477e+00, +1.227272e+00,
+  +3.564711e+00, +1.294959e+00,
+  +3.754363e+00, +1.191948e+00,
+  +3.482098e+00, +1.322725e+00,
+  +3.602777e+00, +1.279658e+00,
+  +3.585433e+00, +1.286858e+00,
+  +3.347505e+00, +1.356415e+00,
+  +3.220855e+00, +1.378914e+00,
+  +3.558808e+00, +1.297174e+00,
+  +3.403618e+00, +1.343809e+00,
+  +3.179828e+00, +1.384721e+00,
+  +3.054789e+00, +1.398759e+00,
+  +3.294153e+00, +1.366808e+00,
+  +3.247312e+00, +1.374813e+00,
+  +2.988547e+00, +1.404247e+00,
+  +3.114508e+00, +1.392698e+00,
+  +2.899226e+00, +1.409802e+00,
+  +2.533256e+00, +1.414778e+00,
+  +2.654773e+00, +1.415909e+00,
+  +2.565100e+00, +1.415313e+00,
+  +2.976456e+00, +1.405118e+00,
+  +2.484200e+00, +1.413640e+00,
+  +2.324751e+00, +1.407476e+00,
+  +1.930468e+00, +1.378221e+00,
+  +2.329017e+00, +1.407688e+00,
+  +1.760640e+00, +1.360319e+00,
+  +2.147375e+00, +1.396603e+00,
+  +1.741989e+00, +1.358178e+00,
+  +1.743859e+00, +1.358394e+00,
+  +1.557372e+00, +1.335208e+00,
+  +1.280551e+00, +1.295087e+00,
+  +1.429880e+00, +1.317546e+00,
+  +1.213485e+00, +1.284400e+00,
+  +9.168172e-01, +1.232870e+00,
+  +1.311141e+00, +1.299839e+00,
+  +1.231969e+00, +1.287382e+00,
+  +7.453773e-01, +1.200049e+00,
+  +6.151587e-01, +1.173683e+00,
+  +5.935666e-01, +1.169193e+00,
+  +2.538707e-01, +1.094227e+00,
+  +6.806136e-01, +1.187089e+00,
+  +2.805447e-01, +1.100405e+00,
+  +6.184807e-01, +1.174371e+00,
+  +1.170550e-01, +1.061762e+00,
+  +2.890507e-01, +1.102365e+00,
+  +3.834234e-01, +1.123772e+00,
+  +3.980161e-04, +1.033061e+00,
+  -3.651680e-01, +9.370367e-01,
+  -8.386351e-01, +7.987201e-01,
+  -8.105704e-01, +8.073702e-01,
+  -8.735139e-01, +7.878886e-01,
+  -9.913836e-01, +7.506100e-01,
+  -8.784011e-01, +7.863636e-01,
+  -1.181440e+00, +6.882566e-01,
+  -1.229556e+00, +6.720191e-01,
+  -1.035839e+00, +7.362765e-01,
+  -8.031520e-01, +8.096470e-01,
+  -1.539136e+00, +5.629549e-01,
+  -1.755423e+00, +4.817306e-01,
+  -1.337589e+00, +6.348763e-01,
+  -1.836966e+00, +4.499485e-01,
+  -1.913367e+00, +4.195617e-01,
+  -2.126467e+00, +3.314900e-01,
+  -1.927625e+00, +4.138238e-01,
+  -2.339862e+00, +2.379074e-01,
+  -1.881736e+00, +4.322152e-01,
+  -2.116753e+00, +3.356163e-01,
+  -2.255733e+00, +2.754930e-01,
+  -2.555834e+00, +1.368473e-01,
+  -2.770277e+00, +2.895711e-02,
+  -2.563376e+00, +1.331890e-01,
+  -2.826715e+00, -9.000818e-04,
+  -2.978191e+00, -8.457804e-02,
+  -3.115855e+00, -1.658786e-01,
+  -2.982049e+00, -8.678322e-02,
+  -3.307892e+00, -2.902083e-01,
+  -3.038346e+00, -1.194222e-01,
+  -3.190057e+00, -2.122060e-01,
+  -3.279086e+00, -2.705777e-01,
+  -3.322028e+00, -2.999889e-01,
+  -3.122576e+00, -1.699965e-01,
+  -3.551973e+00, -4.768674e-01,
+  -3.581866e+00, -5.032175e-01,
+  -3.497799e+00, -4.315203e-01,
+  -3.565384e+00, -4.885602e-01,
+  -3.699493e+00, -6.199815e-01,
+  -3.585166e+00, -5.061925e-01,
+  -3.758914e+00, -6.918275e-01,
+  -3.741104e+00, -6.689131e-01,
+  -3.688331e+00, -6.077239e-01,
+  -3.810425e+00, -7.689015e-01,
+  -3.791829e+00, -7.386911e-01,
+  -3.789951e+00, -7.358189e-01,
+  -3.823100e+00, -7.918398e-01,
+  -3.857021e+00, -8.727074e-01,
+  -3.858250e+00, -8.767645e-01,
+  -3.872100e+00, -9.563174e-01,
+  -3.864397e+00, -1.032630e+00,
+  -3.846230e+00, -1.081669e+00,
+  -3.834799e+00, -1.102536e+00,
+  -3.866684e+00, -1.022901e+00,
+  -3.808643e+00, -1.139084e+00,
+  -3.868840e+00, -1.011569e+00,
+  -3.791071e+00, -1.158615e+00,
+  -3.797999e+00, -1.151267e+00,
+  -3.696278e+00, -1.232314e+00,
+  -3.779007e+00, -1.170504e+00,
+  -3.622855e+00, -1.270793e+00,
+  -3.647249e+00, -1.259166e+00,
+  -3.655412e+00, -1.255042e+00,
+  -3.573218e+00, -1.291696e+00,
+  -3.638019e+00, -1.263684e+00,
+  -3.498409e+00, -1.317750e+00,
+  -3.304143e+00, -1.364970e+00,
+  -3.183001e+00, -1.384295e+00,
+  -3.202456e+00, -1.381599e+00,
+  -3.244063e+00, -1.375332e+00,
+  -3.233308e+00, -1.377019e+00,
+  -3.060112e+00, -1.398264e+00,
+  -3.078187e+00, -1.396517e+00,
+  -2.689594e+00, -1.415761e+00,
+  -2.947662e+00, -1.407039e+00,
+  -2.854490e+00, -1.411860e+00,
+  -2.660499e+00, -1.415900e+00,
+  -2.875955e+00, -1.410930e+00,
+  -2.675385e+00, -1.415848e+00,
+  -2.813155e+00, -1.413363e+00,
+  -2.417673e+00, -1.411512e+00,
+  -2.725461e+00, -1.415373e+00,
+  -2.148334e+00, -1.396672e+00,
+  -2.108972e+00, -1.393738e+00,
+  -2.029905e+00, -1.387302e+00,
+  -2.046214e+00, -1.388687e+00,
+  -2.057402e+00, -1.389621e+00,
+  -1.650250e+00, -1.347160e+00,
+  -1.806764e+00, -1.365469e+00,
+  -1.206973e+00, -1.283343e+00,
+  -8.029259e-01, -1.211308e+00,
+  -1.229551e+00, -1.286993e+00,
+  -1.101507e+00, -1.265754e+00,
+  -9.110645e-01, -1.231804e+00,
+  -1.110046e+00, -1.267211e+00,
+  -8.465274e-01, -1.219677e+00,
+  -7.594163e-01, -1.202818e+00,
+  -8.023823e-01, -1.211203e+00,
+  -3.732519e-01, -1.121494e+00,
+  -1.918373e-01, -1.079668e+00,
+  -4.671988e-01, -1.142253e+00,
+  -4.033645e-01, -1.128215e+00,
+  -1.920740e-01, -1.079724e+00,
+  -3.022157e-01, -1.105389e+00,
+  -1.652831e-01, -1.073354e+00,
+  +4.671625e-01, -9.085886e-01,
+  +5.940178e-01, -8.721832e-01,
+  +3.147557e-01, -9.508290e-01,
+  +6.383631e-01, -8.591867e-01,
+  +9.888923e-01, -7.514088e-01,
+  +7.076339e-01, -8.386023e-01,
+  +1.326682e+00, -6.386698e-01,
+  +1.149834e+00, -6.988221e-01,
+  +1.257742e+00, -6.624207e-01,
+  +1.492352e+00, -5.799632e-01,
+  +1.595574e+00, -5.421766e-01,
+  +1.240173e+00, -6.684113e-01,
+  +1.706612e+00, -5.004442e-01,
+  +1.873984e+00, -4.353002e-01,
+  +1.985633e+00, -3.902561e-01,
+  +1.722880e+00, -4.942329e-01,
+  +2.095182e+00, -3.447402e-01,
+  +2.018118e+00, -3.768991e-01,
+  +2.422702e+00, -1.999563e-01,
+  +2.370611e+00, -2.239326e-01,
+  +2.152154e+00, -3.205250e-01,
+  +2.525121e+00, -1.516499e-01,
+  +2.422116e+00, -2.002280e-01,
+  +2.842806e+00, +9.536372e-03,
+  +3.030128e+00, +1.146027e-01,
+  +2.888424e+00, +3.433444e-02,
+  +2.991609e+00, +9.226409e-02,
+  +2.924807e+00, +5.445844e-02,
+  +3.007772e+00, +1.015875e-01,
+  +2.781973e+00, -2.282382e-02,
+  +3.164737e+00, +1.961781e-01,
+  +3.237671e+00, +2.430139e-01,
+  +3.046123e+00, +1.240014e-01,
+  +3.414834e+00, +3.669060e-01,
+  +3.436591e+00, +3.833600e-01,
+  +3.626207e+00, +5.444311e-01,
+  +3.223325e+00, +2.336361e-01,
+  +3.511963e+00, +4.431060e-01,
+  +3.698380e+00, +6.187442e-01,
+  +3.670244e+00, +5.884943e-01,
+  +3.558833e+00, +4.828230e-01,
+  +3.661807e+00, +5.797689e-01,
+  +3.767261e+00, +7.030893e-01,
+  +3.801065e+00, +7.532650e-01,
+  +3.828523e+00, +8.024454e-01,
+  +3.840719e+00, +8.287032e-01,
+  +3.848748e+00, +8.485921e-01,
+  +3.865801e+00, +9.066551e-01,
+  +3.870983e+00, +9.404873e-01,
+  +3.870263e+00, +1.001884e+00,
+  +3.864462e+00, +1.032374e+00,
+  +3.870542e+00, +9.996121e-01,
+  +3.865424e+00, +1.028474e+00
+};
+ceres::ConstMatrixRef kY(kYData, kYRows, kYCols);
+
+class PointToLineSegmentContourCostFunction : public ceres::CostFunction {
+ public:
+  PointToLineSegmentContourCostFunction(const int num_segments,
+                                        const Eigen::Vector2d y)
+      : num_segments_(num_segments), y_(y) {
+    // The first parameter is the preimage position.
+    mutable_parameter_block_sizes()->push_back(1);
+    // The next parameters are the control points for the line segment contour.
+    for (int i = 0; i < num_segments_; ++i) {
+      mutable_parameter_block_sizes()->push_back(2);
+    }
+    set_num_residuals(2);
+  }
+
+  virtual bool Evaluate(const double* const* x,
+                        double* residuals,
+                        double** jacobians) const {
+    // Convert the preimage position `t` into a segment index `i0` and the
+    // line segment interpolation parameter `u`. `i1` is the index of the next
+    // control point.
+    const double t = ModuloNumSegments(*x[0]);
+    CHECK_GE(t, 0.0);
+    CHECK_LT(t, num_segments_);
+    const int i0 = floor(t), i1 = (i0 + 1) % num_segments_;
+    const double u = t - i0;
+
+    // Linearly interpolate between control points `i0` and `i1`.
+    residuals[0] = y_[0] - ((1.0 - u) * x[1 + i0][0] + u * x[1 + i1][0]);
+    residuals[1] = y_[1] - ((1.0 - u) * x[1 + i0][1] + u * x[1 + i1][1]);
+
+    if (jacobians == NULL) {
+      return true;
+    }
+
+    if (jacobians[0] != NULL) {
+      jacobians[0][0] = x[1 + i0][0] - x[1 + i1][0];
+      jacobians[0][1] = x[1 + i0][1] - x[1 + i1][1];
+    }
+    for (int i = 0; i < num_segments_; ++i) {
+      if (jacobians[i + 1] != NULL) {
+        ceres::MatrixRef(jacobians[i + 1], 2, 2).setZero();
+        if (i == i0) {
+          jacobians[i + 1][0] = -(1.0 - u);
+          jacobians[i + 1][3] = -(1.0 - u);
+        } else if (i == i1) {
+          jacobians[i + 1][0] = -u;
+          jacobians[i + 1][3] = -u;
+        }
+      }
+    }
+    return true;
+  }
+
+  static ceres::CostFunction* Create(const int num_segments,
+                                     const Eigen::Vector2d y) {
+    return new PointToLineSegmentContourCostFunction(num_segments, y);
+  }
+
+ private:
+  inline double ModuloNumSegments(const double& t) const {
+    return t - num_segments_ * floor(t / num_segments_);
+  }
+
+  const int num_segments_;
+  const Eigen::Vector2d y_;
+};
+
+struct EuclideanDistanceFunctor {
+  EuclideanDistanceFunctor(const double& sqrt_weight)
+      : sqrt_weight_(sqrt_weight) {}
+
+  template <typename T>
+  bool operator()(const T* x0, const T* x1, T* residuals) const {
+    residuals[0] = T(sqrt_weight_) * (x0[0] - x1[0]);
+    residuals[1] = T(sqrt_weight_) * (x0[1] - x1[1]);
+    return true;
+  }
+
+  static ceres::CostFunction* Create(const double& sqrt_weight) {
+    return new ceres::AutoDiffCostFunction<EuclideanDistanceFunctor, 2, 2, 2>(
+        new EuclideanDistanceFunctor(sqrt_weight));
+  }
+
+ private:
+  const double sqrt_weight_;
+};
+
+bool SolveWithFullReport(ceres::Solver::Options options,
+                         ceres::Problem* problem,
+                         bool dynamic_sparsity) {
+  options.dynamic_sparsity = dynamic_sparsity;
+
+  ceres::Solver::Summary summary;
+  ceres::Solve(options, problem, &summary);
+
+  std::cout << "####################" << std::endl;
+  std::cout << "dynamic_sparsity = " << dynamic_sparsity << std::endl;
+  std::cout << "####################" << std::endl;
+  std::cout << summary.FullReport() << std::endl;
+
+  return summary.termination_type == ceres::CONVERGENCE;
+}
+
+int main(int argc, char** argv) {
+  google::InitGoogleLogging(argv[0]);
+
+  // Problem configuration.
+  const int num_segments = 151;
+  const double regularization_weight = 1e-2;
+
+  // Eigen::MatrixXd is column major so we define our own MatrixXd which is
+  // row major. Eigen::VectorXd can be used directly.
+  typedef Eigen::Matrix<double,
+                        Eigen::Dynamic, Eigen::Dynamic,
+                        Eigen::RowMajor> MatrixXd;
+  using Eigen::VectorXd;
+
+  // `X` is the matrix of control points which make up the contour of line
+  // segments. The number of control points is equal to the number of line
+  // segments because the contour is closed.
+  //
+  // Initialize `X` to points on the unit circle.
+  VectorXd w(num_segments + 1);
+  w.setLinSpaced(num_segments + 1, 0.0, 2.0 * M_PI);
+  w.conservativeResize(num_segments);
+  MatrixXd X(num_segments, 2);
+  X.col(0) = w.array().cos();
+  X.col(1) = w.array().sin();
+
+  // Each data point has an associated preimage position on the line segment
+  // contour. For each data point we initialize the preimage positions to
+  // the index of the closest control point.
+  const int num_observations = kY.rows();
+  VectorXd t(num_observations);
+  for (int i = 0; i < num_observations; ++i) {
+    (X.rowwise() - kY.row(i)).rowwise().squaredNorm().minCoeff(&t[i]);
+  }
+
+  ceres::Problem problem;
+
+  // For each data point add a residual which measures its distance to its
+  // corresponding position on the line segment contour.
+  std::vector<double*> parameter_blocks(1 + num_segments);
+  parameter_blocks[0] = NULL;
+  for (int i = 0; i < num_segments; ++i) {
+    parameter_blocks[i + 1] = X.data() + 2 * i;
+  }
+  for (int i = 0; i < num_observations; ++i) {
+    parameter_blocks[0] = &t[i];
+    problem.AddResidualBlock(
+      PointToLineSegmentContourCostFunction::Create(num_segments, kY.row(i)),
+      NULL,
+      parameter_blocks);
+  }
+
+  // Add regularization to minimize the length of the line segment contour.
+  for (int i = 0; i < num_segments; ++i) {
+    problem.AddResidualBlock(
+      EuclideanDistanceFunctor::Create(sqrt(regularization_weight)),
+      NULL,
+      X.data() + 2 * i,
+      X.data() + 2 * ((i + 1) % num_segments));
+  }
+
+  ceres::Solver::Options options;
+  options.max_num_iterations = 100;
+  options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
+
+  // First, solve `X` and `t` jointly with dynamic_sparsity = true.
+  MatrixXd X0 = X;
+  VectorXd t0 = t;
+  CHECK(SolveWithFullReport(options, &problem, true));
+
+  // Second, solve with dynamic_sparsity = false.
+  X = X0;
+  t = t0;
+  CHECK(SolveWithFullReport(options, &problem, false));
+
+  return 0;
+}
diff --git a/include/ceres/solver.h b/include/ceres/solver.h
index c5bfadc..3d63951 100644
--- a/include/ceres/solver.h
+++ b/include/ceres/solver.h
@@ -108,6 +108,7 @@
 
       num_linear_solver_threads = 1;
       use_postordering = false;
+      dynamic_sparsity = false;
       min_linear_solver_iterations = 1;
       max_linear_solver_iterations = 500;
       eta = 1e-1;
@@ -500,6 +501,21 @@
     // matrix. Setting use_postordering to true enables this tradeoff.
     bool use_postordering;
 
+    // Some non-linear least squares problems are symbolically dense but
+    // numerically sparse. i.e. at any given state only a small number
+    // of jacobian entries are non-zero, but the position and number of
+    // non-zeros is different depending on the state. For these problems
+    // it can be useful to factorize the sparse jacobian at each solver
+    // iteration instead of including all of the zero entries in a single
+    // general factorization.
+    //
+    // If your problem does not have this property (or you do not know),
+    // then it is probably best to keep this false, otherwise it will
+    // likely lead to worse performance.
+
+    // This settings affects the SPARSE_NORMAL_CHOLESKY solver.
+    bool dynamic_sparsity;
+
     // Some non-linear least squares problems have additional
     // structure in the way the parameter blocks interact that it is
     // beneficial to modify the way the trust region step is computed.
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 87faf2b..a9ec1c2 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -59,6 +59,8 @@
     dense_sparse_matrix.cc
     detect_structure.cc
     dogleg_strategy.cc
+    dynamic_compressed_row_jacobian_writer.cc
+    dynamic_compressed_row_sparse_matrix.cc
     evaluator.cc
     file.cc
     gradient_checking_cost_function.cc
@@ -220,6 +222,7 @@
   CERES_TEST(covariance)
   CERES_TEST(dense_sparse_matrix)
   CERES_TEST(dynamic_autodiff_cost_function)
+  CERES_TEST(dynamic_compressed_row_sparse_matrix)
   CERES_TEST(dynamic_numeric_diff_cost_function)
   CERES_TEST(evaluator)
   CERES_TEST(gradient_checker)
diff --git a/internal/ceres/compressed_row_jacobian_writer.cc b/internal/ceres/compressed_row_jacobian_writer.cc
index bbadb77..ed8db14 100644
--- a/internal/ceres/compressed_row_jacobian_writer.cc
+++ b/internal/ceres/compressed_row_jacobian_writer.cc
@@ -40,6 +40,44 @@
 namespace ceres {
 namespace internal {
 
+void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors(
+    const Program* program, CompressedRowSparseMatrix* jacobian) {
+  const vector<ParameterBlock*>& parameter_blocks =
+      program->parameter_blocks();
+  vector<int>& col_blocks = *(jacobian->mutable_col_blocks());
+  col_blocks.resize(parameter_blocks.size());
+  for (int i = 0; i < parameter_blocks.size(); ++i) {
+    col_blocks[i] = parameter_blocks[i]->LocalSize();
+  }
+
+  const vector<ResidualBlock*>& residual_blocks =
+      program->residual_blocks();
+  vector<int>& row_blocks = *(jacobian->mutable_row_blocks());
+  row_blocks.resize(residual_blocks.size());
+  for (int i = 0; i < residual_blocks.size(); ++i) {
+    row_blocks[i] = residual_blocks[i]->NumResiduals();
+  }
+}
+
+void CompressedRowJacobianWriter::GetOrderedParameterBlocks(
+      const Program* program,
+      int residual_id,
+      vector<pair<int, int> >* evaluated_jacobian_blocks) {
+  const ResidualBlock* residual_block =
+      program->residual_blocks()[residual_id];
+  const int num_parameter_blocks = residual_block->NumParameterBlocks();
+
+  for (int j = 0; j < num_parameter_blocks; ++j) {
+    const ParameterBlock* parameter_block =
+        residual_block->parameter_blocks()[j];
+    if (!parameter_block->IsConstant()) {
+      evaluated_jacobian_blocks->push_back(
+          make_pair(parameter_block->index(), j));
+    }
+  }
+  sort(evaluated_jacobian_blocks->begin(), evaluated_jacobian_blocks->end());
+}
+
 SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const {
   const vector<ResidualBlock*>& residual_blocks =
       program_->residual_blocks();
@@ -71,7 +109,7 @@
           total_num_effective_parameters,
           num_jacobian_nonzeros + total_num_effective_parameters);
 
-  // At this stage, the CompressedSparseMatrix is an invalid state. But this
+  // At this stage, the CompressedRowSparseMatrix is an invalid state. But this
   // seems to be the only way to construct it without doing a memory copy.
   int* rows = jacobian->mutable_rows();
   int* cols = jacobian->mutable_cols();
@@ -132,22 +170,7 @@
   }
   CHECK_EQ(num_jacobian_nonzeros, rows[total_num_residuals]);
 
-  // Populate the row and column block vectors for use by block
-  // oriented ordering algorithms. This is useful when
-  // Solver::Options::use_block_amd = true.
-  const vector<ParameterBlock*>& parameter_blocks =
-      program_->parameter_blocks();
-  vector<int>& col_blocks = *(jacobian->mutable_col_blocks());
-  col_blocks.resize(parameter_blocks.size());
-  for (int i = 0; i <  parameter_blocks.size(); ++i) {
-    col_blocks[i] = parameter_blocks[i]->LocalSize();
-  }
-
-  vector<int>& row_blocks = *(jacobian->mutable_row_blocks());
-  row_blocks.resize(residual_blocks.size());
-  for (int i = 0; i <  residual_blocks.size(); ++i) {
-    row_blocks[i] = residual_blocks[i]->NumResiduals();
-  }
+  PopulateJacobianRowAndColumnBlockVectors(program_, jacobian);
 
   return jacobian;
 }
@@ -164,25 +187,10 @@
 
   const ResidualBlock* residual_block =
       program_->residual_blocks()[residual_id];
-  const int num_parameter_blocks = residual_block->NumParameterBlocks();
   const int num_residuals = residual_block->NumResiduals();
 
-  // It is necessary to determine the order of the jacobian blocks before
-  // copying them into the CompressedRowSparseMatrix. Just because a cost
-  // function uses parameter blocks 1 after 2 in its arguments does not mean
-  // that the block 1 occurs before block 2 in the column layout of the
-  // jacobian. Thus, determine the order by sorting the jacobian blocks by their
-  // position in the state vector.
   vector<pair<int, int> > evaluated_jacobian_blocks;
-  for (int j = 0; j < num_parameter_blocks; ++j) {
-    const ParameterBlock* parameter_block =
-        residual_block->parameter_blocks()[j];
-    if (!parameter_block->IsConstant()) {
-      evaluated_jacobian_blocks.push_back(
-          make_pair(parameter_block->index(), j));
-    }
-  }
-  sort(evaluated_jacobian_blocks.begin(), evaluated_jacobian_blocks.end());
+  GetOrderedParameterBlocks(program_, residual_id, &evaluated_jacobian_blocks);
 
   // Where in the current row does the jacobian for a parameter block begin.
   int col_pos = 0;
diff --git a/internal/ceres/compressed_row_jacobian_writer.h b/internal/ceres/compressed_row_jacobian_writer.h
index c103165..935e650 100644
--- a/internal/ceres/compressed_row_jacobian_writer.h
+++ b/internal/ceres/compressed_row_jacobian_writer.h
@@ -39,6 +39,7 @@
 namespace ceres {
 namespace internal {
 
+class CompressedRowSparseMatrix;
 class Program;
 class SparseMatrix;
 
@@ -49,6 +50,36 @@
     : program_(program) {
   }
 
+  // `PopulateJacobianRowAndColumnBlockVectors` sets `col_blocks` and
+  // `row_blocks` for a `CompressedRowSparseMatrix`, based on the parameter
+  // block sizes and residual sizes respectively from the program. This is
+  // useful when Solver::Options::use_block_amd = true;
+  //
+  // This function is static so that it is available to other jacobian
+  // writers which use `CompressedRowSparseMatrix` (or derived types).
+  // (Jacobian writers do not fall under any type hierarchy; they only
+  // have to provide an interface as specified in program_evaluator.h).
+  static void PopulateJacobianRowAndColumnBlockVectors(
+      const Program* program,
+      CompressedRowSparseMatrix* jacobian);
+
+  // It is necessary to determine the order of the jacobian blocks before
+  // copying them into a `CompressedRowSparseMatrix` (or derived type).
+  // Just because a cost function uses parameter blocks 1 after 2 in its
+  // arguments does not mean that the block 1 occurs before block 2 in the
+  // column layout of the jacobian. Thus, `GetOrderedParameterBlocks`
+  // determines the order by sorting the jacobian blocks by their position in
+  // the state vector.
+  //
+  // This function is static so that it is available to other jacobian
+  // writers which use `CompressedRowSparseMatrix` (or derived types).
+  // (Jacobian writers do not fall under any type hierarchy; they only
+  // have to provide an interface as specified in program_evaluator.h).
+  static void GetOrderedParameterBlocks(
+      const Program* program,
+      int residual_id,
+      vector<pair<int, int> >* evaluated_jacobian_blocks);
+
   // JacobianWriter interface.
 
   // Since the compressed row matrix has different layout than that assumed by
diff --git a/internal/ceres/compressed_row_sparse_matrix.cc b/internal/ceres/compressed_row_sparse_matrix.cc
index bef98d6..7993ed6 100644
--- a/internal/ceres/compressed_row_sparse_matrix.cc
+++ b/internal/ceres/compressed_row_sparse_matrix.cc
@@ -286,6 +286,13 @@
   matrix->values.resize(matrix->rows[matrix->num_rows]);
 }
 
+void CompressedRowSparseMatrix::SetMaxNumNonZeros(int num_nonzeros) {
+  CHECK_GE(num_nonzeros, 0);
+
+  cols_.resize(num_nonzeros);
+  values_.resize(num_nonzeros);
+}
+
 void CompressedRowSparseMatrix::SolveLowerTriangularInPlace(
     double* solution) const {
   for (int r = 0; r < num_rows_; ++r) {
diff --git a/internal/ceres/compressed_row_sparse_matrix.h b/internal/ceres/compressed_row_sparse_matrix.h
index 06b8689..a0ba7ee 100644
--- a/internal/ceres/compressed_row_sparse_matrix.h
+++ b/internal/ceres/compressed_row_sparse_matrix.h
@@ -115,6 +115,9 @@
   const vector<int>& col_blocks() const { return col_blocks_; }
   vector<int>* mutable_col_blocks() { return &col_blocks_; }
 
+  // Destructive array resizing method.
+  void SetMaxNumNonZeros(int num_nonzeros);
+
   // Non-destructive array resizing method.
   void set_num_rows(const int num_rows) { num_rows_ = num_rows; }
   void set_num_cols(const int num_cols) { num_cols_ = num_cols; }
diff --git a/internal/ceres/dynamic_compressed_row_finalizer.h b/internal/ceres/dynamic_compressed_row_finalizer.h
new file mode 100644
index 0000000..5e6b0d8
--- /dev/null
+++ b/internal/ceres/dynamic_compressed_row_finalizer.h
@@ -0,0 +1,51 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+
+#ifndef CERES_INTERNAL_DYNAMIC_COMPRESED_ROW_FINALIZER_H_
+#define CERES_INTERNAL_DYNAMIC_COMPRESED_ROW_FINALIZER_H_
+
+#include "ceres/casts.h"
+#include "ceres/dynamic_compressed_row_sparse_matrix.h"
+
+namespace ceres {
+namespace internal {
+
+struct DynamicCompressedRowJacobianFinalizer {
+  void operator()(SparseMatrix* base_jacobian, int num_parameters) {
+    DynamicCompressedRowSparseMatrix* jacobian =
+      down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian);
+    jacobian->Finalize(num_parameters);
+  }
+};
+
+}  // namespace internal
+}  // namespace ceres
+
+#endif // CERES_INTERNAL_DYNAMIC_COMPRESED_ROW_FINALISER_H_
diff --git a/internal/ceres/dynamic_compressed_row_jacobian_writer.cc b/internal/ceres/dynamic_compressed_row_jacobian_writer.cc
new file mode 100644
index 0000000..2f01617
--- /dev/null
+++ b/internal/ceres/dynamic_compressed_row_jacobian_writer.cc
@@ -0,0 +1,107 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+
+#include "ceres/compressed_row_jacobian_writer.h"
+#include "ceres/dynamic_compressed_row_jacobian_writer.h"
+#include "ceres/casts.h"
+#include "ceres/dynamic_compressed_row_sparse_matrix.h"
+#include "ceres/parameter_block.h"
+#include "ceres/program.h"
+#include "ceres/residual_block.h"
+
+namespace ceres {
+namespace internal {
+
+ScratchEvaluatePreparer*
+DynamicCompressedRowJacobianWriter::CreateEvaluatePreparers(int num_threads) {
+  return ScratchEvaluatePreparer::Create(*program_, num_threads);
+}
+
+SparseMatrix* DynamicCompressedRowJacobianWriter::CreateJacobian() const {
+  // Initialize `jacobian` with zero number of `max_num_nonzeros`.
+  const int num_residuals = program_->NumResiduals();
+  const int num_effective_parameters = program_->NumEffectiveParameters();
+
+  DynamicCompressedRowSparseMatrix* jacobian =
+      new DynamicCompressedRowSparseMatrix(num_residuals,
+                                           num_effective_parameters,
+                                           0);
+
+  CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors(
+      program_, jacobian);
+
+  return jacobian;
+}
+
+void DynamicCompressedRowJacobianWriter::Write(int residual_id,
+                                               int residual_offset,
+                                               double **jacobians,
+                                               SparseMatrix* base_jacobian) {
+  DynamicCompressedRowSparseMatrix* jacobian =
+    down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian);
+
+  // Get the `residual_block` of interest.
+  const ResidualBlock* residual_block =
+      program_->residual_blocks()[residual_id];
+  const int num_residuals = residual_block->NumResiduals();
+
+  vector<pair<int, int> > evaluated_jacobian_blocks;
+  CompressedRowJacobianWriter::GetOrderedParameterBlocks(
+    program_, residual_id, &evaluated_jacobian_blocks);
+
+  // `residual_offset` is the residual row in the global jacobian.
+  // Empty the jacobian rows.
+  jacobian->ClearRows(residual_offset, num_residuals);
+
+  // Iterate over each parameter block.
+  for (int i = 0; i < evaluated_jacobian_blocks.size(); ++i) {
+    const ParameterBlock* parameter_block =
+        program_->parameter_blocks()[evaluated_jacobian_blocks[i].first];
+    const int parameter_block_jacobian_index =
+        evaluated_jacobian_blocks[i].second;
+    const int parameter_block_size = parameter_block->LocalSize();
+
+    // For each parameter block only insert its non-zero entries.
+    for (int r = 0; r < num_residuals; ++r) {
+      for (int c = 0; c < parameter_block_size; ++c) {
+        const double& v = jacobians[parameter_block_jacobian_index][
+            r * parameter_block_size + c];
+        // Only insert non-zero entries.
+        if (v != 0.0) {
+          jacobian->InsertEntry(
+            residual_offset + r, parameter_block->delta_offset() + c, v);
+        }
+      }
+    }
+  }
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/dynamic_compressed_row_jacobian_writer.h b/internal/ceres/dynamic_compressed_row_jacobian_writer.h
new file mode 100644
index 0000000..df9581b
--- /dev/null
+++ b/internal/ceres/dynamic_compressed_row_jacobian_writer.h
@@ -0,0 +1,83 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+//
+// A jacobian writer that directly writes to dynamic compressed row sparse
+// matrices.
+
+#ifndef CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_
+#define CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_
+
+#include "ceres/evaluator.h"
+#include "ceres/scratch_evaluate_preparer.h"
+
+namespace ceres {
+namespace internal {
+
+class Program;
+class SparseMatrix;
+
+class DynamicCompressedRowJacobianWriter {
+ public:
+  DynamicCompressedRowJacobianWriter(Evaluator::Options /* ignored */,
+                                     Program* program)
+    : program_(program) {
+  }
+
+  // JacobianWriter interface.
+
+  // The compressed row matrix has different layout than that assumed by
+  // the cost functions. The scratch space is therefore used to store
+  // the jacobians (including zeros) temporarily before only the non-zero
+  // entries are copied over to the larger jacobian in `Write`.
+  ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads);
+
+  // Return a `DynamicCompressedRowSparseMatrix` which is filled by
+  // `Write`. Note that `Finalize` must be called to make the
+  // `CompressedRowSparseMatrix` interface valid.
+  SparseMatrix* CreateJacobian() const;
+
+  // Write only the non-zero jacobian entries for a residual block
+  // (specified by `residual_id`) into `base_jacobian`, starting at the row
+  // specifed by `residual_offset`.
+  //
+  // This method is thread-safe over residual blocks (each `residual_id`).
+  void Write(int residual_id,
+             int residual_offset,
+             double **jacobians,
+             SparseMatrix* base_jacobian);
+
+ private:
+  Program* program_;
+};
+
+}  // namespace internal
+}  // namespace ceres
+
+#endif // CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_
diff --git a/internal/ceres/dynamic_compressed_row_sparse_matrix.cc b/internal/ceres/dynamic_compressed_row_sparse_matrix.cc
new file mode 100644
index 0000000..f285d52
--- /dev/null
+++ b/internal/ceres/dynamic_compressed_row_sparse_matrix.cc
@@ -0,0 +1,107 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+
+#include <cstring>
+#include "ceres/dynamic_compressed_row_sparse_matrix.h"
+
+namespace ceres {
+namespace internal {
+
+DynamicCompressedRowSparseMatrix::DynamicCompressedRowSparseMatrix(
+  int num_rows,
+  int num_cols,
+  int initial_max_num_nonzeros)
+    : CompressedRowSparseMatrix(num_rows,
+                                num_cols,
+                                initial_max_num_nonzeros) {
+    dynamic_cols_.resize(num_rows);
+    dynamic_values_.resize(num_rows);
+  }
+
+void DynamicCompressedRowSparseMatrix::InsertEntry(int row,
+                                                   int col,
+                                                   const double& value) {
+  CHECK_GE(row, 0);
+  CHECK_LT(row, num_rows());
+  CHECK_GE(col, 0);
+  CHECK_LT(col, num_cols());
+  dynamic_cols_[row].push_back(col);
+  dynamic_values_[row].push_back(value);
+}
+
+void DynamicCompressedRowSparseMatrix::ClearRows(int row_start,
+                                                 int num_rows) {
+  for (int r = 0; r < num_rows; ++r) {
+    const int i = row_start + r;
+    CHECK_GE(i, 0);
+    CHECK_LT(i, this->num_rows());
+    dynamic_cols_[i].resize(0);
+    dynamic_values_[i].resize(0);
+  }
+}
+
+void DynamicCompressedRowSparseMatrix::Finalize(int num_additional_elements) {
+  // `num_additional_elements` is provided as an argument so that additional
+  // storage can be reserved when it is known by the finalizer.
+  CHECK_GE(num_additional_elements, 0);
+
+  // Count the number of non-zeros and resize `cols_` and `values_`.
+  int num_jacobian_nonzeros = 0;
+  for (int i = 0; i < dynamic_cols_.size(); ++i) {
+    num_jacobian_nonzeros += dynamic_cols_[i].size();
+  }
+
+  SetMaxNumNonZeros(num_jacobian_nonzeros + num_additional_elements);
+
+  // Flatten `dynamic_cols_` into `cols_` and `dynamic_values_`
+  // into `values_`.
+  int index_into_values_and_cols = 0;
+  for (int i = 0; i < num_rows(); ++i) {
+    mutable_rows()[i] = index_into_values_and_cols;
+    const int num_nonzero_columns = dynamic_cols_[i].size();
+    if (num_nonzero_columns > 0) {
+      memcpy(mutable_cols() + index_into_values_and_cols,
+             &dynamic_cols_[i][0],
+             dynamic_cols_[i].size() * sizeof(dynamic_cols_[0][0]));
+      memcpy(mutable_values() + index_into_values_and_cols,
+             &dynamic_values_[i][0],
+             dynamic_values_[i].size() * sizeof(dynamic_values_[0][0]));
+      index_into_values_and_cols += dynamic_cols_[i].size();
+    }
+  }
+  mutable_rows()[num_rows()] = index_into_values_and_cols;
+
+  CHECK_EQ(index_into_values_and_cols, num_jacobian_nonzeros)
+    << "Ceres bug: final index into values_ and cols_ should be equal to "
+    << "the number of jacobian nonzeros. Please contact the developers!";
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/dynamic_compressed_row_sparse_matrix.h b/internal/ceres/dynamic_compressed_row_sparse_matrix.h
new file mode 100644
index 0000000..7a89a70
--- /dev/null
+++ b/internal/ceres/dynamic_compressed_row_sparse_matrix.h
@@ -0,0 +1,99 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+//
+// A compressed row sparse matrix that provides an extended interface to
+// allow dynamic insertion of entries. This is provided for the use case
+// where the sparsity structure and number of non-zero entries is dynamic.
+// This flexibility is achieved by using an (internal) scratch space that
+// allows independent insertion of entries into each row (thread-safe).
+// Once insertion is complete, the `Finalize` method must be called to ensure
+// that the underlying `CompressedRowSparseMatrix` is consistent.
+//
+// This should only be used if you really do need a dynamic sparsity pattern.
+
+#ifndef CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_SPARSE_MATRIX_H_
+#define CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_SPARSE_MATRIX_H_
+
+#include "ceres/compressed_row_sparse_matrix.h"
+
+namespace ceres {
+namespace internal {
+
+class DynamicCompressedRowSparseMatrix : public CompressedRowSparseMatrix {
+ public:
+  // Set the number of rows and columns for the underlyig
+  // `CompressedRowSparseMatrix` and set the initial number of maximum non-zero
+  // entries. Note that following the insertion of entries, when `Finalize`
+  // is called the number of non-zeros is determined and all internal
+  // structures are adjusted as required. If you know the upper limit on the
+  // number of non-zeros, then passing this value here can prevent future
+  // memory reallocations which may improve performance. Otherwise, if no
+  // upper limit is available a value of 0 is sufficient.
+  //
+  // Typical usage of this class is to define a new instance with a given
+  // number of rows, columns and maximum number of non-zero elements
+  // (if available). Next, entries are inserted at row and column positions
+  // using `InsertEntry`. Finally, once all elements have been inserted,
+  // `Finalize` must be called to make the underlying
+  // `CompressedRowSparseMatrix` consistent.
+  DynamicCompressedRowSparseMatrix(int num_rows,
+                                   int num_cols,
+                                   int initial_max_num_nonzeros);
+
+  // Insert an entry at a given row and column position. This method is
+  // thread-safe across rows i.e. different threads can insert values
+  // simultaneously into different rows. It should be emphasised that this
+  // method always inserts a new entry and does not check for existing
+  // entries at the specified row and column position. Duplicate entries
+  // for a given row and column position will result in undefined
+  // behavior.
+  void InsertEntry(int row, int col, const double& value);
+
+  // Clear all entries for rows, starting from row index `row_start`
+  // and proceeding for `num_rows`.
+  void ClearRows(int row_start, int num_rows);
+
+  // Make the underlying internal `CompressedRowSparseMatrix` data structures
+  // consistent. Additional space for non-zero entries in the
+  // `CompressedRowSparseMatrix` can be reserved by specifying
+  // `num_additional_elements`. This is useful when it is known that rows will
+  // be appended to the `CompressedRowSparseMatrix` (e.g. appending a diagonal
+  // matrix to the jacobian) as it prevents need for future reallocation.
+  void Finalize(int num_additional_elements);
+
+ private:
+  vector<vector<int> > dynamic_cols_;
+  vector<vector<double> > dynamic_values_;
+};
+
+}  // namespace internal
+}  // namespace ceres
+
+#endif // CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_SPARSE_MATRIX_H_
diff --git a/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc b/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc
new file mode 100644
index 0000000..03bfcb6
--- /dev/null
+++ b/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc
@@ -0,0 +1,217 @@
+// 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: richie.stebbing@gmail.com (Richard Stebbing)
+
+#include "ceres/dynamic_compressed_row_sparse_matrix.h"
+
+#include "ceres/casts.h"
+#include "ceres/compressed_row_sparse_matrix.h"
+#include "ceres/casts.h"
+#include "ceres/internal/eigen.h"
+#include "ceres/internal/scoped_ptr.h"
+#include "ceres/linear_least_squares_problems.h"
+#include "ceres/triplet_sparse_matrix.h"
+#include "gtest/gtest.h"
+
+namespace ceres {
+namespace internal {
+
+class DynamicCompressedRowSparseMatrixTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    num_rows = 7;
+    num_cols = 4;
+
+    // The number of additional elements reserved when `Finalize` is called
+    // should have no effect on the number of rows, columns or nonzeros.
+    // Set this to some nonzero value to be sure.
+    num_additional_elements = 13;
+
+    expected_num_nonzeros = num_rows * num_cols - min(num_rows, num_cols);
+
+    InitialiseDenseReference();
+    InitialiseSparseMatrixReferences();
+
+    dcrsm.reset(new DynamicCompressedRowSparseMatrix(num_rows,
+                                                     num_cols,
+                                                     0));
+  }
+
+  void Finalize() {
+    dcrsm->Finalize(num_additional_elements);
+  }
+
+  void InitialiseDenseReference() {
+    dense.resize(num_rows, num_cols);
+    dense.setZero();
+    int num_nonzeros = 0;
+    for (int i = 0; i < (num_rows * num_cols); ++i) {
+      const int r = i / num_cols, c = i % num_cols;
+      if (r != c) {
+        dense(r, c) = i + 1;
+        ++num_nonzeros;
+      }
+    }
+    ASSERT_EQ(num_nonzeros, expected_num_nonzeros);
+  }
+
+  void InitialiseSparseMatrixReferences() {
+    std::vector<int> rows, cols;
+    std::vector<double> values;
+    for (int i = 0; i < (num_rows * num_cols); ++i) {
+      const int r = i / num_cols, c = i % num_cols;
+      if (r != c) {
+        rows.push_back(r);
+        cols.push_back(c);
+        values.push_back(i + 1);
+      }
+    }
+    ASSERT_EQ(values.size(), expected_num_nonzeros);
+
+    tsm.reset(new TripletSparseMatrix(num_rows,
+                                      num_cols,
+                                      expected_num_nonzeros));
+    std::copy(rows.begin(), rows.end(), tsm->mutable_rows());
+    std::copy(cols.begin(), cols.end(), tsm->mutable_cols());
+    std::copy(values.begin(), values.end(), tsm->mutable_values());
+    tsm->set_num_nonzeros(values.size());
+
+    Matrix dense_from_tsm;
+    tsm->ToDenseMatrix(&dense_from_tsm);
+    ASSERT_TRUE((dense.array() == dense_from_tsm.array()).all());
+
+    crsm.reset(new CompressedRowSparseMatrix(*tsm));
+    Matrix dense_from_crsm;
+    crsm->ToDenseMatrix(&dense_from_crsm);
+    ASSERT_TRUE((dense.array() == dense_from_crsm.array()).all());
+  }
+
+  void InsertNonZeroEntriesFromDenseReference() {
+    for (int r = 0; r < num_rows; ++r) {
+      for (int c = 0; c < num_cols; ++c) {
+        const double& v = dense(r, c);
+        if (v != 0.0) {
+          dcrsm->InsertEntry(r, c, v);
+        }
+      }
+    }
+  }
+
+  void ExpectEmpty() {
+    EXPECT_EQ(dcrsm->num_rows(), num_rows);
+    EXPECT_EQ(dcrsm->num_cols(), num_cols);
+    EXPECT_EQ(dcrsm->num_nonzeros(), 0);
+
+    Matrix dense_from_dcrsm;
+    dcrsm->ToDenseMatrix(&dense_from_dcrsm);
+    EXPECT_EQ(dense_from_dcrsm.rows(), num_rows);
+    EXPECT_EQ(dense_from_dcrsm.cols(), num_cols);
+    EXPECT_TRUE((dense_from_dcrsm.array() == 0.0).all());
+  }
+
+  void ExpectEqualToDenseReference() {
+    Matrix dense_from_dcrsm;
+    dcrsm->ToDenseMatrix(&dense_from_dcrsm);
+    EXPECT_TRUE((dense.array() == dense_from_dcrsm.array()).all());
+  }
+
+  void ExpectEqualToCompressedRowSparseMatrixReference() {
+    typedef Eigen::Map<const Eigen::VectorXi> ConstIntVectorRef;
+
+    ConstIntVectorRef crsm_rows(crsm->rows(), crsm->num_rows() + 1);
+    ConstIntVectorRef dcrsm_rows(dcrsm->rows(), dcrsm->num_rows() + 1);
+    EXPECT_TRUE((crsm_rows.array() == dcrsm_rows.array()).all());
+
+    ConstIntVectorRef crsm_cols(crsm->cols(), crsm->num_nonzeros());
+    ConstIntVectorRef dcrsm_cols(dcrsm->cols(), dcrsm->num_nonzeros());
+    EXPECT_TRUE((crsm_cols.array() == dcrsm_cols.array()).all());
+
+    ConstVectorRef crsm_values(crsm->values(), crsm->num_nonzeros());
+    ConstVectorRef dcrsm_values(dcrsm->values(), dcrsm->num_nonzeros());
+    EXPECT_TRUE((crsm_values.array() == dcrsm_values.array()).all());
+  }
+
+  int num_rows;
+  int num_cols;
+
+  int num_additional_elements;
+
+  int expected_num_nonzeros;
+
+  Matrix dense;
+  scoped_ptr<TripletSparseMatrix> tsm;
+  scoped_ptr<CompressedRowSparseMatrix> crsm;
+
+  scoped_ptr<DynamicCompressedRowSparseMatrix> dcrsm;
+};
+
+TEST_F(DynamicCompressedRowSparseMatrixTest, Initialization) {
+  ExpectEmpty();
+
+  Finalize();
+  ExpectEmpty();
+}
+
+TEST_F(DynamicCompressedRowSparseMatrixTest, InsertEntryAndFinalize) {
+  InsertNonZeroEntriesFromDenseReference();
+  ExpectEmpty();
+
+  Finalize();
+  ExpectEqualToDenseReference();
+  ExpectEqualToCompressedRowSparseMatrixReference();
+}
+
+TEST_F(DynamicCompressedRowSparseMatrixTest, ClearRows) {
+  InsertNonZeroEntriesFromDenseReference();
+  Finalize();
+  ExpectEqualToDenseReference();
+  ExpectEqualToCompressedRowSparseMatrixReference();
+
+  dcrsm->ClearRows(0, 0);
+  Finalize();
+  ExpectEqualToDenseReference();
+  ExpectEqualToCompressedRowSparseMatrixReference();
+
+  dcrsm->ClearRows(0, num_rows);
+  ExpectEqualToCompressedRowSparseMatrixReference();
+
+  Finalize();
+  ExpectEmpty();
+
+  InsertNonZeroEntriesFromDenseReference();
+  dcrsm->ClearRows(1, 2);
+  Finalize();
+  dense.block(1, 0, 2, num_cols).setZero();
+  ExpectEqualToDenseReference();
+
+  InitialiseDenseReference();
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/evaluator.cc b/internal/ceres/evaluator.cc
index 31a4176..c94c62c 100644
--- a/internal/ceres/evaluator.cc
+++ b/internal/ceres/evaluator.cc
@@ -35,6 +35,8 @@
 #include "ceres/compressed_row_sparse_matrix.h"
 #include "ceres/crs_matrix.h"
 #include "ceres/dense_jacobian_writer.h"
+#include "ceres/dynamic_compressed_row_finalizer.h"
+#include "ceres/dynamic_compressed_row_jacobian_writer.h"
 #include "ceres/evaluator.h"
 #include "ceres/internal/port.h"
 #include "ceres/program_evaluator.h"
@@ -63,9 +65,17 @@
                                   BlockJacobianWriter>(options,
                                                        program);
     case SPARSE_NORMAL_CHOLESKY:
-      return new ProgramEvaluator<ScratchEvaluatePreparer,
-                                  CompressedRowJacobianWriter>(options,
-                                                               program);
+      if (options.dynamic_sparsity) {
+        return new ProgramEvaluator<ScratchEvaluatePreparer,
+                                    DynamicCompressedRowJacobianWriter,
+                                    DynamicCompressedRowJacobianFinalizer>(
+                                        options, program);
+      } else {
+        return new ProgramEvaluator<ScratchEvaluatePreparer,
+                                    CompressedRowJacobianWriter>(options,
+                                                                 program);
+      }
+
     default:
       *error = "Invalid Linear Solver Type. Unable to create evaluator.";
       return NULL;
diff --git a/internal/ceres/evaluator.h b/internal/ceres/evaluator.h
index 3d25462..8fc60b8 100644
--- a/internal/ceres/evaluator.h
+++ b/internal/ceres/evaluator.h
@@ -61,11 +61,13 @@
     Options()
         : num_threads(1),
           num_eliminate_blocks(-1),
-          linear_solver_type(DENSE_QR) {}
+          linear_solver_type(DENSE_QR),
+          dynamic_sparsity(false) {}
 
     int num_threads;
     int num_eliminate_blocks;
     LinearSolverType linear_solver_type;
+    bool dynamic_sparsity;
   };
 
   static Evaluator* Create(const Options& options,
diff --git a/internal/ceres/evaluator_test.cc b/internal/ceres/evaluator_test.cc
index ea24504..c0de3fc 100644
--- a/internal/ceres/evaluator_test.cc
+++ b/internal/ceres/evaluator_test.cc
@@ -44,6 +44,7 @@
 #include "ceres/program.h"
 #include "ceres/sized_cost_function.h"
 #include "ceres/sparse_matrix.h"
+#include "ceres/stringprintf.h"
 #include "ceres/types.h"
 #include "gtest/gtest.h"
 
@@ -91,18 +92,42 @@
   }
 };
 
+struct EvaluatorTestOptions {
+  EvaluatorTestOptions(LinearSolverType linear_solver_type,
+                       int num_eliminate_blocks,
+                       bool dynamic_sparsity = false)
+    : linear_solver_type(linear_solver_type),
+      num_eliminate_blocks(num_eliminate_blocks),
+      dynamic_sparsity(dynamic_sparsity) {}
+
+  LinearSolverType linear_solver_type;
+  int num_eliminate_blocks;
+  bool dynamic_sparsity;
+};
+
 struct EvaluatorTest
-    : public ::testing::TestWithParam<pair<LinearSolverType, int> > {
+    : public ::testing::TestWithParam<EvaluatorTestOptions> {
   Evaluator* CreateEvaluator(Program* program) {
     // This program is straight from the ProblemImpl, and so has no index/offset
     // yet; compute it here as required by the evalutor implementations.
     program->SetParameterOffsetsAndIndex();
 
-    VLOG(1) << "Creating evaluator with type: " << GetParam().first
-            << " and num_eliminate_blocks: " << GetParam().second;
+    if (VLOG_IS_ON(1)) {
+      string report;
+      StringAppendF(&report, "Creating evaluator with type: %d",
+                    GetParam().linear_solver_type);
+      if (GetParam().linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
+        StringAppendF(&report, ", dynamic_sparsity: %d",
+                      GetParam().dynamic_sparsity);
+      }
+      StringAppendF(&report, " and num_eliminate_blocks: %d",
+                    GetParam().num_eliminate_blocks);
+      VLOG(1) << report;
+    }
     Evaluator::Options options;
-    options.linear_solver_type = GetParam().first;
-    options.num_eliminate_blocks = GetParam().second;
+    options.linear_solver_type = GetParam().linear_solver_type;
+    options.num_eliminate_blocks = GetParam().num_eliminate_blocks;
+    options.dynamic_sparsity = GetParam().dynamic_sparsity;
     string error;
     return Evaluator::Create(options, program, &error);
   }
@@ -517,23 +542,25 @@
 INSTANTIATE_TEST_CASE_P(
     LinearSolvers,
     EvaluatorTest,
-    ::testing::Values(make_pair(DENSE_QR, 0),
-                      make_pair(DENSE_SCHUR, 0),
-                      make_pair(DENSE_SCHUR, 1),
-                      make_pair(DENSE_SCHUR, 2),
-                      make_pair(DENSE_SCHUR, 3),
-                      make_pair(DENSE_SCHUR, 4),
-                      make_pair(SPARSE_SCHUR, 0),
-                      make_pair(SPARSE_SCHUR, 1),
-                      make_pair(SPARSE_SCHUR, 2),
-                      make_pair(SPARSE_SCHUR, 3),
-                      make_pair(SPARSE_SCHUR, 4),
-                      make_pair(ITERATIVE_SCHUR, 0),
-                      make_pair(ITERATIVE_SCHUR, 1),
-                      make_pair(ITERATIVE_SCHUR, 2),
-                      make_pair(ITERATIVE_SCHUR, 3),
-                      make_pair(ITERATIVE_SCHUR, 4),
-                      make_pair(SPARSE_NORMAL_CHOLESKY, 0)));
+    ::testing::Values(
+      EvaluatorTestOptions(DENSE_QR, 0),
+      EvaluatorTestOptions(DENSE_SCHUR, 0),
+      EvaluatorTestOptions(DENSE_SCHUR, 1),
+      EvaluatorTestOptions(DENSE_SCHUR, 2),
+      EvaluatorTestOptions(DENSE_SCHUR, 3),
+      EvaluatorTestOptions(DENSE_SCHUR, 4),
+      EvaluatorTestOptions(SPARSE_SCHUR, 0),
+      EvaluatorTestOptions(SPARSE_SCHUR, 1),
+      EvaluatorTestOptions(SPARSE_SCHUR, 2),
+      EvaluatorTestOptions(SPARSE_SCHUR, 3),
+      EvaluatorTestOptions(SPARSE_SCHUR, 4),
+      EvaluatorTestOptions(ITERATIVE_SCHUR, 0),
+      EvaluatorTestOptions(ITERATIVE_SCHUR, 1),
+      EvaluatorTestOptions(ITERATIVE_SCHUR, 2),
+      EvaluatorTestOptions(ITERATIVE_SCHUR, 3),
+      EvaluatorTestOptions(ITERATIVE_SCHUR, 4),
+      EvaluatorTestOptions(SPARSE_NORMAL_CHOLESKY, 0, false),
+      EvaluatorTestOptions(SPARSE_NORMAL_CHOLESKY, 0, true)));
 
 // Simple cost function used to check if the evaluator is sensitive to
 // state changes.
diff --git a/internal/ceres/linear_solver.h b/internal/ceres/linear_solver.h
index 8737c7c..f091bc5 100644
--- a/internal/ceres/linear_solver.h
+++ b/internal/ceres/linear_solver.h
@@ -98,6 +98,7 @@
           dense_linear_algebra_library_type(EIGEN),
           sparse_linear_algebra_library_type(SUITE_SPARSE),
           use_postordering(false),
+          dynamic_sparsity(false),
           min_num_iterations(1),
           max_num_iterations(1),
           num_threads(1),
@@ -115,6 +116,7 @@
 
     // See solver.h for information about this flag.
     bool use_postordering;
+    bool dynamic_sparsity;
 
     // Number of internal iterations that the solver uses. This
     // parameter only makes sense for iterative solvers like CG.
diff --git a/internal/ceres/program_evaluator.h b/internal/ceres/program_evaluator.h
index 8aa2a39..a06dc8c 100644
--- a/internal/ceres/program_evaluator.h
+++ b/internal/ceres/program_evaluator.h
@@ -97,7 +97,13 @@
 namespace ceres {
 namespace internal {
 
-template<typename EvaluatePreparer, typename JacobianWriter>
+struct NullJacobianFinalizer {
+  void operator()(SparseMatrix* jacobian, int num_parameters) {}
+};
+
+template<typename EvaluatePreparer,
+         typename JacobianWriter,
+         typename JacobianFinalizer = NullJacobianFinalizer>
 class ProgramEvaluator : public Evaluator {
  public:
   ProgramEvaluator(const Evaluator::Options &options, Program* program)
@@ -244,9 +250,10 @@
     }
 
     if (!abort) {
+      const int num_parameters = program_->NumEffectiveParameters();
+
       // Sum the cost and gradient (if requested) from each thread.
       (*cost) = 0.0;
-      int num_parameters = program_->NumEffectiveParameters();
       if (gradient != NULL) {
         VectorRef(gradient, num_parameters).setZero();
       }
@@ -257,6 +264,15 @@
               VectorRef(evaluate_scratch_[i].gradient.get(), num_parameters);
         }
       }
+
+      // Finalize the Jacobian if it is available.
+      // `num_parameters` is passed to the finalizer so that additional
+      // storage can be reserved for additional diagonal elements if
+      // necessary.
+      if (jacobian != NULL) {
+        JacobianFinalizer f;
+        f(jacobian, num_parameters);
+      }
     }
     return !abort;
   }
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index e85ffbf..6ae2c90 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -1183,7 +1183,8 @@
     return transformed_program.release();
   }
 
-  if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
+  if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
+      !options->dynamic_sparsity) {
     if (!ReorderProgramForSparseNormalCholesky(
             options->sparse_linear_algebra_library_type,
             linear_solver_ordering,
@@ -1305,6 +1306,7 @@
   linear_solver_options.dense_linear_algebra_library_type =
       options->dense_linear_algebra_library_type;
   linear_solver_options.use_postordering = options->use_postordering;
+  linear_solver_options.dynamic_sparsity = options->dynamic_sparsity;
 
   // Ignore user's postordering preferences and force it to be true if
   // cholmod_camd is not available. This ensures that the linear
@@ -1457,6 +1459,7 @@
          ->second.size())
       : 0;
   evaluator_options.num_threads = options.num_threads;
+  evaluator_options.dynamic_sparsity = options.dynamic_sparsity;
   return Evaluator::Create(evaluator_options, program, error);
 }
 
diff --git a/internal/ceres/sparse_normal_cholesky_solver.cc b/internal/ceres/sparse_normal_cholesky_solver.cc
index 880adf7..07537e3 100644
--- a/internal/ceres/sparse_normal_cholesky_solver.cc
+++ b/internal/ceres/sparse_normal_cholesky_solver.cc
@@ -56,13 +56,13 @@
       options_(options) {
 }
 
-SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {
+void SparseNormalCholeskySolver::FreeFactorization() {
 #ifndef CERES_NO_SUITESPARSE
   if (factor_ != NULL) {
     ss_.Free(factor_);
     factor_ = NULL;
   }
-#endif
+#endif  // CERES_NO_SUITESPARSE
 
 #ifndef CERES_NO_CXSPARSE
   if (cxsparse_factor_ != NULL) {
@@ -72,6 +72,10 @@
 #endif  // CERES_NO_CXSPARSE
 }
 
+SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {
+  FreeFactorization();
+}
+
 LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl(
     CompressedRowSparseMatrix* A,
     const double* b,
@@ -150,13 +154,20 @@
   event_logger.AddEvent("Setup");
 
   // Compute symbolic factorization if not available.
+  if (options_.dynamic_sparsity) {
+    FreeFactorization();
+  }
   if (cxsparse_factor_ == NULL) {
     if (options_.use_postordering) {
       cxsparse_factor_ = cxsparse_.BlockAnalyzeCholesky(AtA,
                                                         A->col_blocks(),
                                                         A->col_blocks());
     } else {
-      cxsparse_factor_ = cxsparse_.AnalyzeCholeskyWithNaturalOrdering(AtA);
+      if (options_.dynamic_sparsity) {
+        cxsparse_factor_ = cxsparse_.AnalyzeCholesky(AtA);
+      } else {
+        cxsparse_factor_ = cxsparse_.AnalyzeCholeskyWithNaturalOrdering(AtA);
+      }
     }
   }
   event_logger.AddEvent("Analysis");
@@ -169,6 +180,7 @@
     summary.termination_type = LINEAR_SOLVER_FAILURE;
   }
   event_logger.AddEvent("Solve");
+
   return summary;
 }
 #else
@@ -198,6 +210,9 @@
   cholmod_sparse lhs = ss_.CreateSparseMatrixTransposeView(A);
   event_logger.AddEvent("Setup");
 
+  if (options_.dynamic_sparsity) {
+    FreeFactorization();
+  }
   if (factor_ == NULL) {
     if (options_.use_postordering) {
       factor_ = ss_.BlockAnalyzeCholesky(&lhs,
@@ -205,7 +220,11 @@
                                          A->row_blocks(),
                                          &summary.message);
     } else {
-      factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&lhs, &summary.message);
+      if (options_.dynamic_sparsity) {
+        factor_ = ss_.AnalyzeCholesky(&lhs, &summary.message);
+      } else {
+        factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&lhs, &summary.message);
+      }
     }
   }
   event_logger.AddEvent("Analysis");
diff --git a/internal/ceres/sparse_normal_cholesky_solver.h b/internal/ceres/sparse_normal_cholesky_solver.h
index adca08a..ea91534 100644
--- a/internal/ceres/sparse_normal_cholesky_solver.h
+++ b/internal/ceres/sparse_normal_cholesky_solver.h
@@ -71,6 +71,8 @@
       const LinearSolver::PerSolveOptions& options,
       double* rhs_and_solution);
 
+  void FreeFactorization();
+
   SuiteSparse ss_;
   // Cached factorization
   cholmod_factor* factor_;
diff --git a/internal/ceres/unsymmetric_linear_solver_test.cc b/internal/ceres/unsymmetric_linear_solver_test.cc
index 624db68..412e611 100644
--- a/internal/ceres/unsymmetric_linear_solver_test.cc
+++ b/internal/ceres/unsymmetric_linear_solver_test.cc
@@ -172,6 +172,15 @@
   options.use_postordering = true;
   TestSolver(options);
 }
+
+TEST_F(UnsymmetricLinearSolverTest,
+       SparseNormalCholeskyUsingSuiteSparseDynamicSparsity) {
+  LinearSolver::Options options;
+  options.sparse_linear_algebra_library_type = SUITE_SPARSE;
+  options.type = SPARSE_NORMAL_CHOLESKY;
+  options.dynamic_sparsity = true;
+  TestSolver(options);
+}
 #endif
 
 #ifndef CERES_NO_CXSPARSE
@@ -192,6 +201,15 @@
   options.use_postordering = true;
   TestSolver(options);
 }
+
+TEST_F(UnsymmetricLinearSolverTest,
+       SparseNormalCholeskyUsingCXSparseDynamicSparsity) {
+  LinearSolver::Options options;
+  options.sparse_linear_algebra_library_type = CX_SPARSE;
+  options.type = SPARSE_NORMAL_CHOLESKY;
+  options.dynamic_sparsity = true;
+  TestSolver(options);
+}
 #endif
 
 }  // namespace internal