blob: 2802a75effecbca183d0763935a03932a0b1194b [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"
43#include "ceres/parameter_block.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070044#include "ceres/problem.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070045#include "ceres/problem_impl.h"
46#include "ceres/program.h"
47#include "ceres/residual_block.h"
48#include "ceres/schur_ordering.h"
49#include "ceres/stringprintf.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070050#include "ceres/trust_region_minimizer.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070051
52namespace ceres {
53namespace internal {
54namespace {
55
Keir Mierle8ebb0732012-04-30 23:09:08 -070056// Callback for updating the user's parameter blocks. Updates are only
57// done if the step is successful.
58class StateUpdatingCallback : public IterationCallback {
59 public:
60 StateUpdatingCallback(Program* program, double* parameters)
61 : program_(program), parameters_(parameters) {}
62
63 CallbackReturnType operator()(const IterationSummary& summary) {
64 if (summary.step_is_successful) {
65 program_->StateVectorToParameterBlocks(parameters_);
66 program_->CopyParameterBlockStateToUserState();
67 }
68 return SOLVER_CONTINUE;
69 }
70
71 private:
72 Program* program_;
73 double* parameters_;
74};
75
76// Callback for logging the state of the minimizer to STDERR or STDOUT
77// depending on the user's preferences and logging level.
78class LoggingCallback : public IterationCallback {
79 public:
80 explicit LoggingCallback(bool log_to_stdout)
81 : log_to_stdout_(log_to_stdout) {}
82
83 ~LoggingCallback() {}
84
85 CallbackReturnType operator()(const IterationSummary& summary) {
86 const char* kReportRowFormat =
87 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -070088 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -070089 string output = StringPrintf(kReportRowFormat,
90 summary.iteration,
91 summary.cost,
92 summary.cost_change,
93 summary.gradient_max_norm,
94 summary.step_norm,
95 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070096 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -070097 summary.linear_solver_iterations,
98 summary.iteration_time_in_seconds,
99 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700100 if (log_to_stdout_) {
101 cout << output << endl;
102 } else {
103 VLOG(1) << output;
104 }
105 return SOLVER_CONTINUE;
106 }
107
108 private:
109 const bool log_to_stdout_;
110};
111
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700112// Basic callback to record the execution of the solver to a file for
113// offline analysis.
114class FileLoggingCallback : public IterationCallback {
115 public:
116 explicit FileLoggingCallback(const string& filename)
117 : fptr_(NULL) {
118 fptr_ = fopen(filename.c_str(), "w");
119 CHECK_NOTNULL(fptr_);
120 }
121
122 virtual ~FileLoggingCallback() {
123 if (fptr_ != NULL) {
124 fclose(fptr_);
125 }
126 }
127
128 virtual CallbackReturnType operator()(const IterationSummary& summary) {
129 fprintf(fptr_,
130 "%4d %e %e\n",
131 summary.iteration,
132 summary.cost,
133 summary.cumulative_time_in_seconds);
134 return SOLVER_CONTINUE;
135 }
136 private:
137 FILE* fptr_;
138};
139
Keir Mierle8ebb0732012-04-30 23:09:08 -0700140} // namespace
141
142void SolverImpl::Minimize(const Solver::Options& options,
143 Program* program,
144 Evaluator* evaluator,
145 LinearSolver* linear_solver,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700146 double* parameters,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700147 Solver::Summary* summary) {
148 Minimizer::Options minimizer_options(options);
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700149
150 // TODO(sameeragarwal): Add support for logging the configuration
151 // and more detailed stats.
152 scoped_ptr<IterationCallback> file_logging_callback;
153 if (!options.solver_log.empty()) {
154 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
155 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
156 file_logging_callback.get());
157 }
158
Keir Mierle8ebb0732012-04-30 23:09:08 -0700159 LoggingCallback logging_callback(options.minimizer_progress_to_stdout);
160 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700161 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
162 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700163 }
164
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700165 StateUpdatingCallback updating_callback(program, parameters);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700166 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700167 // This must get pushed to the front of the callbacks so that it is run
168 // before any of the user callbacks.
169 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
170 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700171 }
172
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700173 minimizer_options.evaluator = evaluator;
174 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
175 minimizer_options.jacobian = jacobian.get();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700176
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700177 TrustRegionStrategy::Options trust_region_strategy_options;
178 trust_region_strategy_options.linear_solver = linear_solver;
179 trust_region_strategy_options.initial_radius =
180 options.initial_trust_region_radius;
181 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
182 trust_region_strategy_options.lm_min_diagonal = options.lm_min_diagonal;
183 trust_region_strategy_options.lm_max_diagonal = options.lm_max_diagonal;
184 trust_region_strategy_options.trust_region_strategy_type =
185 options.trust_region_strategy_type;
Markus Moll51cf7cb2012-08-20 20:10:20 +0200186 trust_region_strategy_options.dogleg_type = options.dogleg_type;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700187 scoped_ptr<TrustRegionStrategy> strategy(
188 TrustRegionStrategy::Create(trust_region_strategy_options));
189 minimizer_options.trust_region_strategy = strategy.get();
190
191 TrustRegionMinimizer minimizer;
192 time_t minimizer_start_time = time(NULL);
193 minimizer.Minimize(minimizer_options, parameters, summary);
194 summary->minimizer_time_in_seconds = time(NULL) - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700195}
196
197void SolverImpl::Solve(const Solver::Options& original_options,
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700198 ProblemImpl* original_problem_impl,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700199 Solver::Summary* summary) {
Sameer Agarwalfa015192012-06-11 14:21:42 -0700200 time_t solver_start_time = time(NULL);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700201 Solver::Options options(original_options);
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700202 Program* original_program = original_problem_impl->mutable_program();
203 ProblemImpl* problem_impl = original_problem_impl;
204 // Reset the summary object to its default values.
205 *CHECK_NOTNULL(summary) = Solver::Summary();
206
Keir Mierle8ebb0732012-04-30 23:09:08 -0700207
208#ifndef CERES_USE_OPENMP
209 if (options.num_threads > 1) {
210 LOG(WARNING)
211 << "OpenMP support is not compiled into this binary; "
212 << "only options.num_threads=1 is supported. Switching"
213 << "to single threaded mode.";
214 options.num_threads = 1;
215 }
216 if (options.num_linear_solver_threads > 1) {
217 LOG(WARNING)
218 << "OpenMP support is not compiled into this binary; "
219 << "only options.num_linear_solver_threads=1 is supported. Switching"
220 << "to single threaded mode.";
221 options.num_linear_solver_threads = 1;
222 }
223#endif
224
Keir Mierle8ebb0732012-04-30 23:09:08 -0700225 summary->linear_solver_type_given = options.linear_solver_type;
226 summary->num_eliminate_blocks_given = original_options.num_eliminate_blocks;
227 summary->num_threads_given = original_options.num_threads;
228 summary->num_linear_solver_threads_given =
229 original_options.num_linear_solver_threads;
230 summary->ordering_type = original_options.ordering_type;
231
Keir Mierle8ebb0732012-04-30 23:09:08 -0700232 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
233 summary->num_parameters = problem_impl->NumParameters();
234 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
235 summary->num_residuals = problem_impl->NumResiduals();
236
237 summary->num_threads_used = options.num_threads;
Sameer Agarwal97fb6d92012-06-17 10:08:19 -0700238 summary->sparse_linear_algebra_library =
239 options.sparse_linear_algebra_library;
240 summary->trust_region_strategy_type = options.trust_region_strategy_type;
Sameer Agarwal1e289202012-08-21 18:00:54 -0700241 summary->dogleg_type = options.dogleg_type;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700242
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700243 // Evaluate the initial cost, residual vector and the jacobian
244 // matrix if requested by the user. The initial cost needs to be
245 // computed on the original unpreprocessed problem, as it is used to
246 // determine the value of the "fixed" part of the objective function
247 // after the problem has undergone reduction.
248 Evaluator::Evaluate(
249 original_program,
250 options.num_threads,
251 &(summary->initial_cost),
252 options.return_initial_residuals ? &summary->initial_residuals : NULL,
253 options.return_initial_gradient ? &summary->initial_gradient : NULL,
254 options.return_initial_jacobian ? &summary->initial_jacobian : NULL);
255 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700256
257 // If the user requests gradient checking, construct a new
258 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
259 // GradientCheckingCostFunction and replacing problem_impl with
260 // gradient_checking_problem_impl.
261 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
Keir Mierle6196cba2012-06-18 11:03:40 -0700262 // Save the original problem impl so we don't use the gradient
263 // checking one when computing the residuals.
Keir Mierle8ebb0732012-04-30 23:09:08 -0700264 if (options.check_gradients) {
265 VLOG(1) << "Checking Gradients";
266 gradient_checking_problem_impl.reset(
267 CreateGradientCheckingProblemImpl(
268 problem_impl,
269 options.numeric_derivative_relative_step_size,
270 options.gradient_check_relative_precision));
271
272 // From here on, problem_impl will point to the GradientChecking version.
273 problem_impl = gradient_checking_problem_impl.get();
274 }
275
276 // Create the three objects needed to minimize: the transformed program, the
277 // evaluator, and the linear solver.
278
Keir Mierle31c1e782012-08-17 16:16:32 -0700279 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
280 problem_impl,
281 &summary->fixed_cost,
282 &summary->error));
Keir Mierle8ebb0732012-04-30 23:09:08 -0700283 if (reduced_program == NULL) {
284 return;
285 }
286
287 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
288 summary->num_parameters_reduced = reduced_program->NumParameters();
289 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
290 summary->num_residuals_reduced = reduced_program->NumResiduals();
291
292 scoped_ptr<LinearSolver>
293 linear_solver(CreateLinearSolver(&options, &summary->error));
294 summary->linear_solver_type_used = options.linear_solver_type;
295 summary->preconditioner_type = options.preconditioner_type;
296 summary->num_eliminate_blocks_used = options.num_eliminate_blocks;
297 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
298
299 if (linear_solver == NULL) {
300 return;
301 }
302
303 if (!MaybeReorderResidualBlocks(options,
304 reduced_program.get(),
305 &summary->error)) {
306 return;
307 }
308
309 scoped_ptr<Evaluator> evaluator(
310 CreateEvaluator(options, reduced_program.get(), &summary->error));
311 if (evaluator == NULL) {
312 return;
313 }
314
315 // The optimizer works on contiguous parameter vectors; allocate some.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700316 Vector parameters(reduced_program->NumParameters());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700317
318 // Collect the discontiguous parameters into a contiguous state vector.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700319 reduced_program->ParameterBlocksToStateVector(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700320
Sameer Agarwalfa015192012-06-11 14:21:42 -0700321 time_t minimizer_start_time = time(NULL);
322 summary->preprocessor_time_in_seconds =
323 minimizer_start_time - solver_start_time;
324
Keir Mierle8ebb0732012-04-30 23:09:08 -0700325 // Run the optimization.
326 Minimize(options,
327 reduced_program.get(),
328 evaluator.get(),
329 linear_solver.get(),
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700330 parameters.data(),
Keir Mierle8ebb0732012-04-30 23:09:08 -0700331 summary);
332
333 // If the user aborted mid-optimization or the optimization
334 // terminated because of a numerical failure, then return without
335 // updating user state.
336 if (summary->termination_type == USER_ABORT ||
337 summary->termination_type == NUMERICAL_FAILURE) {
338 return;
339 }
340
Sameer Agarwalfa015192012-06-11 14:21:42 -0700341 time_t post_process_start_time = time(NULL);
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700342
Keir Mierle8ebb0732012-04-30 23:09:08 -0700343 // Push the contiguous optimized parameters back to the user's parameters.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700344 reduced_program->StateVectorToParameterBlocks(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700345 reduced_program->CopyParameterBlockStateToUserState();
346
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700347 // Evaluate the final cost, residual vector and the jacobian
348 // matrix if requested by the user.
349 Evaluator::Evaluate(
350 original_program,
351 options.num_threads,
352 &summary->final_cost,
353 options.return_final_residuals ? &summary->final_residuals : NULL,
354 options.return_final_gradient ? &summary->final_gradient : NULL,
355 options.return_final_jacobian ? &summary->final_jacobian : NULL);
356
Keir Mierle6196cba2012-06-18 11:03:40 -0700357 // Ensure the program state is set to the user parameters on the way out.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700358 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700359 // Stick a fork in it, we're done.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700360 summary->postprocessor_time_in_seconds = time(NULL) - post_process_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700361}
362
363// Strips varying parameters and residuals, maintaining order, and updating
364// num_eliminate_blocks.
365bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
366 int* num_eliminate_blocks,
Markus Moll8de78db2012-07-14 15:49:11 +0200367 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700368 string* error) {
369 int original_num_eliminate_blocks = *num_eliminate_blocks;
370 vector<ParameterBlock*>* parameter_blocks =
371 program->mutable_parameter_blocks();
372
Markus Moll8de78db2012-07-14 15:49:11 +0200373 scoped_array<double> residual_block_evaluate_scratch;
374 if (fixed_cost != NULL) {
Keir Mierlec161a9d2012-07-18 14:01:48 -0700375 residual_block_evaluate_scratch.reset(
Markus Moll8de78db2012-07-14 15:49:11 +0200376 new double[program->MaxScratchDoublesNeededForEvaluate()]);
377 *fixed_cost = 0.0;
378 }
379
Keir Mierle8ebb0732012-04-30 23:09:08 -0700380 // Mark all the parameters as unused. Abuse the index member of the parameter
381 // blocks for the marking.
382 for (int i = 0; i < parameter_blocks->size(); ++i) {
383 (*parameter_blocks)[i]->set_index(-1);
384 }
385
386 // Filter out residual that have all-constant parameters, and mark all the
387 // parameter blocks that appear in residuals.
388 {
389 vector<ResidualBlock*>* residual_blocks =
390 program->mutable_residual_blocks();
391 int j = 0;
392 for (int i = 0; i < residual_blocks->size(); ++i) {
393 ResidualBlock* residual_block = (*residual_blocks)[i];
394 int num_parameter_blocks = residual_block->NumParameterBlocks();
395
396 // Determine if the residual block is fixed, and also mark varying
397 // parameters that appear in the residual block.
398 bool all_constant = true;
399 for (int k = 0; k < num_parameter_blocks; k++) {
400 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
401 if (!parameter_block->IsConstant()) {
402 all_constant = false;
403 parameter_block->set_index(1);
404 }
405 }
406
407 if (!all_constant) {
408 (*residual_blocks)[j++] = (*residual_blocks)[i];
Markus Moll8de78db2012-07-14 15:49:11 +0200409 } else if (fixed_cost != NULL) {
410 // The residual is constant and will be removed, so its cost is
411 // added to the variable fixed_cost.
412 double cost = 0.0;
413 if (!residual_block->Evaluate(
414 &cost, NULL, NULL, residual_block_evaluate_scratch.get())) {
415 *error = StringPrintf("Evaluation of the residual %d failed during "
416 "removal of fixed residual blocks.", i);
417 return false;
418 }
419 *fixed_cost += cost;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700420 }
421 }
422 residual_blocks->resize(j);
423 }
424
425 // Filter out unused or fixed parameter blocks, and update
426 // num_eliminate_blocks as necessary.
427 {
428 vector<ParameterBlock*>* parameter_blocks =
429 program->mutable_parameter_blocks();
430 int j = 0;
431 for (int i = 0; i < parameter_blocks->size(); ++i) {
432 ParameterBlock* parameter_block = (*parameter_blocks)[i];
433 if (parameter_block->index() == 1) {
434 (*parameter_blocks)[j++] = parameter_block;
435 } else if (i < original_num_eliminate_blocks) {
436 (*num_eliminate_blocks)--;
437 }
438 }
439 parameter_blocks->resize(j);
440 }
441
442 CHECK(((program->NumResidualBlocks() == 0) &&
443 (program->NumParameterBlocks() == 0)) ||
444 ((program->NumResidualBlocks() != 0) &&
445 (program->NumParameterBlocks() != 0)))
446 << "Congratulations, you found a bug in Ceres. Please report it.";
447 return true;
448}
449
450Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
451 ProblemImpl* problem_impl,
Markus Moll8de78db2012-07-14 15:49:11 +0200452 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700453 string* error) {
454 Program* original_program = problem_impl->mutable_program();
455 scoped_ptr<Program> transformed_program(new Program(*original_program));
456
457 if (options->ordering_type == USER &&
458 !ApplyUserOrdering(*problem_impl,
459 options->ordering,
460 transformed_program.get(),
461 error)) {
462 return NULL;
463 }
464
465 if (options->ordering_type == SCHUR && options->num_eliminate_blocks != 0) {
466 *error = "Can't specify SCHUR ordering and num_eliminate_blocks "
467 "at the same time; SCHUR ordering determines "
468 "num_eliminate_blocks automatically.";
469 return NULL;
470 }
471
472 if (options->ordering_type == SCHUR && options->ordering.size() != 0) {
473 *error = "Can't specify SCHUR ordering type and the ordering "
474 "vector at the same time; SCHUR ordering determines "
475 "a suitable parameter ordering automatically.";
476 return NULL;
477 }
478
479 int num_eliminate_blocks = options->num_eliminate_blocks;
480
481 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
482 &num_eliminate_blocks,
Markus Moll8de78db2012-07-14 15:49:11 +0200483 fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700484 error)) {
485 return NULL;
486 }
487
488 if (transformed_program->NumParameterBlocks() == 0) {
489 LOG(WARNING) << "No varying parameter blocks to optimize; "
490 << "bailing early.";
491 return transformed_program.release();
492 }
493
494 if (options->ordering_type == SCHUR) {
495 vector<ParameterBlock*> schur_ordering;
496 num_eliminate_blocks = ComputeSchurOrdering(*transformed_program,
497 &schur_ordering);
498 CHECK_EQ(schur_ordering.size(), transformed_program->NumParameterBlocks())
499 << "Congratulations, you found a Ceres bug! Please report this error "
500 << "to the developers.";
501
502 // Replace the transformed program's ordering with the schur ordering.
503 swap(*transformed_program->mutable_parameter_blocks(), schur_ordering);
504 }
505 options->num_eliminate_blocks = num_eliminate_blocks;
506 CHECK_GE(options->num_eliminate_blocks, 0)
507 << "Congratulations, you found a Ceres bug! Please report this error "
508 << "to the developers.";
509
510 // Since the transformed program is the "active" program, and it is mutated,
511 // update the parameter offsets and indices.
512 transformed_program->SetParameterOffsetsAndIndex();
513 return transformed_program.release();
514}
515
516LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
517 string* error) {
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700518 if (options->trust_region_strategy_type == DOGLEG) {
519 if (options->linear_solver_type == ITERATIVE_SCHUR ||
520 options->linear_solver_type == CGNR) {
521 *error = "DOGLEG only supports exact factorization based linear "
522 "solvers. If you want to use an iterative solver please "
523 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
524 return NULL;
525 }
526 }
527
Keir Mierle8ebb0732012-04-30 23:09:08 -0700528#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -0700529 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
530 options->sparse_linear_algebra_library == SUITE_SPARSE) {
531 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
532 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700533 return NULL;
534 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700535#endif
536
537#ifdef CERES_NO_CXSPARSE
538 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
539 options->sparse_linear_algebra_library == CX_SPARSE) {
540 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
541 "CXSparse was not enabled when Ceres was built.";
542 return NULL;
543 }
544#endif
545
Keir Mierle8ebb0732012-04-30 23:09:08 -0700546
547 if (options->linear_solver_max_num_iterations <= 0) {
548 *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
549 return NULL;
550 }
551 if (options->linear_solver_min_num_iterations <= 0) {
552 *error = "Solver::Options::linear_solver_min_num_iterations is 0.";
553 return NULL;
554 }
555 if (options->linear_solver_min_num_iterations >
556 options->linear_solver_max_num_iterations) {
557 *error = "Solver::Options::linear_solver_min_num_iterations > "
558 "Solver::Options::linear_solver_max_num_iterations.";
559 return NULL;
560 }
561
562 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700563 linear_solver_options.min_num_iterations =
564 options->linear_solver_min_num_iterations;
565 linear_solver_options.max_num_iterations =
566 options->linear_solver_max_num_iterations;
567 linear_solver_options.type = options->linear_solver_type;
568 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700569 linear_solver_options.sparse_linear_algebra_library =
570 options->sparse_linear_algebra_library;
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700571 linear_solver_options.use_block_amd = options->use_block_amd;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700572
573#ifdef CERES_NO_SUITESPARSE
574 if (linear_solver_options.preconditioner_type == SCHUR_JACOBI) {
575 *error = "SCHUR_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700576 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700577 return NULL;
578 }
579
580 if (linear_solver_options.preconditioner_type == CLUSTER_JACOBI) {
581 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700582 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700583 return NULL;
584 }
585
586 if (linear_solver_options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
587 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700588 "Ceres with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700589 return NULL;
590 }
591#endif
592
593 linear_solver_options.num_threads = options->num_linear_solver_threads;
594 linear_solver_options.num_eliminate_blocks =
595 options->num_eliminate_blocks;
596
597 if ((linear_solver_options.num_eliminate_blocks == 0) &&
598 IsSchurType(linear_solver_options.type)) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700599#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
600 LOG(INFO) << "No elimination block remaining switching to DENSE_QR.";
601 linear_solver_options.type = DENSE_QR;
602#else
Keir Mierle8ebb0732012-04-30 23:09:08 -0700603 LOG(INFO) << "No elimination block remaining "
604 << "switching to SPARSE_NORMAL_CHOLESKY.";
605 linear_solver_options.type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700606#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700607 }
608
Sameer Agarwalb0518732012-05-29 00:27:57 -0700609#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle8ebb0732012-04-30 23:09:08 -0700610 if (linear_solver_options.type == SPARSE_SCHUR) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700611 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
612 "CXSparse was enabled when Ceres was compiled.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700613 return NULL;
614 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700615#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700616
617 // The matrix used for storing the dense Schur complement has a
618 // single lock guarding the whole matrix. Running the
619 // SchurComplementSolver with multiple threads leads to maximum
620 // contention and slowdown. If the problem is large enough to
621 // benefit from a multithreaded schur eliminator, you should be
622 // using a SPARSE_SCHUR solver anyways.
623 if ((linear_solver_options.num_threads > 1) &&
624 (linear_solver_options.type == DENSE_SCHUR)) {
625 LOG(WARNING) << "Warning: Solver::Options::num_linear_solver_threads = "
626 << options->num_linear_solver_threads
627 << " with DENSE_SCHUR will result in poor performance; "
628 << "switching to single-threaded.";
629 linear_solver_options.num_threads = 1;
630 }
631
632 options->linear_solver_type = linear_solver_options.type;
633 options->num_linear_solver_threads = linear_solver_options.num_threads;
634
635 return LinearSolver::Create(linear_solver_options);
636}
637
638bool SolverImpl::ApplyUserOrdering(const ProblemImpl& problem_impl,
639 vector<double*>& ordering,
640 Program* program,
641 string* error) {
642 if (ordering.size() != program->NumParameterBlocks()) {
643 *error = StringPrintf("User specified ordering does not have the same "
644 "number of parameters as the problem. The problem"
645 "has %d blocks while the ordering has %ld blocks.",
646 program->NumParameterBlocks(),
647 ordering.size());
648 return false;
649 }
650
651 // Ensure that there are no duplicates in the user's ordering.
652 {
653 vector<double*> ordering_copy(ordering);
654 sort(ordering_copy.begin(), ordering_copy.end());
655 if (unique(ordering_copy.begin(), ordering_copy.end())
656 != ordering_copy.end()) {
657 *error = "User specified ordering contains duplicates.";
658 return false;
659 }
660 }
661
662 vector<ParameterBlock*>* parameter_blocks =
663 program->mutable_parameter_blocks();
664
665 fill(parameter_blocks->begin(),
666 parameter_blocks->end(),
667 static_cast<ParameterBlock*>(NULL));
668
669 const ProblemImpl::ParameterMap& parameter_map = problem_impl.parameter_map();
670 for (int i = 0; i < ordering.size(); ++i) {
671 ProblemImpl::ParameterMap::const_iterator it =
672 parameter_map.find(ordering[i]);
673 if (it == parameter_map.end()) {
674 *error = StringPrintf("User specified ordering contains a pointer "
675 "to a double that is not a parameter block in the "
676 "problem. The invalid double is at position %d "
677 " in options.ordering.", i);
678 return false;
679 }
680 (*parameter_blocks)[i] = it->second;
681 }
682 return true;
683}
684
685// Find the minimum index of any parameter block to the given residual.
686// Parameter blocks that have indices greater than num_eliminate_blocks are
687// considered to have an index equal to num_eliminate_blocks.
688int MinParameterBlock(const ResidualBlock* residual_block,
689 int num_eliminate_blocks) {
690 int min_parameter_block_position = num_eliminate_blocks;
691 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
692 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -0700693 if (!parameter_block->IsConstant()) {
694 CHECK_NE(parameter_block->index(), -1)
695 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
696 << "This is a Ceres bug; please contact the developers!";
697 min_parameter_block_position = std::min(parameter_block->index(),
698 min_parameter_block_position);
699 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700700 }
701 return min_parameter_block_position;
702}
703
704// Reorder the residuals for program, if necessary, so that the residuals
705// involving each E block occur together. This is a necessary condition for the
706// Schur eliminator, which works on these "row blocks" in the jacobian.
707bool SolverImpl::MaybeReorderResidualBlocks(const Solver::Options& options,
708 Program* program,
709 string* error) {
710 // Only Schur types require the lexicographic reordering.
711 if (!IsSchurType(options.linear_solver_type)) {
712 return true;
713 }
714
715 CHECK_NE(0, options.num_eliminate_blocks)
716 << "Congratulations, you found a Ceres bug! Please report this error "
717 << "to the developers.";
718
719 // Create a histogram of the number of residuals for each E block. There is an
720 // extra bucket at the end to catch all non-eliminated F blocks.
721 vector<int> residual_blocks_per_e_block(options.num_eliminate_blocks + 1);
722 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
723 vector<int> min_position_per_residual(residual_blocks->size());
724 for (int i = 0; i < residual_blocks->size(); ++i) {
725 ResidualBlock* residual_block = (*residual_blocks)[i];
726 int position = MinParameterBlock(residual_block,
727 options.num_eliminate_blocks);
728 min_position_per_residual[i] = position;
729 DCHECK_LE(position, options.num_eliminate_blocks);
730 residual_blocks_per_e_block[position]++;
731 }
732
733 // Run a cumulative sum on the histogram, to obtain offsets to the start of
734 // each histogram bucket (where each bucket is for the residuals for that
735 // E-block).
736 vector<int> offsets(options.num_eliminate_blocks + 1);
737 std::partial_sum(residual_blocks_per_e_block.begin(),
738 residual_blocks_per_e_block.end(),
739 offsets.begin());
740 CHECK_EQ(offsets.back(), residual_blocks->size())
741 << "Congratulations, you found a Ceres bug! Please report this error "
742 << "to the developers.";
743
744 CHECK(find(residual_blocks_per_e_block.begin(),
745 residual_blocks_per_e_block.end() - 1, 0) !=
746 residual_blocks_per_e_block.end())
747 << "Congratulations, you found a Ceres bug! Please report this error "
748 << "to the developers.";
749
750 // Fill in each bucket with the residual blocks for its corresponding E block.
751 // Each bucket is individually filled from the back of the bucket to the front
752 // of the bucket. The filling order among the buckets is dictated by the
753 // residual blocks. This loop uses the offsets as counters; subtracting one
754 // from each offset as a residual block is placed in the bucket. When the
755 // filling is finished, the offset pointerts should have shifted down one
756 // entry (this is verified below).
757 vector<ResidualBlock*> reordered_residual_blocks(
758 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
759 for (int i = 0; i < residual_blocks->size(); ++i) {
760 int bucket = min_position_per_residual[i];
761
762 // Decrement the cursor, which should now point at the next empty position.
763 offsets[bucket]--;
764
765 // Sanity.
766 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
767 << "Congratulations, you found a Ceres bug! Please report this error "
768 << "to the developers.";
769
770 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
771 }
772
773 // Sanity check #1: The difference in bucket offsets should match the
774 // histogram sizes.
775 for (int i = 0; i < options.num_eliminate_blocks; ++i) {
776 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
777 << "Congratulations, you found a Ceres bug! Please report this error "
778 << "to the developers.";
779 }
780 // Sanity check #2: No NULL's left behind.
781 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
782 CHECK(reordered_residual_blocks[i] != NULL)
783 << "Congratulations, you found a Ceres bug! Please report this error "
784 << "to the developers.";
785 }
786
787 // Now that the residuals are collected by E block, swap them in place.
788 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
789 return true;
790}
791
792Evaluator* SolverImpl::CreateEvaluator(const Solver::Options& options,
793 Program* program,
794 string* error) {
795 Evaluator::Options evaluator_options;
796 evaluator_options.linear_solver_type = options.linear_solver_type;
797 evaluator_options.num_eliminate_blocks = options.num_eliminate_blocks;
798 evaluator_options.num_threads = options.num_threads;
799 return Evaluator::Create(evaluator_options, program, error);
800}
801
802} // namespace internal
803} // namespace ceres