blob: 8f2f38b067262531f5ec879dbb0a03eefda99269 [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: keir@google.com (Keir Mierle)
30
31#include "ceres/solver_impl.h"
32
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -070033#include <cstdio>
Keir Mierle8ebb0732012-04-30 23:09:08 -070034#include <iostream> // NOLINT
35#include <numeric>
36#include "ceres/evaluator.h"
37#include "ceres/gradient_checking_cost_function.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070038#include "ceres/iteration_callback.h"
39#include "ceres/levenberg_marquardt_strategy.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070040#include "ceres/linear_solver.h"
41#include "ceres/map_util.h"
42#include "ceres/minimizer.h"
Sameer Agarwal91c9bfe2012-09-10 17:41:38 -070043#include "ceres/ordering.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070044#include "ceres/parameter_block.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070045#include "ceres/problem.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070046#include "ceres/problem_impl.h"
47#include "ceres/program.h"
48#include "ceres/residual_block.h"
49#include "ceres/schur_ordering.h"
50#include "ceres/stringprintf.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070051#include "ceres/trust_region_minimizer.h"
Petter Strandmark76533b32012-09-04 22:08:50 -070052#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070053
54namespace ceres {
55namespace internal {
56namespace {
57
Keir Mierle8ebb0732012-04-30 23:09:08 -070058// Callback for updating the user's parameter blocks. Updates are only
59// done if the step is successful.
60class StateUpdatingCallback : public IterationCallback {
61 public:
62 StateUpdatingCallback(Program* program, double* parameters)
63 : program_(program), parameters_(parameters) {}
64
65 CallbackReturnType operator()(const IterationSummary& summary) {
66 if (summary.step_is_successful) {
67 program_->StateVectorToParameterBlocks(parameters_);
68 program_->CopyParameterBlockStateToUserState();
69 }
70 return SOLVER_CONTINUE;
71 }
72
73 private:
74 Program* program_;
75 double* parameters_;
76};
77
78// Callback for logging the state of the minimizer to STDERR or STDOUT
79// depending on the user's preferences and logging level.
80class LoggingCallback : public IterationCallback {
81 public:
82 explicit LoggingCallback(bool log_to_stdout)
83 : log_to_stdout_(log_to_stdout) {}
84
85 ~LoggingCallback() {}
86
87 CallbackReturnType operator()(const IterationSummary& summary) {
88 const char* kReportRowFormat =
89 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -070090 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -070091 string output = StringPrintf(kReportRowFormat,
92 summary.iteration,
93 summary.cost,
94 summary.cost_change,
95 summary.gradient_max_norm,
96 summary.step_norm,
97 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070098 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -070099 summary.linear_solver_iterations,
100 summary.iteration_time_in_seconds,
101 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700102 if (log_to_stdout_) {
103 cout << output << endl;
104 } else {
105 VLOG(1) << output;
106 }
107 return SOLVER_CONTINUE;
108 }
109
110 private:
111 const bool log_to_stdout_;
112};
113
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700114// Basic callback to record the execution of the solver to a file for
115// offline analysis.
116class FileLoggingCallback : public IterationCallback {
117 public:
118 explicit FileLoggingCallback(const string& filename)
119 : fptr_(NULL) {
120 fptr_ = fopen(filename.c_str(), "w");
121 CHECK_NOTNULL(fptr_);
122 }
123
124 virtual ~FileLoggingCallback() {
125 if (fptr_ != NULL) {
126 fclose(fptr_);
127 }
128 }
129
130 virtual CallbackReturnType operator()(const IterationSummary& summary) {
131 fprintf(fptr_,
132 "%4d %e %e\n",
133 summary.iteration,
134 summary.cost,
135 summary.cumulative_time_in_seconds);
136 return SOLVER_CONTINUE;
137 }
138 private:
139 FILE* fptr_;
140};
141
Keir Mierle8ebb0732012-04-30 23:09:08 -0700142} // namespace
143
144void SolverImpl::Minimize(const Solver::Options& options,
145 Program* program,
146 Evaluator* evaluator,
147 LinearSolver* linear_solver,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700148 double* parameters,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700149 Solver::Summary* summary) {
150 Minimizer::Options minimizer_options(options);
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700151
152 // TODO(sameeragarwal): Add support for logging the configuration
153 // and more detailed stats.
154 scoped_ptr<IterationCallback> file_logging_callback;
155 if (!options.solver_log.empty()) {
156 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
157 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
158 file_logging_callback.get());
159 }
160
Keir Mierle8ebb0732012-04-30 23:09:08 -0700161 LoggingCallback logging_callback(options.minimizer_progress_to_stdout);
162 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700163 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
164 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700165 }
166
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700167 StateUpdatingCallback updating_callback(program, parameters);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700168 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700169 // This must get pushed to the front of the callbacks so that it is run
170 // before any of the user callbacks.
171 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
172 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700173 }
174
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700175 minimizer_options.evaluator = evaluator;
176 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
177 minimizer_options.jacobian = jacobian.get();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700178
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700179 TrustRegionStrategy::Options trust_region_strategy_options;
180 trust_region_strategy_options.linear_solver = linear_solver;
181 trust_region_strategy_options.initial_radius =
182 options.initial_trust_region_radius;
183 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
184 trust_region_strategy_options.lm_min_diagonal = options.lm_min_diagonal;
185 trust_region_strategy_options.lm_max_diagonal = options.lm_max_diagonal;
186 trust_region_strategy_options.trust_region_strategy_type =
187 options.trust_region_strategy_type;
Markus Moll51cf7cb2012-08-20 20:10:20 +0200188 trust_region_strategy_options.dogleg_type = options.dogleg_type;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700189 scoped_ptr<TrustRegionStrategy> strategy(
190 TrustRegionStrategy::Create(trust_region_strategy_options));
191 minimizer_options.trust_region_strategy = strategy.get();
192
193 TrustRegionMinimizer minimizer;
Petter Strandmark76533b32012-09-04 22:08:50 -0700194 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700195 minimizer.Minimize(minimizer_options, parameters, summary);
Petter Strandmark76533b32012-09-04 22:08:50 -0700196 summary->minimizer_time_in_seconds =
197 WallTimeInSeconds() - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700198}
199
200void SolverImpl::Solve(const Solver::Options& original_options,
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700201 ProblemImpl* original_problem_impl,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700202 Solver::Summary* summary) {
Petter Strandmark76533b32012-09-04 22:08:50 -0700203 double solver_start_time = WallTimeInSeconds();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700204 Solver::Options options(original_options);
Sameer Agarwal91c9bfe2012-09-10 17:41:38 -0700205
206 // Code for supporting both the old and the new ordering API. This
207 // code will get refactored and re-written as the old API is
208 // steadily deprecated.
209
210 // Clobber all the input parameters from the old api parameters.
211 if (options.use_new_ordering_api) {
212 // For linear solvers which are not of Schur type, do nothing.
213 options.ordering.clear();
214 options.num_eliminate_blocks = 0;
215 options.ordering_type = NATURAL;
216
217 if (IsSchurType(options.linear_solver_type)) {
218 if (options.ordering_new_api == NULL) {
219 // User says we are free to find the independent set, and order
220 // any which way.
221 options.ordering_type = SCHUR;
222 } else {
223 // User has given an ordering and asked for a Schur type solver.
224 options.ordering_type = USER;
225
226 // The lowest numbered group corresponds to
227 // num_eliminate_blocks e_blocks.
228 const map<int, set<double*> >& group_id_to_parameter_block
229 = options.ordering_new_api->group_id_to_parameter_blocks();
230
231 map<int, set<double*> >::const_iterator it =
232 group_id_to_parameter_block.begin();
233 CHECK(it != group_id_to_parameter_block.end());
234
235 options.num_eliminate_blocks = it->second.size();
236 for (; it != group_id_to_parameter_block.end(); ++it) {
237 options.ordering.insert(options.ordering.end(),
238 it->second.begin(),
239 it->second.end());
240 }
241 CHECK_EQ(options.ordering.size(),
242 original_problem_impl->NumParameterBlocks());
243 }
244 }
245 } else {
246 CHECK(options.ordering_new_api == NULL);
247 }
248
249
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700250 Program* original_program = original_problem_impl->mutable_program();
251 ProblemImpl* problem_impl = original_problem_impl;
252 // Reset the summary object to its default values.
253 *CHECK_NOTNULL(summary) = Solver::Summary();
254
Keir Mierle8ebb0732012-04-30 23:09:08 -0700255
256#ifndef CERES_USE_OPENMP
257 if (options.num_threads > 1) {
258 LOG(WARNING)
259 << "OpenMP support is not compiled into this binary; "
260 << "only options.num_threads=1 is supported. Switching"
261 << "to single threaded mode.";
262 options.num_threads = 1;
263 }
264 if (options.num_linear_solver_threads > 1) {
265 LOG(WARNING)
Sameer Agarwal08f0d4d2012-09-05 14:06:22 -0700266 << "OpenMP support is not compiled into this binary"
Keir Mierle8ebb0732012-04-30 23:09:08 -0700267 << "only options.num_linear_solver_threads=1 is supported. Switching"
268 << "to single threaded mode.";
269 options.num_linear_solver_threads = 1;
270 }
271#endif
272
Sameer Agarwal08f0d4d2012-09-05 14:06:22 -0700273 if (IsSchurType(options.linear_solver_type) &&
274 options.ordering_type != SCHUR &&
275 options.num_eliminate_blocks < 1) {
276 summary->error =
277 StringPrintf("Using a Schur type solver with %s"
278 " ordering requires that"
279 " Solver::Options::num_eliminate_blocks"
280 " be set to a positive integer.",
281 OrderingTypeToString(options.ordering_type));
282 LOG(WARNING) << summary->error;
283 return;
284 }
285
Keir Mierle8ebb0732012-04-30 23:09:08 -0700286 summary->linear_solver_type_given = options.linear_solver_type;
287 summary->num_eliminate_blocks_given = original_options.num_eliminate_blocks;
288 summary->num_threads_given = original_options.num_threads;
289 summary->num_linear_solver_threads_given =
290 original_options.num_linear_solver_threads;
291 summary->ordering_type = original_options.ordering_type;
292
Keir Mierle8ebb0732012-04-30 23:09:08 -0700293 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
294 summary->num_parameters = problem_impl->NumParameters();
295 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
296 summary->num_residuals = problem_impl->NumResiduals();
297
298 summary->num_threads_used = options.num_threads;
Sameer Agarwal97fb6d92012-06-17 10:08:19 -0700299 summary->sparse_linear_algebra_library =
300 options.sparse_linear_algebra_library;
301 summary->trust_region_strategy_type = options.trust_region_strategy_type;
Sameer Agarwal1e289202012-08-21 18:00:54 -0700302 summary->dogleg_type = options.dogleg_type;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700303
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700304 // Evaluate the initial cost, residual vector and the jacobian
305 // matrix if requested by the user. The initial cost needs to be
306 // computed on the original unpreprocessed problem, as it is used to
307 // determine the value of the "fixed" part of the objective function
308 // after the problem has undergone reduction.
309 Evaluator::Evaluate(
310 original_program,
311 options.num_threads,
312 &(summary->initial_cost),
313 options.return_initial_residuals ? &summary->initial_residuals : NULL,
314 options.return_initial_gradient ? &summary->initial_gradient : NULL,
315 options.return_initial_jacobian ? &summary->initial_jacobian : NULL);
316 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700317
318 // If the user requests gradient checking, construct a new
319 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
320 // GradientCheckingCostFunction and replacing problem_impl with
321 // gradient_checking_problem_impl.
322 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
Keir Mierle6196cba2012-06-18 11:03:40 -0700323 // Save the original problem impl so we don't use the gradient
324 // checking one when computing the residuals.
Keir Mierle8ebb0732012-04-30 23:09:08 -0700325 if (options.check_gradients) {
326 VLOG(1) << "Checking Gradients";
327 gradient_checking_problem_impl.reset(
328 CreateGradientCheckingProblemImpl(
329 problem_impl,
330 options.numeric_derivative_relative_step_size,
331 options.gradient_check_relative_precision));
332
333 // From here on, problem_impl will point to the GradientChecking version.
334 problem_impl = gradient_checking_problem_impl.get();
335 }
336
337 // Create the three objects needed to minimize: the transformed program, the
338 // evaluator, and the linear solver.
Keir Mierle31c1e782012-08-17 16:16:32 -0700339 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
340 problem_impl,
341 &summary->fixed_cost,
342 &summary->error));
Keir Mierle8ebb0732012-04-30 23:09:08 -0700343 if (reduced_program == NULL) {
344 return;
345 }
346
347 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
348 summary->num_parameters_reduced = reduced_program->NumParameters();
349 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
350 summary->num_residuals_reduced = reduced_program->NumResiduals();
351
352 scoped_ptr<LinearSolver>
353 linear_solver(CreateLinearSolver(&options, &summary->error));
354 summary->linear_solver_type_used = options.linear_solver_type;
355 summary->preconditioner_type = options.preconditioner_type;
356 summary->num_eliminate_blocks_used = options.num_eliminate_blocks;
357 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
358
359 if (linear_solver == NULL) {
360 return;
361 }
362
363 if (!MaybeReorderResidualBlocks(options,
364 reduced_program.get(),
365 &summary->error)) {
366 return;
367 }
368
369 scoped_ptr<Evaluator> evaluator(
370 CreateEvaluator(options, reduced_program.get(), &summary->error));
371 if (evaluator == NULL) {
372 return;
373 }
374
375 // The optimizer works on contiguous parameter vectors; allocate some.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700376 Vector parameters(reduced_program->NumParameters());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700377
378 // Collect the discontiguous parameters into a contiguous state vector.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700379 reduced_program->ParameterBlocksToStateVector(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700380
Petter Strandmark76533b32012-09-04 22:08:50 -0700381 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalfa015192012-06-11 14:21:42 -0700382 summary->preprocessor_time_in_seconds =
383 minimizer_start_time - solver_start_time;
384
Keir Mierle8ebb0732012-04-30 23:09:08 -0700385 // Run the optimization.
386 Minimize(options,
387 reduced_program.get(),
388 evaluator.get(),
389 linear_solver.get(),
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700390 parameters.data(),
Keir Mierle8ebb0732012-04-30 23:09:08 -0700391 summary);
392
393 // If the user aborted mid-optimization or the optimization
394 // terminated because of a numerical failure, then return without
395 // updating user state.
396 if (summary->termination_type == USER_ABORT ||
397 summary->termination_type == NUMERICAL_FAILURE) {
398 return;
399 }
400
Petter Strandmark76533b32012-09-04 22:08:50 -0700401 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700402
Keir Mierle8ebb0732012-04-30 23:09:08 -0700403 // Push the contiguous optimized parameters back to the user's parameters.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700404 reduced_program->StateVectorToParameterBlocks(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700405 reduced_program->CopyParameterBlockStateToUserState();
406
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700407 // Evaluate the final cost, residual vector and the jacobian
408 // matrix if requested by the user.
409 Evaluator::Evaluate(
410 original_program,
411 options.num_threads,
412 &summary->final_cost,
413 options.return_final_residuals ? &summary->final_residuals : NULL,
414 options.return_final_gradient ? &summary->final_gradient : NULL,
415 options.return_final_jacobian ? &summary->final_jacobian : NULL);
416
Keir Mierle6196cba2012-06-18 11:03:40 -0700417 // Ensure the program state is set to the user parameters on the way out.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700418 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700419 // Stick a fork in it, we're done.
Petter Strandmark76533b32012-09-04 22:08:50 -0700420 summary->postprocessor_time_in_seconds =
421 WallTimeInSeconds() - post_process_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700422}
423
424// Strips varying parameters and residuals, maintaining order, and updating
425// num_eliminate_blocks.
426bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
427 int* num_eliminate_blocks,
Markus Moll8de78db2012-07-14 15:49:11 +0200428 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700429 string* error) {
430 int original_num_eliminate_blocks = *num_eliminate_blocks;
431 vector<ParameterBlock*>* parameter_blocks =
432 program->mutable_parameter_blocks();
433
Markus Moll8de78db2012-07-14 15:49:11 +0200434 scoped_array<double> residual_block_evaluate_scratch;
435 if (fixed_cost != NULL) {
Keir Mierlec161a9d2012-07-18 14:01:48 -0700436 residual_block_evaluate_scratch.reset(
Markus Moll8de78db2012-07-14 15:49:11 +0200437 new double[program->MaxScratchDoublesNeededForEvaluate()]);
438 *fixed_cost = 0.0;
439 }
440
Keir Mierle8ebb0732012-04-30 23:09:08 -0700441 // Mark all the parameters as unused. Abuse the index member of the parameter
442 // blocks for the marking.
443 for (int i = 0; i < parameter_blocks->size(); ++i) {
444 (*parameter_blocks)[i]->set_index(-1);
445 }
446
447 // Filter out residual that have all-constant parameters, and mark all the
448 // parameter blocks that appear in residuals.
449 {
450 vector<ResidualBlock*>* residual_blocks =
451 program->mutable_residual_blocks();
452 int j = 0;
453 for (int i = 0; i < residual_blocks->size(); ++i) {
454 ResidualBlock* residual_block = (*residual_blocks)[i];
455 int num_parameter_blocks = residual_block->NumParameterBlocks();
456
457 // Determine if the residual block is fixed, and also mark varying
458 // parameters that appear in the residual block.
459 bool all_constant = true;
460 for (int k = 0; k < num_parameter_blocks; k++) {
461 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
462 if (!parameter_block->IsConstant()) {
463 all_constant = false;
464 parameter_block->set_index(1);
465 }
466 }
467
468 if (!all_constant) {
469 (*residual_blocks)[j++] = (*residual_blocks)[i];
Markus Moll8de78db2012-07-14 15:49:11 +0200470 } else if (fixed_cost != NULL) {
471 // The residual is constant and will be removed, so its cost is
472 // added to the variable fixed_cost.
473 double cost = 0.0;
474 if (!residual_block->Evaluate(
475 &cost, NULL, NULL, residual_block_evaluate_scratch.get())) {
476 *error = StringPrintf("Evaluation of the residual %d failed during "
477 "removal of fixed residual blocks.", i);
478 return false;
479 }
480 *fixed_cost += cost;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700481 }
482 }
483 residual_blocks->resize(j);
484 }
485
486 // Filter out unused or fixed parameter blocks, and update
487 // num_eliminate_blocks as necessary.
488 {
489 vector<ParameterBlock*>* parameter_blocks =
490 program->mutable_parameter_blocks();
491 int j = 0;
492 for (int i = 0; i < parameter_blocks->size(); ++i) {
493 ParameterBlock* parameter_block = (*parameter_blocks)[i];
494 if (parameter_block->index() == 1) {
495 (*parameter_blocks)[j++] = parameter_block;
496 } else if (i < original_num_eliminate_blocks) {
497 (*num_eliminate_blocks)--;
498 }
499 }
500 parameter_blocks->resize(j);
501 }
502
503 CHECK(((program->NumResidualBlocks() == 0) &&
504 (program->NumParameterBlocks() == 0)) ||
505 ((program->NumResidualBlocks() != 0) &&
506 (program->NumParameterBlocks() != 0)))
507 << "Congratulations, you found a bug in Ceres. Please report it.";
508 return true;
509}
510
511Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
512 ProblemImpl* problem_impl,
Markus Moll8de78db2012-07-14 15:49:11 +0200513 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700514 string* error) {
515 Program* original_program = problem_impl->mutable_program();
516 scoped_ptr<Program> transformed_program(new Program(*original_program));
517
518 if (options->ordering_type == USER &&
519 !ApplyUserOrdering(*problem_impl,
520 options->ordering,
521 transformed_program.get(),
522 error)) {
523 return NULL;
524 }
525
526 if (options->ordering_type == SCHUR && options->num_eliminate_blocks != 0) {
527 *error = "Can't specify SCHUR ordering and num_eliminate_blocks "
528 "at the same time; SCHUR ordering determines "
529 "num_eliminate_blocks automatically.";
530 return NULL;
531 }
532
533 if (options->ordering_type == SCHUR && options->ordering.size() != 0) {
534 *error = "Can't specify SCHUR ordering type and the ordering "
535 "vector at the same time; SCHUR ordering determines "
536 "a suitable parameter ordering automatically.";
537 return NULL;
538 }
539
540 int num_eliminate_blocks = options->num_eliminate_blocks;
541
542 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
543 &num_eliminate_blocks,
Markus Moll8de78db2012-07-14 15:49:11 +0200544 fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700545 error)) {
546 return NULL;
547 }
548
549 if (transformed_program->NumParameterBlocks() == 0) {
550 LOG(WARNING) << "No varying parameter blocks to optimize; "
551 << "bailing early.";
552 return transformed_program.release();
553 }
554
555 if (options->ordering_type == SCHUR) {
556 vector<ParameterBlock*> schur_ordering;
557 num_eliminate_blocks = ComputeSchurOrdering(*transformed_program,
558 &schur_ordering);
559 CHECK_EQ(schur_ordering.size(), transformed_program->NumParameterBlocks())
560 << "Congratulations, you found a Ceres bug! Please report this error "
561 << "to the developers.";
562
563 // Replace the transformed program's ordering with the schur ordering.
564 swap(*transformed_program->mutable_parameter_blocks(), schur_ordering);
565 }
566 options->num_eliminate_blocks = num_eliminate_blocks;
567 CHECK_GE(options->num_eliminate_blocks, 0)
568 << "Congratulations, you found a Ceres bug! Please report this error "
569 << "to the developers.";
570
571 // Since the transformed program is the "active" program, and it is mutated,
572 // update the parameter offsets and indices.
573 transformed_program->SetParameterOffsetsAndIndex();
574 return transformed_program.release();
575}
576
577LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
578 string* error) {
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700579 if (options->trust_region_strategy_type == DOGLEG) {
580 if (options->linear_solver_type == ITERATIVE_SCHUR ||
581 options->linear_solver_type == CGNR) {
582 *error = "DOGLEG only supports exact factorization based linear "
583 "solvers. If you want to use an iterative solver please "
584 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
585 return NULL;
586 }
587 }
588
Keir Mierle8ebb0732012-04-30 23:09:08 -0700589#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -0700590 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
591 options->sparse_linear_algebra_library == SUITE_SPARSE) {
592 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
593 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700594 return NULL;
595 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700596#endif
597
598#ifdef CERES_NO_CXSPARSE
599 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
600 options->sparse_linear_algebra_library == CX_SPARSE) {
601 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
602 "CXSparse was not enabled when Ceres was built.";
603 return NULL;
604 }
605#endif
606
Keir Mierle8ebb0732012-04-30 23:09:08 -0700607
608 if (options->linear_solver_max_num_iterations <= 0) {
609 *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
610 return NULL;
611 }
612 if (options->linear_solver_min_num_iterations <= 0) {
613 *error = "Solver::Options::linear_solver_min_num_iterations is 0.";
614 return NULL;
615 }
616 if (options->linear_solver_min_num_iterations >
617 options->linear_solver_max_num_iterations) {
618 *error = "Solver::Options::linear_solver_min_num_iterations > "
619 "Solver::Options::linear_solver_max_num_iterations.";
620 return NULL;
621 }
622
623 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700624 linear_solver_options.min_num_iterations =
625 options->linear_solver_min_num_iterations;
626 linear_solver_options.max_num_iterations =
627 options->linear_solver_max_num_iterations;
628 linear_solver_options.type = options->linear_solver_type;
629 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700630 linear_solver_options.sparse_linear_algebra_library =
631 options->sparse_linear_algebra_library;
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700632 linear_solver_options.use_block_amd = options->use_block_amd;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700633
634#ifdef CERES_NO_SUITESPARSE
635 if (linear_solver_options.preconditioner_type == SCHUR_JACOBI) {
636 *error = "SCHUR_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700637 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700638 return NULL;
639 }
640
641 if (linear_solver_options.preconditioner_type == CLUSTER_JACOBI) {
642 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700643 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700644 return NULL;
645 }
646
647 if (linear_solver_options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
648 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700649 "Ceres with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700650 return NULL;
651 }
652#endif
653
654 linear_solver_options.num_threads = options->num_linear_solver_threads;
655 linear_solver_options.num_eliminate_blocks =
656 options->num_eliminate_blocks;
657
658 if ((linear_solver_options.num_eliminate_blocks == 0) &&
659 IsSchurType(linear_solver_options.type)) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700660#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
661 LOG(INFO) << "No elimination block remaining switching to DENSE_QR.";
662 linear_solver_options.type = DENSE_QR;
663#else
Keir Mierle8ebb0732012-04-30 23:09:08 -0700664 LOG(INFO) << "No elimination block remaining "
665 << "switching to SPARSE_NORMAL_CHOLESKY.";
666 linear_solver_options.type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700667#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700668 }
669
Sameer Agarwalb0518732012-05-29 00:27:57 -0700670#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle8ebb0732012-04-30 23:09:08 -0700671 if (linear_solver_options.type == SPARSE_SCHUR) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700672 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
673 "CXSparse was enabled when Ceres was compiled.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700674 return NULL;
675 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700676#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700677
678 // The matrix used for storing the dense Schur complement has a
679 // single lock guarding the whole matrix. Running the
680 // SchurComplementSolver with multiple threads leads to maximum
681 // contention and slowdown. If the problem is large enough to
682 // benefit from a multithreaded schur eliminator, you should be
683 // using a SPARSE_SCHUR solver anyways.
684 if ((linear_solver_options.num_threads > 1) &&
685 (linear_solver_options.type == DENSE_SCHUR)) {
686 LOG(WARNING) << "Warning: Solver::Options::num_linear_solver_threads = "
687 << options->num_linear_solver_threads
688 << " with DENSE_SCHUR will result in poor performance; "
689 << "switching to single-threaded.";
690 linear_solver_options.num_threads = 1;
691 }
692
693 options->linear_solver_type = linear_solver_options.type;
694 options->num_linear_solver_threads = linear_solver_options.num_threads;
695
696 return LinearSolver::Create(linear_solver_options);
697}
698
699bool SolverImpl::ApplyUserOrdering(const ProblemImpl& problem_impl,
700 vector<double*>& ordering,
701 Program* program,
702 string* error) {
703 if (ordering.size() != program->NumParameterBlocks()) {
704 *error = StringPrintf("User specified ordering does not have the same "
705 "number of parameters as the problem. The problem"
706 "has %d blocks while the ordering has %ld blocks.",
707 program->NumParameterBlocks(),
708 ordering.size());
709 return false;
710 }
711
712 // Ensure that there are no duplicates in the user's ordering.
713 {
714 vector<double*> ordering_copy(ordering);
715 sort(ordering_copy.begin(), ordering_copy.end());
716 if (unique(ordering_copy.begin(), ordering_copy.end())
717 != ordering_copy.end()) {
718 *error = "User specified ordering contains duplicates.";
719 return false;
720 }
721 }
722
723 vector<ParameterBlock*>* parameter_blocks =
724 program->mutable_parameter_blocks();
725
726 fill(parameter_blocks->begin(),
727 parameter_blocks->end(),
728 static_cast<ParameterBlock*>(NULL));
729
730 const ProblemImpl::ParameterMap& parameter_map = problem_impl.parameter_map();
731 for (int i = 0; i < ordering.size(); ++i) {
732 ProblemImpl::ParameterMap::const_iterator it =
733 parameter_map.find(ordering[i]);
734 if (it == parameter_map.end()) {
735 *error = StringPrintf("User specified ordering contains a pointer "
736 "to a double that is not a parameter block in the "
737 "problem. The invalid double is at position %d "
738 " in options.ordering.", i);
739 return false;
740 }
741 (*parameter_blocks)[i] = it->second;
742 }
743 return true;
744}
745
746// Find the minimum index of any parameter block to the given residual.
747// Parameter blocks that have indices greater than num_eliminate_blocks are
748// considered to have an index equal to num_eliminate_blocks.
749int MinParameterBlock(const ResidualBlock* residual_block,
750 int num_eliminate_blocks) {
751 int min_parameter_block_position = num_eliminate_blocks;
752 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
753 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -0700754 if (!parameter_block->IsConstant()) {
755 CHECK_NE(parameter_block->index(), -1)
756 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
757 << "This is a Ceres bug; please contact the developers!";
758 min_parameter_block_position = std::min(parameter_block->index(),
759 min_parameter_block_position);
760 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700761 }
762 return min_parameter_block_position;
763}
764
765// Reorder the residuals for program, if necessary, so that the residuals
766// involving each E block occur together. This is a necessary condition for the
767// Schur eliminator, which works on these "row blocks" in the jacobian.
768bool SolverImpl::MaybeReorderResidualBlocks(const Solver::Options& options,
769 Program* program,
770 string* error) {
771 // Only Schur types require the lexicographic reordering.
772 if (!IsSchurType(options.linear_solver_type)) {
773 return true;
774 }
775
776 CHECK_NE(0, options.num_eliminate_blocks)
777 << "Congratulations, you found a Ceres bug! Please report this error "
778 << "to the developers.";
779
780 // Create a histogram of the number of residuals for each E block. There is an
781 // extra bucket at the end to catch all non-eliminated F blocks.
782 vector<int> residual_blocks_per_e_block(options.num_eliminate_blocks + 1);
783 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
784 vector<int> min_position_per_residual(residual_blocks->size());
785 for (int i = 0; i < residual_blocks->size(); ++i) {
786 ResidualBlock* residual_block = (*residual_blocks)[i];
787 int position = MinParameterBlock(residual_block,
788 options.num_eliminate_blocks);
789 min_position_per_residual[i] = position;
790 DCHECK_LE(position, options.num_eliminate_blocks);
791 residual_blocks_per_e_block[position]++;
792 }
793
794 // Run a cumulative sum on the histogram, to obtain offsets to the start of
795 // each histogram bucket (where each bucket is for the residuals for that
796 // E-block).
797 vector<int> offsets(options.num_eliminate_blocks + 1);
798 std::partial_sum(residual_blocks_per_e_block.begin(),
799 residual_blocks_per_e_block.end(),
800 offsets.begin());
801 CHECK_EQ(offsets.back(), residual_blocks->size())
802 << "Congratulations, you found a Ceres bug! Please report this error "
803 << "to the developers.";
804
805 CHECK(find(residual_blocks_per_e_block.begin(),
806 residual_blocks_per_e_block.end() - 1, 0) !=
807 residual_blocks_per_e_block.end())
808 << "Congratulations, you found a Ceres bug! Please report this error "
809 << "to the developers.";
810
811 // Fill in each bucket with the residual blocks for its corresponding E block.
812 // Each bucket is individually filled from the back of the bucket to the front
813 // of the bucket. The filling order among the buckets is dictated by the
814 // residual blocks. This loop uses the offsets as counters; subtracting one
815 // from each offset as a residual block is placed in the bucket. When the
816 // filling is finished, the offset pointerts should have shifted down one
817 // entry (this is verified below).
818 vector<ResidualBlock*> reordered_residual_blocks(
819 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
820 for (int i = 0; i < residual_blocks->size(); ++i) {
821 int bucket = min_position_per_residual[i];
822
823 // Decrement the cursor, which should now point at the next empty position.
824 offsets[bucket]--;
825
826 // Sanity.
827 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
828 << "Congratulations, you found a Ceres bug! Please report this error "
829 << "to the developers.";
830
831 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
832 }
833
834 // Sanity check #1: The difference in bucket offsets should match the
835 // histogram sizes.
836 for (int i = 0; i < options.num_eliminate_blocks; ++i) {
837 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
838 << "Congratulations, you found a Ceres bug! Please report this error "
839 << "to the developers.";
840 }
841 // Sanity check #2: No NULL's left behind.
842 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
843 CHECK(reordered_residual_blocks[i] != NULL)
844 << "Congratulations, you found a Ceres bug! Please report this error "
845 << "to the developers.";
846 }
847
848 // Now that the residuals are collected by E block, swap them in place.
849 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
850 return true;
851}
852
853Evaluator* SolverImpl::CreateEvaluator(const Solver::Options& options,
854 Program* program,
855 string* error) {
856 Evaluator::Options evaluator_options;
857 evaluator_options.linear_solver_type = options.linear_solver_type;
858 evaluator_options.num_eliminate_blocks = options.num_eliminate_blocks;
859 evaluator_options.num_threads = options.num_threads;
860 return Evaluator::Create(evaluator_options, program, error);
861}
862
863} // namespace internal
864} // namespace ceres