Ensure that partial evaluation of residuals triggers an error ResidualBlock evaluation has logic to ensure that CostFunction should always fill out the residual and jacobian arrays completely by using a special value to pre-populate these arrays. This works for CostFunctions with analytical Jacobians but not for AutoDiffCostFunction and NumericDiffCostFunction Jacobians. There is no way to fix this for NumericDiffCostFunctions without introducing significant performance penalties but the residual evaluation fails, which should be enough to catch such errors. For AutoDiffCostFunction the way the Jets are default initialized was sidestepping this check. So now, the Jet that is used to capture the output residuals is now initialized with kImpossibleValue, which will ensure that if the user forgets to fill all output fields, it triggers an evaluation error. This change required that ceres::internal::kImpossibleValue be moved out of array_utils.h/cc to types.h. Change-Id: I35bb0946cf0785a5d43c7b5459a2272848fb2a9b
diff --git a/internal/ceres/numeric_diff_cost_function_test.cc b/internal/ceres/numeric_diff_cost_function_test.cc index 13ab106..983f11e 100644 --- a/internal/ceres/numeric_diff_cost_function_test.cc +++ b/internal/ceres/numeric_diff_cost_function_test.cc
@@ -37,6 +37,7 @@ #include <vector> #include "ceres/internal/macros.h" #include "ceres/internal/scoped_ptr.h" +#include "ceres/array_utils.h" #include "ceres/numeric_diff_test_utils.h" #include "ceres/test_util.h" #include "ceres/types.h" @@ -353,6 +354,36 @@ functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function); } +struct OnlyFillsOneOutputFunctor { + bool operator()(const double* x, double* output) const { + output[0] = x[0]; + return true; + } +}; + +TEST(NumericDiffCostFunction, PartiallyFilledResidualShouldFailEvaluation) { + double parameter = 1.0; + double jacobian[2]; + double residuals[2]; + double* parameters[] = {¶meter}; + double* jacobians[] = {jacobian}; + + scoped_ptr<CostFunction> cost_function( + new NumericDiffCostFunction<OnlyFillsOneOutputFunctor, CENTRAL, 2, 1>( + new OnlyFillsOneOutputFunctor)); + InvalidateArray(2, jacobian); + InvalidateArray(2, residuals); + EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, jacobians)); + EXPECT_FALSE(IsArrayValid(2, residuals)); + InvalidateArray(2, residuals); + EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, NULL)); + // We are only testing residuals here, because the Jacobians are + // computed using finite differencing from the residuals, so unless + // we introduce a validation step after every evaluation of + // residuals inside NumericDiffCostFunction, there is no way of + // ensuring that the Jacobian array is invalid. + EXPECT_FALSE(IsArrayValid(2, residuals)); +} } // namespace internal } // namespace ceres