blob: 982fb40901aef59e7ae7e46396ecbb272067db95 [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
33#include <iostream> // NOLINT
34#include <numeric>
35#include "ceres/evaluator.h"
36#include "ceres/gradient_checking_cost_function.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070037#include "ceres/iteration_callback.h"
38#include "ceres/levenberg_marquardt_strategy.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070039#include "ceres/linear_solver.h"
40#include "ceres/map_util.h"
41#include "ceres/minimizer.h"
42#include "ceres/parameter_block.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070043#include "ceres/problem.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070044#include "ceres/problem_impl.h"
45#include "ceres/program.h"
46#include "ceres/residual_block.h"
47#include "ceres/schur_ordering.h"
48#include "ceres/stringprintf.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070049#include "ceres/trust_region_minimizer.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070050
51namespace ceres {
52namespace internal {
53namespace {
54
55void EvaluateCostAndResiduals(ProblemImpl* problem_impl,
56 double* cost,
57 vector<double>* residuals) {
58 CHECK_NOTNULL(cost);
59 Program* program = CHECK_NOTNULL(problem_impl)->mutable_program();
60 if (residuals != NULL) {
61 residuals->resize(program->NumResiduals());
62 program->Evaluate(cost, &(*residuals)[0]);
63 } else {
64 program->Evaluate(cost, NULL);
65 }
66}
67
68// Callback for updating the user's parameter blocks. Updates are only
69// done if the step is successful.
70class StateUpdatingCallback : public IterationCallback {
71 public:
72 StateUpdatingCallback(Program* program, double* parameters)
73 : program_(program), parameters_(parameters) {}
74
75 CallbackReturnType operator()(const IterationSummary& summary) {
76 if (summary.step_is_successful) {
77 program_->StateVectorToParameterBlocks(parameters_);
78 program_->CopyParameterBlockStateToUserState();
79 }
80 return SOLVER_CONTINUE;
81 }
82
83 private:
84 Program* program_;
85 double* parameters_;
86};
87
88// Callback for logging the state of the minimizer to STDERR or STDOUT
89// depending on the user's preferences and logging level.
90class LoggingCallback : public IterationCallback {
91 public:
92 explicit LoggingCallback(bool log_to_stdout)
93 : log_to_stdout_(log_to_stdout) {}
94
95 ~LoggingCallback() {}
96
97 CallbackReturnType operator()(const IterationSummary& summary) {
98 const char* kReportRowFormat =
99 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -0700100 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700101 string output = StringPrintf(kReportRowFormat,
102 summary.iteration,
103 summary.cost,
104 summary.cost_change,
105 summary.gradient_max_norm,
106 summary.step_norm,
107 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700108 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -0700109 summary.linear_solver_iterations,
110 summary.iteration_time_in_seconds,
111 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700112 if (log_to_stdout_) {
113 cout << output << endl;
114 } else {
115 VLOG(1) << output;
116 }
117 return SOLVER_CONTINUE;
118 }
119
120 private:
121 const bool log_to_stdout_;
122};
123
124} // namespace
125
126void SolverImpl::Minimize(const Solver::Options& options,
127 Program* program,
128 Evaluator* evaluator,
129 LinearSolver* linear_solver,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700130 double* parameters,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700131 Solver::Summary* summary) {
132 Minimizer::Options minimizer_options(options);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700133 LoggingCallback logging_callback(options.minimizer_progress_to_stdout);
134 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700135 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
136 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700137 }
138
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700139 StateUpdatingCallback updating_callback(program, parameters);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700140 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700141 // This must get pushed to the front of the callbacks so that it is run
142 // before any of the user callbacks.
143 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
144 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700145 }
146
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700147 minimizer_options.evaluator = evaluator;
148 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
149 minimizer_options.jacobian = jacobian.get();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700150
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700151 TrustRegionStrategy::Options trust_region_strategy_options;
152 trust_region_strategy_options.linear_solver = linear_solver;
153 trust_region_strategy_options.initial_radius =
154 options.initial_trust_region_radius;
155 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
156 trust_region_strategy_options.lm_min_diagonal = options.lm_min_diagonal;
157 trust_region_strategy_options.lm_max_diagonal = options.lm_max_diagonal;
158 trust_region_strategy_options.trust_region_strategy_type =
159 options.trust_region_strategy_type;
160 scoped_ptr<TrustRegionStrategy> strategy(
161 TrustRegionStrategy::Create(trust_region_strategy_options));
162 minimizer_options.trust_region_strategy = strategy.get();
163
164 TrustRegionMinimizer minimizer;
165 time_t minimizer_start_time = time(NULL);
166 minimizer.Minimize(minimizer_options, parameters, summary);
167 summary->minimizer_time_in_seconds = time(NULL) - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700168}
169
170void SolverImpl::Solve(const Solver::Options& original_options,
171 Problem* problem,
172 Solver::Summary* summary) {
Sameer Agarwalfa015192012-06-11 14:21:42 -0700173 time_t solver_start_time = time(NULL);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700174 Solver::Options options(original_options);
175
176#ifndef CERES_USE_OPENMP
177 if (options.num_threads > 1) {
178 LOG(WARNING)
179 << "OpenMP support is not compiled into this binary; "
180 << "only options.num_threads=1 is supported. Switching"
181 << "to single threaded mode.";
182 options.num_threads = 1;
183 }
184 if (options.num_linear_solver_threads > 1) {
185 LOG(WARNING)
186 << "OpenMP support is not compiled into this binary; "
187 << "only options.num_linear_solver_threads=1 is supported. Switching"
188 << "to single threaded mode.";
189 options.num_linear_solver_threads = 1;
190 }
191#endif
192
193 // Reset the summary object to its default values;
194 *CHECK_NOTNULL(summary) = Solver::Summary();
195 summary->linear_solver_type_given = options.linear_solver_type;
196 summary->num_eliminate_blocks_given = original_options.num_eliminate_blocks;
197 summary->num_threads_given = original_options.num_threads;
198 summary->num_linear_solver_threads_given =
199 original_options.num_linear_solver_threads;
200 summary->ordering_type = original_options.ordering_type;
201
202 ProblemImpl* problem_impl = CHECK_NOTNULL(problem)->problem_impl_.get();
203
204 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
205 summary->num_parameters = problem_impl->NumParameters();
206 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
207 summary->num_residuals = problem_impl->NumResiduals();
208
209 summary->num_threads_used = options.num_threads;
Sameer Agarwal97fb6d92012-06-17 10:08:19 -0700210 summary->sparse_linear_algebra_library =
211 options.sparse_linear_algebra_library;
212 summary->trust_region_strategy_type = options.trust_region_strategy_type;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700213
Keir Mierle57d91f52012-06-17 23:45:23 -0700214 // Ensure the program state is set to the user parameters.
215 Program* program = CHECK_NOTNULL(problem_impl)->mutable_program();
216 if (!program->CopyUserStateToParameterBlocks()) {
217 // This can only happen if there was a numerical problem updating the local
218 // jacobians. Indicate as such and fail out.
Sameer Agarwal4a6cc1c2012-06-18 10:20:08 -0700219 summary->termination_type = NUMERICAL_FAILURE;
Keir Mierle57d91f52012-06-17 23:45:23 -0700220 summary->error = "Local parameterization failure.";
221 return;
222 }
223
Keir Mierle8ebb0732012-04-30 23:09:08 -0700224 // Evaluate the initial cost and residual vector (if needed). The
225 // initial cost needs to be computed on the original unpreprocessed
226 // problem, as it is used to determine the value of the "fixed" part
227 // of the objective function after the problem has undergone
228 // reduction. Also the initial residuals are in the order in which
229 // the user added the ResidualBlocks to the optimization problem.
230 EvaluateCostAndResiduals(problem_impl,
231 &summary->initial_cost,
232 options.return_initial_residuals
233 ? &summary->initial_residuals
234 : NULL);
235
236 // If the user requests gradient checking, construct a new
237 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
238 // GradientCheckingCostFunction and replacing problem_impl with
239 // gradient_checking_problem_impl.
240 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
241 if (options.check_gradients) {
242 VLOG(1) << "Checking Gradients";
243 gradient_checking_problem_impl.reset(
244 CreateGradientCheckingProblemImpl(
245 problem_impl,
246 options.numeric_derivative_relative_step_size,
247 options.gradient_check_relative_precision));
248
249 // From here on, problem_impl will point to the GradientChecking version.
250 problem_impl = gradient_checking_problem_impl.get();
251 }
252
253 // Create the three objects needed to minimize: the transformed program, the
254 // evaluator, and the linear solver.
255
256 scoped_ptr<Program> reduced_program(
257 CreateReducedProgram(&options, problem_impl, &summary->error));
258 if (reduced_program == NULL) {
259 return;
260 }
261
262 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
263 summary->num_parameters_reduced = reduced_program->NumParameters();
264 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
265 summary->num_residuals_reduced = reduced_program->NumResiduals();
266
267 scoped_ptr<LinearSolver>
268 linear_solver(CreateLinearSolver(&options, &summary->error));
269 summary->linear_solver_type_used = options.linear_solver_type;
270 summary->preconditioner_type = options.preconditioner_type;
271 summary->num_eliminate_blocks_used = options.num_eliminate_blocks;
272 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
273
274 if (linear_solver == NULL) {
275 return;
276 }
277
278 if (!MaybeReorderResidualBlocks(options,
279 reduced_program.get(),
280 &summary->error)) {
281 return;
282 }
283
284 scoped_ptr<Evaluator> evaluator(
285 CreateEvaluator(options, reduced_program.get(), &summary->error));
286 if (evaluator == NULL) {
287 return;
288 }
289
290 // The optimizer works on contiguous parameter vectors; allocate some.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700291 Vector parameters(reduced_program->NumParameters());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700292
293 // Collect the discontiguous parameters into a contiguous state vector.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700294 reduced_program->ParameterBlocksToStateVector(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700295
Sameer Agarwalfa015192012-06-11 14:21:42 -0700296 time_t minimizer_start_time = time(NULL);
297 summary->preprocessor_time_in_seconds =
298 minimizer_start_time - solver_start_time;
299
Keir Mierle8ebb0732012-04-30 23:09:08 -0700300 // Run the optimization.
301 Minimize(options,
302 reduced_program.get(),
303 evaluator.get(),
304 linear_solver.get(),
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700305 parameters.data(),
Keir Mierle8ebb0732012-04-30 23:09:08 -0700306 summary);
307
308 // If the user aborted mid-optimization or the optimization
309 // terminated because of a numerical failure, then return without
310 // updating user state.
311 if (summary->termination_type == USER_ABORT ||
312 summary->termination_type == NUMERICAL_FAILURE) {
313 return;
314 }
315
Sameer Agarwalfa015192012-06-11 14:21:42 -0700316 time_t post_process_start_time = time(NULL);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700317 // Push the contiguous optimized parameters back to the user's parameters.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700318 reduced_program->StateVectorToParameterBlocks(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700319 reduced_program->CopyParameterBlockStateToUserState();
320
321 // Return the final cost and residuals for the original problem.
322 EvaluateCostAndResiduals(problem->problem_impl_.get(),
323 &summary->final_cost,
324 options.return_final_residuals
325 ? &summary->final_residuals
326 : NULL);
327
328 // Stick a fork in it, we're done.
Sameer Agarwalfa015192012-06-11 14:21:42 -0700329 time_t post_process_end_time = time(NULL);
330 summary->postprocessor_time_in_seconds =
331 post_process_end_time - post_process_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700332}
333
334// Strips varying parameters and residuals, maintaining order, and updating
335// num_eliminate_blocks.
336bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
337 int* num_eliminate_blocks,
338 string* error) {
339 int original_num_eliminate_blocks = *num_eliminate_blocks;
340 vector<ParameterBlock*>* parameter_blocks =
341 program->mutable_parameter_blocks();
342
343 // Mark all the parameters as unused. Abuse the index member of the parameter
344 // blocks for the marking.
345 for (int i = 0; i < parameter_blocks->size(); ++i) {
346 (*parameter_blocks)[i]->set_index(-1);
347 }
348
349 // Filter out residual that have all-constant parameters, and mark all the
350 // parameter blocks that appear in residuals.
351 {
352 vector<ResidualBlock*>* residual_blocks =
353 program->mutable_residual_blocks();
354 int j = 0;
355 for (int i = 0; i < residual_blocks->size(); ++i) {
356 ResidualBlock* residual_block = (*residual_blocks)[i];
357 int num_parameter_blocks = residual_block->NumParameterBlocks();
358
359 // Determine if the residual block is fixed, and also mark varying
360 // parameters that appear in the residual block.
361 bool all_constant = true;
362 for (int k = 0; k < num_parameter_blocks; k++) {
363 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
364 if (!parameter_block->IsConstant()) {
365 all_constant = false;
366 parameter_block->set_index(1);
367 }
368 }
369
370 if (!all_constant) {
371 (*residual_blocks)[j++] = (*residual_blocks)[i];
372 }
373 }
374 residual_blocks->resize(j);
375 }
376
377 // Filter out unused or fixed parameter blocks, and update
378 // num_eliminate_blocks as necessary.
379 {
380 vector<ParameterBlock*>* parameter_blocks =
381 program->mutable_parameter_blocks();
382 int j = 0;
383 for (int i = 0; i < parameter_blocks->size(); ++i) {
384 ParameterBlock* parameter_block = (*parameter_blocks)[i];
385 if (parameter_block->index() == 1) {
386 (*parameter_blocks)[j++] = parameter_block;
387 } else if (i < original_num_eliminate_blocks) {
388 (*num_eliminate_blocks)--;
389 }
390 }
391 parameter_blocks->resize(j);
392 }
393
394 CHECK(((program->NumResidualBlocks() == 0) &&
395 (program->NumParameterBlocks() == 0)) ||
396 ((program->NumResidualBlocks() != 0) &&
397 (program->NumParameterBlocks() != 0)))
398 << "Congratulations, you found a bug in Ceres. Please report it.";
399 return true;
400}
401
402Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
403 ProblemImpl* problem_impl,
404 string* error) {
405 Program* original_program = problem_impl->mutable_program();
406 scoped_ptr<Program> transformed_program(new Program(*original_program));
407
408 if (options->ordering_type == USER &&
409 !ApplyUserOrdering(*problem_impl,
410 options->ordering,
411 transformed_program.get(),
412 error)) {
413 return NULL;
414 }
415
416 if (options->ordering_type == SCHUR && options->num_eliminate_blocks != 0) {
417 *error = "Can't specify SCHUR ordering and num_eliminate_blocks "
418 "at the same time; SCHUR ordering determines "
419 "num_eliminate_blocks automatically.";
420 return NULL;
421 }
422
423 if (options->ordering_type == SCHUR && options->ordering.size() != 0) {
424 *error = "Can't specify SCHUR ordering type and the ordering "
425 "vector at the same time; SCHUR ordering determines "
426 "a suitable parameter ordering automatically.";
427 return NULL;
428 }
429
430 int num_eliminate_blocks = options->num_eliminate_blocks;
431
432 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
433 &num_eliminate_blocks,
434 error)) {
435 return NULL;
436 }
437
438 if (transformed_program->NumParameterBlocks() == 0) {
439 LOG(WARNING) << "No varying parameter blocks to optimize; "
440 << "bailing early.";
441 return transformed_program.release();
442 }
443
444 if (options->ordering_type == SCHUR) {
445 vector<ParameterBlock*> schur_ordering;
446 num_eliminate_blocks = ComputeSchurOrdering(*transformed_program,
447 &schur_ordering);
448 CHECK_EQ(schur_ordering.size(), transformed_program->NumParameterBlocks())
449 << "Congratulations, you found a Ceres bug! Please report this error "
450 << "to the developers.";
451
452 // Replace the transformed program's ordering with the schur ordering.
453 swap(*transformed_program->mutable_parameter_blocks(), schur_ordering);
454 }
455 options->num_eliminate_blocks = num_eliminate_blocks;
456 CHECK_GE(options->num_eliminate_blocks, 0)
457 << "Congratulations, you found a Ceres bug! Please report this error "
458 << "to the developers.";
459
460 // Since the transformed program is the "active" program, and it is mutated,
461 // update the parameter offsets and indices.
462 transformed_program->SetParameterOffsetsAndIndex();
463 return transformed_program.release();
464}
465
466LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
467 string* error) {
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700468 if (options->trust_region_strategy_type == DOGLEG) {
469 if (options->linear_solver_type == ITERATIVE_SCHUR ||
470 options->linear_solver_type == CGNR) {
471 *error = "DOGLEG only supports exact factorization based linear "
472 "solvers. If you want to use an iterative solver please "
473 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
474 return NULL;
475 }
476 }
477
Keir Mierle8ebb0732012-04-30 23:09:08 -0700478#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -0700479 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
480 options->sparse_linear_algebra_library == SUITE_SPARSE) {
481 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
482 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700483 return NULL;
484 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700485#endif
486
487#ifdef CERES_NO_CXSPARSE
488 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
489 options->sparse_linear_algebra_library == CX_SPARSE) {
490 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
491 "CXSparse was not enabled when Ceres was built.";
492 return NULL;
493 }
494#endif
495
Keir Mierle8ebb0732012-04-30 23:09:08 -0700496
497 if (options->linear_solver_max_num_iterations <= 0) {
498 *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
499 return NULL;
500 }
501 if (options->linear_solver_min_num_iterations <= 0) {
502 *error = "Solver::Options::linear_solver_min_num_iterations is 0.";
503 return NULL;
504 }
505 if (options->linear_solver_min_num_iterations >
506 options->linear_solver_max_num_iterations) {
507 *error = "Solver::Options::linear_solver_min_num_iterations > "
508 "Solver::Options::linear_solver_max_num_iterations.";
509 return NULL;
510 }
511
512 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700513 linear_solver_options.min_num_iterations =
514 options->linear_solver_min_num_iterations;
515 linear_solver_options.max_num_iterations =
516 options->linear_solver_max_num_iterations;
517 linear_solver_options.type = options->linear_solver_type;
518 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700519 linear_solver_options.sparse_linear_algebra_library =
520 options->sparse_linear_algebra_library;
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700521 linear_solver_options.use_block_amd = options->use_block_amd;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700522
523#ifdef CERES_NO_SUITESPARSE
524 if (linear_solver_options.preconditioner_type == SCHUR_JACOBI) {
525 *error = "SCHUR_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700526 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700527 return NULL;
528 }
529
530 if (linear_solver_options.preconditioner_type == CLUSTER_JACOBI) {
531 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700532 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700533 return NULL;
534 }
535
536 if (linear_solver_options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
537 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700538 "Ceres with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700539 return NULL;
540 }
541#endif
542
543 linear_solver_options.num_threads = options->num_linear_solver_threads;
544 linear_solver_options.num_eliminate_blocks =
545 options->num_eliminate_blocks;
546
547 if ((linear_solver_options.num_eliminate_blocks == 0) &&
548 IsSchurType(linear_solver_options.type)) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700549#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
550 LOG(INFO) << "No elimination block remaining switching to DENSE_QR.";
551 linear_solver_options.type = DENSE_QR;
552#else
Keir Mierle8ebb0732012-04-30 23:09:08 -0700553 LOG(INFO) << "No elimination block remaining "
554 << "switching to SPARSE_NORMAL_CHOLESKY.";
555 linear_solver_options.type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700556#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700557 }
558
Sameer Agarwalb0518732012-05-29 00:27:57 -0700559#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle8ebb0732012-04-30 23:09:08 -0700560 if (linear_solver_options.type == SPARSE_SCHUR) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700561 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
562 "CXSparse was enabled when Ceres was compiled.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700563 return NULL;
564 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700565#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700566
567 // The matrix used for storing the dense Schur complement has a
568 // single lock guarding the whole matrix. Running the
569 // SchurComplementSolver with multiple threads leads to maximum
570 // contention and slowdown. If the problem is large enough to
571 // benefit from a multithreaded schur eliminator, you should be
572 // using a SPARSE_SCHUR solver anyways.
573 if ((linear_solver_options.num_threads > 1) &&
574 (linear_solver_options.type == DENSE_SCHUR)) {
575 LOG(WARNING) << "Warning: Solver::Options::num_linear_solver_threads = "
576 << options->num_linear_solver_threads
577 << " with DENSE_SCHUR will result in poor performance; "
578 << "switching to single-threaded.";
579 linear_solver_options.num_threads = 1;
580 }
581
582 options->linear_solver_type = linear_solver_options.type;
583 options->num_linear_solver_threads = linear_solver_options.num_threads;
584
585 return LinearSolver::Create(linear_solver_options);
586}
587
588bool SolverImpl::ApplyUserOrdering(const ProblemImpl& problem_impl,
589 vector<double*>& ordering,
590 Program* program,
591 string* error) {
592 if (ordering.size() != program->NumParameterBlocks()) {
593 *error = StringPrintf("User specified ordering does not have the same "
594 "number of parameters as the problem. The problem"
595 "has %d blocks while the ordering has %ld blocks.",
596 program->NumParameterBlocks(),
597 ordering.size());
598 return false;
599 }
600
601 // Ensure that there are no duplicates in the user's ordering.
602 {
603 vector<double*> ordering_copy(ordering);
604 sort(ordering_copy.begin(), ordering_copy.end());
605 if (unique(ordering_copy.begin(), ordering_copy.end())
606 != ordering_copy.end()) {
607 *error = "User specified ordering contains duplicates.";
608 return false;
609 }
610 }
611
612 vector<ParameterBlock*>* parameter_blocks =
613 program->mutable_parameter_blocks();
614
615 fill(parameter_blocks->begin(),
616 parameter_blocks->end(),
617 static_cast<ParameterBlock*>(NULL));
618
619 const ProblemImpl::ParameterMap& parameter_map = problem_impl.parameter_map();
620 for (int i = 0; i < ordering.size(); ++i) {
621 ProblemImpl::ParameterMap::const_iterator it =
622 parameter_map.find(ordering[i]);
623 if (it == parameter_map.end()) {
624 *error = StringPrintf("User specified ordering contains a pointer "
625 "to a double that is not a parameter block in the "
626 "problem. The invalid double is at position %d "
627 " in options.ordering.", i);
628 return false;
629 }
630 (*parameter_blocks)[i] = it->second;
631 }
632 return true;
633}
634
635// Find the minimum index of any parameter block to the given residual.
636// Parameter blocks that have indices greater than num_eliminate_blocks are
637// considered to have an index equal to num_eliminate_blocks.
638int MinParameterBlock(const ResidualBlock* residual_block,
639 int num_eliminate_blocks) {
640 int min_parameter_block_position = num_eliminate_blocks;
641 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
642 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -0700643 if (!parameter_block->IsConstant()) {
644 CHECK_NE(parameter_block->index(), -1)
645 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
646 << "This is a Ceres bug; please contact the developers!";
647 min_parameter_block_position = std::min(parameter_block->index(),
648 min_parameter_block_position);
649 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700650 }
651 return min_parameter_block_position;
652}
653
654// Reorder the residuals for program, if necessary, so that the residuals
655// involving each E block occur together. This is a necessary condition for the
656// Schur eliminator, which works on these "row blocks" in the jacobian.
657bool SolverImpl::MaybeReorderResidualBlocks(const Solver::Options& options,
658 Program* program,
659 string* error) {
660 // Only Schur types require the lexicographic reordering.
661 if (!IsSchurType(options.linear_solver_type)) {
662 return true;
663 }
664
665 CHECK_NE(0, options.num_eliminate_blocks)
666 << "Congratulations, you found a Ceres bug! Please report this error "
667 << "to the developers.";
668
669 // Create a histogram of the number of residuals for each E block. There is an
670 // extra bucket at the end to catch all non-eliminated F blocks.
671 vector<int> residual_blocks_per_e_block(options.num_eliminate_blocks + 1);
672 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
673 vector<int> min_position_per_residual(residual_blocks->size());
674 for (int i = 0; i < residual_blocks->size(); ++i) {
675 ResidualBlock* residual_block = (*residual_blocks)[i];
676 int position = MinParameterBlock(residual_block,
677 options.num_eliminate_blocks);
678 min_position_per_residual[i] = position;
679 DCHECK_LE(position, options.num_eliminate_blocks);
680 residual_blocks_per_e_block[position]++;
681 }
682
683 // Run a cumulative sum on the histogram, to obtain offsets to the start of
684 // each histogram bucket (where each bucket is for the residuals for that
685 // E-block).
686 vector<int> offsets(options.num_eliminate_blocks + 1);
687 std::partial_sum(residual_blocks_per_e_block.begin(),
688 residual_blocks_per_e_block.end(),
689 offsets.begin());
690 CHECK_EQ(offsets.back(), residual_blocks->size())
691 << "Congratulations, you found a Ceres bug! Please report this error "
692 << "to the developers.";
693
694 CHECK(find(residual_blocks_per_e_block.begin(),
695 residual_blocks_per_e_block.end() - 1, 0) !=
696 residual_blocks_per_e_block.end())
697 << "Congratulations, you found a Ceres bug! Please report this error "
698 << "to the developers.";
699
700 // Fill in each bucket with the residual blocks for its corresponding E block.
701 // Each bucket is individually filled from the back of the bucket to the front
702 // of the bucket. The filling order among the buckets is dictated by the
703 // residual blocks. This loop uses the offsets as counters; subtracting one
704 // from each offset as a residual block is placed in the bucket. When the
705 // filling is finished, the offset pointerts should have shifted down one
706 // entry (this is verified below).
707 vector<ResidualBlock*> reordered_residual_blocks(
708 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
709 for (int i = 0; i < residual_blocks->size(); ++i) {
710 int bucket = min_position_per_residual[i];
711
712 // Decrement the cursor, which should now point at the next empty position.
713 offsets[bucket]--;
714
715 // Sanity.
716 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
717 << "Congratulations, you found a Ceres bug! Please report this error "
718 << "to the developers.";
719
720 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
721 }
722
723 // Sanity check #1: The difference in bucket offsets should match the
724 // histogram sizes.
725 for (int i = 0; i < options.num_eliminate_blocks; ++i) {
726 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
727 << "Congratulations, you found a Ceres bug! Please report this error "
728 << "to the developers.";
729 }
730 // Sanity check #2: No NULL's left behind.
731 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
732 CHECK(reordered_residual_blocks[i] != NULL)
733 << "Congratulations, you found a Ceres bug! Please report this error "
734 << "to the developers.";
735 }
736
737 // Now that the residuals are collected by E block, swap them in place.
738 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
739 return true;
740}
741
742Evaluator* SolverImpl::CreateEvaluator(const Solver::Options& options,
743 Program* program,
744 string* error) {
745 Evaluator::Options evaluator_options;
746 evaluator_options.linear_solver_type = options.linear_solver_type;
747 evaluator_options.num_eliminate_blocks = options.num_eliminate_blocks;
748 evaluator_options.num_threads = options.num_threads;
749 return Evaluator::Create(evaluator_options, program, error);
750}
751
752} // namespace internal
753} // namespace ceres