Fix a bug in DynamicAutoDiffCostFunction
DynamicAutoDiffCostFunction::Evaluate when provided with a jacobians
array that was non-empty but all its entries are nullptr, would
compute num_active_parameters = 0, and then skip over all the loops
that evaluated the CostFunctor.
The fix is to check if num_active_parameters == 0, and then treat
it as the case where jacobians array is null.
Thanks to Ky Waegel for reporting and providing a reproduction for this.
Change-Id: Ib86930c2c3f722724d249f662bf88238679bbf98
diff --git a/include/ceres/dynamic_autodiff_cost_function.h b/include/ceres/dynamic_autodiff_cost_function.h
index 7b75150..1027064 100644
--- a/include/ceres/dynamic_autodiff_cost_function.h
+++ b/include/ceres/dynamic_autodiff_cost_function.h
@@ -151,6 +151,9 @@
}
}
+ if (num_active_parameters == 0) {
+ return (*functor_)(parameters, residuals);
+ }
// When `num_active_parameters % Stride != 0` then it can be the case
// that `active_parameter_count < Stride` while parameter_cursor is less
// than the total number of parameters and with no remaining non-constant
diff --git a/internal/ceres/dynamic_autodiff_cost_function_test.cc b/internal/ceres/dynamic_autodiff_cost_function_test.cc
index bec31c5..6b57fd3 100644
--- a/internal/ceres/dynamic_autodiff_cost_function_test.cc
+++ b/internal/ceres/dynamic_autodiff_cost_function_test.cc
@@ -771,5 +771,51 @@
}
}
+class ValueError {
+ public:
+ explicit ValueError(double target_value) : target_value_(target_value) {}
+
+ template <typename T>
+ bool operator()(const T* value, T* residual) const {
+ *residual = *value - T(target_value_);
+ return true;
+ }
+
+ protected:
+ double target_value_;
+};
+
+class DynamicValueError {
+ public:
+ explicit DynamicValueError(double target_value)
+ : target_value_(target_value) {}
+
+ template <typename T>
+ bool operator()(T const* const* parameters, T* residual) const {
+ residual[0] = T(target_value_) - parameters[0][0];
+ return true;
+ }
+
+ protected:
+ double target_value_;
+};
+
+TEST(DynamicAutoDiffCostFunction,
+ EvaluateWithEmptyJacobiansArrayComputesResidual) {
+ const double target_value = 1.0;
+ double parameter = 0;
+ ceres::DynamicAutoDiffCostFunction<DynamicValueError, 1> cost_function(
+ new DynamicValueError(target_value));
+ cost_function.AddParameterBlock(1);
+ cost_function.SetNumResiduals(1);
+
+ double* parameter_blocks[1] = {¶meter};
+ double* jacobians[1] = {nullptr};
+ double residual;
+
+ EXPECT_TRUE(cost_function.Evaluate(parameter_blocks, &residual, jacobians));
+ EXPECT_EQ(residual, target_value);
+}
+
} // namespace internal
} // namespace ceres