Handle empty problems consistently.
Until now Ceres was inconsistent in the way it handled a solve
call on an "empty" Problem. If the problem did not contain
any residual or parameter blocks, it failed. However, if after
constructing the reduced program, the problem was found to not
contain any modifiable parameter blocks, it was considered a valid
problem which had a constant objective function value.
When creating problems automatically, it is often the case that
an empty problem is a corner case. This change makes handling this
corner case consistent with the rest of Ceres logic.
Change-Id: Ia9da09fbf5d5cd7eae6b39a92c1976b8645db9fe
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index f48ad3b..6c579a6 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -208,6 +208,22 @@
}
}
+void SummarizeGivenProgram(const Program& program, Solver::Summary* summary) {
+ summary->num_parameter_blocks = program.NumParameterBlocks();
+ summary->num_parameters = program.NumParameters();
+ summary->num_effective_parameters = program.NumEffectiveParameters();
+ summary->num_residual_blocks = program.NumResidualBlocks();
+ summary->num_residuals = program.NumResiduals();
+}
+
+void SummarizeReducedProgram(const Program& program, Solver::Summary* summary) {
+ summary->num_parameter_blocks_reduced = program.NumParameterBlocks();
+ summary->num_parameters_reduced = program.NumParameters();
+ summary->num_effective_parameters_reduced = program.NumEffectiveParameters();
+ summary->num_residual_blocks_reduced = program.NumResidualBlocks();
+ summary->num_residuals_reduced = program.NumResiduals();
+}
+
} // namespace
void SolverImpl::TrustRegionMinimize(
@@ -351,29 +367,10 @@
*CHECK_NOTNULL(summary) = Solver::Summary();
summary->minimizer_type = TRUST_REGION;
- summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
- summary->num_parameters = problem_impl->NumParameters();
- summary->num_effective_parameters =
- original_program->NumEffectiveParameters();
- summary->num_residual_blocks = problem_impl->NumResidualBlocks();
- summary->num_residuals = problem_impl->NumResiduals();
- // Empty programs are usually a user error.
- if (summary->num_parameter_blocks == 0) {
- summary->message = "Problem contains no parameter blocks.";
- LOG(ERROR) << summary->message;
- return;
- }
-
- if (summary->num_residual_blocks == 0) {
- summary->message = "Problem contains no residual blocks.";
- LOG(ERROR) << summary->message;
- return;
- }
-
+ SummarizeGivenProgram(*original_program, summary);
SummarizeOrdering(original_options.linear_solver_ordering,
&(summary->linear_solver_ordering_given));
-
SummarizeOrdering(original_options.inner_iteration_ordering,
&(summary->inner_iteration_ordering_given));
@@ -475,13 +472,7 @@
SummarizeOrdering(options.linear_solver_ordering,
&(summary->linear_solver_ordering_used));
-
- summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
- summary->num_parameters_reduced = reduced_program->NumParameters();
- summary->num_effective_parameters_reduced =
- reduced_program->NumEffectiveParameters();
- summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
- summary->num_residuals_reduced = reduced_program->NumResiduals();
+ SummarizeReducedProgram(*reduced_program, summary);
if (summary->num_parameter_blocks_reduced == 0) {
summary->preprocessor_time_in_seconds =
@@ -641,6 +632,8 @@
*CHECK_NOTNULL(summary) = Solver::Summary();
summary->minimizer_type = LINE_SEARCH;
+ SummarizeGivenProgram(*original_program, summary);
+
summary->line_search_direction_type =
original_options.line_search_direction_type;
summary->max_lbfgs_rank = original_options.max_lbfgs_rank;
@@ -650,13 +643,6 @@
summary->nonlinear_conjugate_gradient_type =
original_options.nonlinear_conjugate_gradient_type;
- summary->num_parameter_blocks = original_program->NumParameterBlocks();
- summary->num_parameters = original_program->NumParameters();
- summary->num_residual_blocks = original_program->NumResidualBlocks();
- summary->num_residuals = original_program->NumResiduals();
- summary->num_effective_parameters =
- original_program->NumEffectiveParameters();
-
// Validate values for configuration parameters supplied by user.
if ((original_options.line_search_direction_type == ceres::BFGS ||
original_options.line_search_direction_type == ceres::LBFGS) &&
@@ -739,19 +725,6 @@
return;
}
- // Empty programs are usually a user error.
- if (summary->num_parameter_blocks == 0) {
- summary->message = "Problem contains no parameter blocks.";
- LOG(ERROR) << summary->message;
- return;
- }
-
- if (summary->num_residual_blocks == 0) {
- summary->message = "Problem contains no residual blocks.";
- LOG(ERROR) << summary->message;
- return;
- }
-
Solver::Options options(original_options);
// This ensures that we get a Block Jacobian Evaluator along with
@@ -759,7 +732,6 @@
// refactored to deal with the various bits of cleanups related to
// line search.
options.linear_solver_type = CGNR;
-
options.linear_solver_ordering = NULL;
options.inner_iteration_ordering = NULL;
@@ -824,13 +796,7 @@
return;
}
- summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
- summary->num_parameters_reduced = reduced_program->NumParameters();
- summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
- summary->num_effective_parameters_reduced =
- reduced_program->NumEffectiveParameters();
- summary->num_residuals_reduced = reduced_program->NumResiduals();
-
+ SummarizeReducedProgram(*reduced_program, summary);
if (summary->num_parameter_blocks_reduced == 0) {
summary->preprocessor_time_in_seconds =
WallTimeInSeconds() - solver_start_time;