Fix Jacobian evaluation for constant parameter

This CL fixes a regression bug in numeric differentiation where the
differentiation is called even if the parameter block is hold constant.
This resulted into a write to a nullptr.

A unit test is added for Jacobian evaluation of constant parameters.

Change-Id: Ia0f7c6cc7ef18f0f2cd6d758a839729b8ff606a0
diff --git a/include/ceres/internal/numeric_diff.h b/include/ceres/internal/numeric_diff.h
index f24fdb3..09c94b0 100644
--- a/include/ceres/internal/numeric_diff.h
+++ b/include/ceres/internal/numeric_diff.h
@@ -72,6 +72,8 @@
     using Eigen::RowMajor;
     using Eigen::ColMajor;
 
+    DCHECK_NOTNULL(jacobian);
+
     const int num_residuals_internal =
         (kNumResiduals != ceres::DYNAMIC ? kNumResiduals : num_residuals);
     const int parameter_block_index_internal =
@@ -381,7 +383,7 @@
 // each parameter block.
 //
 // Example:
-// A call to 
+// A call to
 // EvaluateJacobianForParameterBlocks<StaticParameterDims<2, 3>>(
 //        functor,
 //        residuals_at_eval_point,
@@ -392,29 +394,41 @@
 // will result in the following calls to
 // NumericDiff<...>::EvaluateJacobianForParameterBlock:
 //
-// if (!NumericDiff<
-//          CostFunctor, method, kNumResiduals, ParameterDims, 0,
-//          2>::EvaluateJacobianForParameterBlock(functor,
-//                                                residuals_at_eval_point,
-//                                                options,
-//                                                num_residuals,
-//                                                0,
-//                                                2,
-//                                                parameters,
-//                                                jacobians[0])) {
-//   return false;
+// if (jacobians[0] != nullptr) {
+//   if (!NumericDiff<
+//           CostFunctor,
+//           method,
+//           kNumResiduals,
+//           StaticParameterDims<2, 3>,
+//           0,
+//           2>::EvaluateJacobianForParameterBlock(functor,
+//                                                 residuals_at_eval_point,
+//                                                 options,
+//                                                 num_residuals,
+//                                                 0,
+//                                                 2,
+//                                                 parameters,
+//                                                 jacobians[0])) {
+//     return false;
+//   }
 // }
-// if (!NumericDiff<
-//          CostFunctor, method, kNumResiduals, ParameterDims, 1,
-//          3>::EvaluateJacobianForParameterBlock(functor,
-//                                                residuals_at_eval_point,
-//                                                options,
-//                                                num_residuals,
-//                                                1,
-//                                                3,
-//                                                parameters,
-//                                                jacobians[1])) {
-//   return false;
+// if (jacobians[1] != nullptr) {
+//   if (!NumericDiff<
+//           CostFunctor,
+//           method,
+//           kNumResiduals,
+//           StaticParameterDims<2, 3>,
+//           1,
+//           3>::EvaluateJacobianForParameterBlock(functor,
+//                                                 residuals_at_eval_point,
+//                                                 options,
+//                                                 num_residuals,
+//                                                 1,
+//                                                 3,
+//                                                 parameters,
+//                                                 jacobians[1])) {
+//     return false;
+//   }
 // }
 template <typename ParameterDims,
           typename Parameters = typename ParameterDims::Parameters,
@@ -422,33 +436,46 @@
 struct EvaluateJacobianForParameterBlocks;
 
 template <typename ParameterDims, int N, int... Ns, int ParameterIdx>
-struct EvaluateJacobianForParameterBlocks<
-    ParameterDims, integer_sequence<int, N, Ns...>, ParameterIdx> {
-  template <NumericDiffMethodType method, int kNumResiduals,
+struct EvaluateJacobianForParameterBlocks<ParameterDims,
+                                          integer_sequence<int, N, Ns...>,
+                                          ParameterIdx> {
+  template <NumericDiffMethodType method,
+            int kNumResiduals,
             typename CostFunctor>
   static bool Apply(const CostFunctor* functor,
                     const double* residuals_at_eval_point,
-                    const NumericDiffOptions& options, int num_residuals,
-                    double** parameters, double** jacobians) {
-    if (!NumericDiff<
-            CostFunctor, method, kNumResiduals, ParameterDims, ParameterIdx,
-            N>::EvaluateJacobianForParameterBlock(functor,
-                                                  residuals_at_eval_point,
-                                                  options,
-                                                  num_residuals,
-                                                  ParameterIdx,
-                                                  N,
-                                                  parameters,
-                                                  jacobians[ParameterIdx])) {
-      return false;
+                    const NumericDiffOptions& options,
+                    int num_residuals,
+                    double** parameters,
+                    double** jacobians) {
+    if (jacobians[ParameterIdx] != nullptr) {
+      if (!NumericDiff<
+              CostFunctor,
+              method,
+              kNumResiduals,
+              ParameterDims,
+              ParameterIdx,
+              N>::EvaluateJacobianForParameterBlock(functor,
+                                                    residuals_at_eval_point,
+                                                    options,
+                                                    num_residuals,
+                                                    ParameterIdx,
+                                                    N,
+                                                    parameters,
+                                                    jacobians[ParameterIdx])) {
+        return false;
+      }
     }
-    return EvaluateJacobianForParameterBlocks<
-        ParameterDims, integer_sequence<int, Ns...>, ParameterIdx + 1>::
+
+    return EvaluateJacobianForParameterBlocks<ParameterDims,
+                                              integer_sequence<int, Ns...>,
+                                              ParameterIdx + 1>::
         template Apply<method, kNumResiduals>(functor,
                                               residuals_at_eval_point,
                                               options,
                                               num_residuals,
-                                              parameters, jacobians);
+                                              parameters,
+                                              jacobians);
   }
 };
 
diff --git a/internal/ceres/numeric_diff_cost_function_test.cc b/internal/ceres/numeric_diff_cost_function_test.cc
index 79d3e8a..105bef5 100644
--- a/internal/ceres/numeric_diff_cost_function_test.cc
+++ b/internal/ceres/numeric_diff_cost_function_test.cc
@@ -32,6 +32,7 @@
 #include "ceres/numeric_diff_cost_function.h"
 
 #include <algorithm>
+#include <array>
 #include <cmath>
 #include <memory>
 #include <string>
@@ -385,5 +386,62 @@
   EXPECT_FALSE(IsArrayValid(2, residuals));
 }
 
+TEST(NumericDiffCostFunction, ParameterBlockConstant) {
+  constexpr int kNumResiduals = 3;
+  constexpr int kX1 = 5;
+  constexpr int kX2 = 5;
+
+  std::unique_ptr<CostFunction> cost_function;
+  cost_function.reset(new NumericDiffCostFunction<EasyFunctor,
+                                                  CENTRAL,
+                                                  kNumResiduals,
+                                                  kX1,
+                                                  kX2>(new EasyFunctor));
+
+  // Prepare the parameters and residuals.
+  std::array<double, kX1> x1{1e-64, 2.0, 3.0, 4.0, 5.0};
+  std::array<double, kX2> x2{9.0, 9.0, 5.0, 5.0, 1.0};
+  std::array<double*, 2> parameter_blocks{x1.data(), x2.data()};
+
+  std::vector<double> residuals(kNumResiduals, -100000);
+
+  // Evaluate the full jacobian.
+  std::vector<std::vector<double>> jacobian_full_vect(2);
+  jacobian_full_vect[0].resize(kNumResiduals * kX1, -100000);
+  jacobian_full_vect[1].resize(kNumResiduals * kX2, -100000);
+  {
+    std::array<double*, 2> jacobian{jacobian_full_vect[0].data(),
+                                    jacobian_full_vect[1].data()};
+    ASSERT_TRUE(cost_function->Evaluate(
+        parameter_blocks.data(), residuals.data(), jacobian.data()));
+  }
+
+  // Evaluate and check jacobian when first parameter block is constant.
+  {
+    std::vector<double> jacobian_vect(kNumResiduals * kX2, -100000);
+    std::array<double*, 2> jacobian{nullptr, jacobian_vect.data()};
+
+    ASSERT_TRUE(cost_function->Evaluate(
+        parameter_blocks.data(), residuals.data(), jacobian.data()));
+
+    for (int i = 0; i < kNumResiduals * kX2; ++i) {
+      EXPECT_DOUBLE_EQ(jacobian_full_vect[1][i], jacobian_vect[i]);
+    }
+  }
+
+  // Evaluate and check jacobian when second parameter block is constant.
+  {
+    std::vector<double> jacobian_vect(kNumResiduals * kX1, -100000);
+    std::array<double*, 2> jacobian{jacobian_vect.data(), nullptr};
+
+    ASSERT_TRUE(cost_function->Evaluate(
+        parameter_blocks.data(), residuals.data(), jacobian.data()));
+
+    for (int i = 0; i < kNumResiduals * kX1; ++i) {
+      EXPECT_DOUBLE_EQ(jacobian_full_vect[0][i], jacobian_vect[i]);
+    }
+  }
+}
+
 }  // namespace internal
 }  // namespace ceres