Numerically robust computation of model_cost_change.
Change-Id: I421df17bab3bfdf782d95285cf352ed37675d835
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index e5f9100..3e13c86 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -270,8 +270,8 @@
Solver::Options options;
SetSolverOptionsFromFlags(&bal_problem, &options);
options.solver_log = FLAGS_solver_log;
- options.gradient_tolerance *= 1e-3;
-
+ options.gradient_tolerance = 1e-12;
+ options.function_tolerance = 1e-12;
Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.FullReport() << "\n";
diff --git a/examples/nist.cc b/examples/nist.cc
index 0f5951f..4fce3e7 100644
--- a/examples/nist.cc
+++ b/examples/nist.cc
@@ -374,16 +374,18 @@
-std::log10(fabs(summary.final_cost - certified_cost) / certified_cost);
}
+ std::cerr << "start " << start + 1 << " " ;
if (num_matching_digits <= kMinNumMatchingDigits) {
- std::cerr << "start " << start + 1 << " " ;
std::cerr << "FAILURE";
- std::cerr << " summary: "
- << summary.BriefReport()
- << " Certified cost: " << certified_cost
- << std::endl;
} else {
+ std::cerr << "SUCCESS";
++num_success;
}
+ std::cerr << " summary: "
+ << summary.BriefReport()
+ << " Certified cost: " << certified_cost
+ << std::endl;
+
}
return num_success;
diff --git a/internal/ceres/trust_region_minimizer.cc b/internal/ceres/trust_region_minimizer.cc
index 194d54e..d848a17 100644
--- a/internal/ceres/trust_region_minimizer.cc
+++ b/internal/ceres/trust_region_minimizer.cc
@@ -270,23 +270,24 @@
<< options.lsqp_dump_directory << "but failed.";
}
- double new_model_cost = 0.0;
+ double model_cost_change = 0.0;
if (strategy_summary.termination_type != FAILURE) {
- // new_model_cost = 1/2 |f + J * step|^2
- model_residuals = residuals;
+ // new_model_cost
+ // = 1/2 [f + J * step]^2
+ // = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ]
+ // model_cost_change
+ // = cost - new_model_cost
+ // = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step]
+ // = -f'J * step - step' * J' * J * step / 2
+ model_residuals.setZero();
jacobian->RightMultiply(trust_region_step.data(), model_residuals.data());
- new_model_cost = model_residuals.squaredNorm() / 2.0;
+ model_cost_change = -(residuals.dot(model_residuals) +
+ model_residuals.squaredNorm() / 2.0);
- // In exact arithmetic, this would never be the case. But poorly
- // conditioned matrices can give rise to situations where the
- // new_model_cost can actually be larger than half the squared
- // norm of the residual vector. We allow for small tolerance
- // around cost and beyond that declare the step to be invalid.
- if ((1.0 - new_model_cost / cost) < -kEpsilon) {
+ if (model_cost_change < 0.0) {
VLOG(1) << "Invalid step: current_cost: " << cost
- << " new_model_cost " << new_model_cost
- << " absolute difference " << (cost - new_model_cost)
- << " relative difference " << (1.0 - new_model_cost/cost);
+ << " absolute difference " << model_cost_change
+ << " relative difference " << (model_cost_change / cost);
} else {
iteration_summary.step_is_valid = true;
}
@@ -322,25 +323,6 @@
// The step is numerically valid, so now we can judge its quality.
num_consecutive_invalid_steps = 0;
- // We allow some slop around 0, and clamp the model_cost_change
- // at kEpsilon * min(1.0, cost) from below.
- //
- // In exact arithmetic this should never be needed, as we are
- // guaranteed to new_model_cost <= cost. However, due to various
- // numerical issues, it is possible that new_model_cost is
- // nearly equal to cost, and the difference is a small negative
- // number. To make sure that the relative_decrease computation
- // remains sane, as clamp the difference (cost - new_model_cost)
- // from below at a small positive number.
- //
- // This number is the minimum of kEpsilon * (cost, 1.0), which
- // ensures that it will never get too large in absolute value,
- // while scaling down proportionally with the magnitude of the
- // cost. This is important for problems where the minimum of the
- // objective function is near zero.
- const double model_cost_change =
- max(kEpsilon * min(1.0, cost), cost - new_model_cost);
-
// Undo the Jacobian column scaling.
delta = (trust_region_step.array() * scale.array()).matrix();
iteration_summary.step_norm = delta.norm();