Fix lower-bound on result of minimising step-size polynomial. - Previously we were requesting a step-size which minimised the polynomial in: [previous.x, current.x * factor]. - This is incorrect c/f Nocedal & Wright p60, the bounds for the minimising step-size should be: [current.x, current.x * factor]. - However, Nocedal & Wright's bounds are insufficient when the function can return invalid values, which we support. - In the case that f(current) is invalid, we need to contract the step-size to lie within: [previous.x, current.x) given that we know that previous.x is valid so a valid step must exist within that range. Change-Id: I67b5aaa09cc6d54cf5f264e2cf894ddc2af3f3ad
diff --git a/internal/ceres/line_search.cc b/internal/ceres/line_search.cc index 86302a9..dd73b0e 100644 --- a/internal/ceres/line_search.cc +++ b/internal/ceres/line_search.cc
@@ -627,7 +627,18 @@ // being a boundary of a bracket. // If f(current) is valid, (but meets no criteria) expand the search by - // increasing the step size. + // increasing the step size. If f(current) is invalid, contract the step + // size. + // + // In Nocedal & Wright [1] (p60), the step-size can only increase in the + // bracketing phase: step_size_{k+1} \in [step_size_k, step_size_k * factor]. + // However this does not account for the function returning invalid values + // which we support, in which case we need to contract the step size whilst + // ensuring that we do not invert the bracket, i.e, we require that: + // step_size_{k-1} <= step_size_{k+1} < step_size_k. + const double min_step_size = + current.value_is_valid + ? current.x : previous.x; const double max_step_size = current.value_is_valid ? (current.x * options().max_step_expansion) : current.x; @@ -646,7 +657,7 @@ previous, unused_previous, current, - previous.x, + min_step_size, max_step_size); summary->polynomial_minimization_time_in_seconds += (WallTimeInSeconds() - polynomial_minimization_start_time); @@ -659,6 +670,10 @@ return false; } + // Only advance the lower boundary (in x) of the bracket if f(current) + // is valid such that we can support contracting the step size when + // f(current) is invalid without risking inverting the bracket in x, i.e. + // prevent previous.x > current.x. previous = current.value_is_valid ? current : previous; ++summary->num_function_evaluations; ++summary->num_gradient_evaluations;