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