mirror of
https://github.com/ceres-solver/ceres-solver.git
synced 2026-08-29 16:40:38 +08:00
Get rid of redundant function evaluations in LineSearchMinimizer
1. Replace LineSearch::Summary::optimal_step_size with LineSearch:Summary::optimal_point which is a FunctionSample. 2. Add the actual vector position and vector gradient of the point in the FunctionSample 3. Use the above two to get rid of an extraneous function evalation in LineSearchMinimizer. Runtime performance is almost 2x improved as a result. Thanks to @svenpilz for reporting this. https://github.com/ceres-solver/ceres-solver/issues/296 Change-Id: Iebf2db7acecb2c95c9b1683b73cdc5faab78b02e
This commit is contained in:
@@ -54,30 +54,8 @@ using std::vector;
|
||||
namespace {
|
||||
// Precision used for floating point values in error message output.
|
||||
const int kErrorMessageNumericPrecision = 8;
|
||||
|
||||
FunctionSample ValueSample(const double x, const double value) {
|
||||
FunctionSample sample;
|
||||
sample.x = x;
|
||||
sample.value = value;
|
||||
sample.value_is_valid = true;
|
||||
return sample;
|
||||
}
|
||||
|
||||
FunctionSample ValueAndGradientSample(const double x,
|
||||
const double value,
|
||||
const double gradient) {
|
||||
FunctionSample sample;
|
||||
sample.x = x;
|
||||
sample.value = value;
|
||||
sample.gradient = gradient;
|
||||
sample.value_is_valid = true;
|
||||
sample.gradient_is_valid = true;
|
||||
return sample;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
ostream& operator<<(ostream &os, const FunctionSample& sample);
|
||||
|
||||
// Convenience stream operator for pushing FunctionSamples into log messages.
|
||||
@@ -113,9 +91,7 @@ LineSearchFunction::LineSearchFunction(Evaluator* evaluator)
|
||||
: evaluator_(evaluator),
|
||||
position_(evaluator->NumParameters()),
|
||||
direction_(evaluator->NumEffectiveParameters()),
|
||||
evaluation_point_(evaluator->NumParameters()),
|
||||
scaled_direction_(evaluator->NumEffectiveParameters()),
|
||||
gradient_(evaluator->NumEffectiveParameters()),
|
||||
initial_evaluator_residual_time_in_seconds(0.0),
|
||||
initial_evaluator_jacobian_time_in_seconds(0.0) {}
|
||||
|
||||
@@ -129,33 +105,44 @@ void LineSearchFunction::Evaluate(const double x,
|
||||
const bool evaluate_gradient,
|
||||
FunctionSample* output) {
|
||||
output->x = x;
|
||||
output->vector_x_is_valid = false;
|
||||
output->value_is_valid = false;
|
||||
output->gradient_is_valid = false;
|
||||
output->vector_gradient_is_valid = false;
|
||||
|
||||
scaled_direction_ = output->x * direction_;
|
||||
output->vector_x.resize(position_.rows(), 1);
|
||||
if (!evaluator_->Plus(position_.data(),
|
||||
scaled_direction_.data(),
|
||||
evaluation_point_.data())) {
|
||||
output->vector_x.data())) {
|
||||
return;
|
||||
}
|
||||
output->vector_x_is_valid = true;
|
||||
|
||||
const bool eval_status =
|
||||
evaluator_->Evaluate(evaluation_point_.data(),
|
||||
&(output->value),
|
||||
NULL,
|
||||
evaluate_gradient ? gradient_.data() : NULL,
|
||||
NULL);
|
||||
double* gradient = NULL;
|
||||
if (evaluate_gradient) {
|
||||
output->vector_gradient.resize(direction_.rows(), 1);
|
||||
gradient = output->vector_gradient.data();
|
||||
}
|
||||
const bool eval_status = evaluator_->Evaluate(
|
||||
output->vector_x.data(), &(output->value), NULL, gradient, NULL);
|
||||
|
||||
if (!eval_status || !IsFinite(output->value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
output->value_is_valid = true;
|
||||
if (evaluate_gradient) {
|
||||
output->gradient = direction_.dot(gradient_);
|
||||
if (!evaluate_gradient) {
|
||||
return;
|
||||
}
|
||||
output->gradient_is_valid = IsFinite(output->gradient);
|
||||
return;
|
||||
|
||||
output->gradient = direction_.dot(output->vector_gradient);
|
||||
if (!IsFinite(output->gradient)) {
|
||||
return;
|
||||
}
|
||||
|
||||
output->gradient_is_valid = true;
|
||||
output->vector_gradient_is_valid = true;
|
||||
}
|
||||
|
||||
double LineSearchFunction::DirectionInfinityNorm() const {
|
||||
@@ -200,7 +187,6 @@ void LineSearch::Search(double step_size_estimate,
|
||||
summary->cost_evaluation_time_in_seconds = 0.0;
|
||||
summary->gradient_evaluation_time_in_seconds = 0.0;
|
||||
summary->polynomial_minimization_time_in_seconds = 0.0;
|
||||
|
||||
options().function->ResetTimeStatistics();
|
||||
this->DoSearch(step_size_estimate, initial_cost, initial_gradient, summary);
|
||||
options().function->
|
||||
@@ -254,12 +240,12 @@ double LineSearch::InterpolatingPolynomialMinimizingStepSize(
|
||||
if (interpolation_type == QUADRATIC) {
|
||||
// Two point interpolation using function values and the
|
||||
// gradient at the lower bound.
|
||||
samples.push_back(ValueSample(current.x, current.value));
|
||||
samples.push_back(FunctionSample(current.x, current.value));
|
||||
|
||||
if (previous.value_is_valid) {
|
||||
// Three point interpolation, using function values and the
|
||||
// gradient at the lower bound.
|
||||
samples.push_back(ValueSample(previous.x, previous.value));
|
||||
samples.push_back(FunctionSample(previous.x, previous.value));
|
||||
}
|
||||
} else if (interpolation_type == CUBIC) {
|
||||
// Two point interpolation using the function values and the gradients.
|
||||
@@ -297,8 +283,9 @@ void ArmijoLineSearch::DoSearch(const double step_size_estimate,
|
||||
|
||||
// Note initial_cost & initial_gradient are evaluated at step_size = 0,
|
||||
// not step_size_estimate, which is our starting guess.
|
||||
const FunctionSample initial_position =
|
||||
ValueAndGradientSample(0.0, initial_cost, initial_gradient);
|
||||
FunctionSample initial_position(0.0, initial_cost, initial_gradient);
|
||||
initial_position.vector_x = function->position();
|
||||
initial_position.vector_x_is_valid = true;
|
||||
|
||||
const double descent_direction_max_norm = function->DirectionInfinityNorm();
|
||||
FunctionSample previous;
|
||||
@@ -365,7 +352,7 @@ void ArmijoLineSearch::DoSearch(const double step_size_estimate,
|
||||
function->Evaluate(step_size, kEvaluateGradient, ¤t);
|
||||
}
|
||||
|
||||
summary->optimal_step_size = current.x;
|
||||
summary->optimal_point = current;
|
||||
summary->success = true;
|
||||
}
|
||||
|
||||
@@ -387,9 +374,9 @@ void WolfeLineSearch::DoSearch(const double step_size_estimate,
|
||||
|
||||
// Note initial_cost & initial_gradient are evaluated at step_size = 0,
|
||||
// not step_size_estimate, which is our starting guess.
|
||||
const FunctionSample initial_position =
|
||||
ValueAndGradientSample(0.0, initial_cost, initial_gradient);
|
||||
|
||||
FunctionSample initial_position(0.0, initial_cost, initial_gradient);
|
||||
initial_position.vector_x = options().function->position();
|
||||
initial_position.vector_x_is_valid = true;
|
||||
bool do_zoom_search = false;
|
||||
// Important: The high/low in bracket_high & bracket_low refer to their
|
||||
// _function_ values, not their step sizes i.e. it is _not_ required that
|
||||
@@ -431,7 +418,7 @@ void WolfeLineSearch::DoSearch(const double step_size_estimate,
|
||||
// produce a valid point when ArmijoLineSearch would succeed, we return the
|
||||
// point with the lowest cost found thus far which satsifies the Armijo
|
||||
// condition (but not the Wolfe conditions).
|
||||
summary->optimal_step_size = bracket_low.x;
|
||||
summary->optimal_point = bracket_low;
|
||||
summary->success = true;
|
||||
return;
|
||||
}
|
||||
@@ -477,11 +464,13 @@ void WolfeLineSearch::DoSearch(const double step_size_estimate,
|
||||
// satisfies the strong Wolfe curvature condition, that we return the point
|
||||
// amongst those found thus far, which minimizes f() and satisfies the Armijo
|
||||
// condition.
|
||||
solution =
|
||||
solution.value_is_valid && solution.value <= bracket_low.value
|
||||
? solution : bracket_low;
|
||||
|
||||
summary->optimal_step_size = solution.x;
|
||||
if (!solution.value_is_valid || solution.value > bracket_low.value) {
|
||||
summary->optimal_point = bracket_low;
|
||||
} else {
|
||||
summary->optimal_point = solution;
|
||||
}
|
||||
|
||||
summary->success = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user