blob: 8e7253726fa4ab7279e7704e1574f698cd434271 [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
Keir Mierle8ebb0732012-04-30 23:09:08 -070055// Callback for updating the user's parameter blocks. Updates are only
56// done if the step is successful.
57class StateUpdatingCallback : public IterationCallback {
58 public:
59 StateUpdatingCallback(Program* program, double* parameters)
60 : program_(program), parameters_(parameters) {}
61
62 CallbackReturnType operator()(const IterationSummary& summary) {
63 if (summary.step_is_successful) {
64 program_->StateVectorToParameterBlocks(parameters_);
65 program_->CopyParameterBlockStateToUserState();
66 }
67 return SOLVER_CONTINUE;
68 }
69
70 private:
71 Program* program_;
72 double* parameters_;
73};
74
75// Callback for logging the state of the minimizer to STDERR or STDOUT
76// depending on the user's preferences and logging level.
77class LoggingCallback : public IterationCallback {
78 public:
79 explicit LoggingCallback(bool log_to_stdout)
80 : log_to_stdout_(log_to_stdout) {}
81
82 ~LoggingCallback() {}
83
84 CallbackReturnType operator()(const IterationSummary& summary) {
85 const char* kReportRowFormat =
86 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -070087 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -070088 string output = StringPrintf(kReportRowFormat,
89 summary.iteration,
90 summary.cost,
91 summary.cost_change,
92 summary.gradient_max_norm,
93 summary.step_norm,
94 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070095 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -070096 summary.linear_solver_iterations,
97 summary.iteration_time_in_seconds,
98 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -070099 if (log_to_stdout_) {
100 cout << output << endl;
101 } else {
102 VLOG(1) << output;
103 }
104 return SOLVER_CONTINUE;
105 }
106
107 private:
108 const bool log_to_stdout_;
109};
110
111} // namespace
112
113void SolverImpl::Minimize(const Solver::Options& options,
114 Program* program,
115 Evaluator* evaluator,
116 LinearSolver* linear_solver,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700117 double* parameters,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700118 Solver::Summary* summary) {
119 Minimizer::Options minimizer_options(options);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700120 LoggingCallback logging_callback(options.minimizer_progress_to_stdout);
121 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700122 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
123 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700124 }
125
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700126 StateUpdatingCallback updating_callback(program, parameters);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700127 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700128 // This must get pushed to the front of the callbacks so that it is run
129 // before any of the user callbacks.
130 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
131 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700132 }
133
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700134 minimizer_options.evaluator = evaluator;
135 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
136 minimizer_options.jacobian = jacobian.get();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700137
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700138 TrustRegionStrategy::Options trust_region_strategy_options;
139 trust_region_strategy_options.linear_solver = linear_solver;
140 trust_region_strategy_options.initial_radius =
141 options.initial_trust_region_radius;
142 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
143 trust_region_strategy_options.lm_min_diagonal = options.lm_min_diagonal;
144 trust_region_strategy_options.lm_max_diagonal = options.lm_max_diagonal;
145 trust_region_strategy_options.trust_region_strategy_type =
146 options.trust_region_strategy_type;
147 scoped_ptr<TrustRegionStrategy> strategy(
148 TrustRegionStrategy::Create(trust_region_strategy_options));
149 minimizer_options.trust_region_strategy = strategy.get();
150
151 TrustRegionMinimizer minimizer;
152 time_t minimizer_start_time = time(NULL);
153 minimizer.Minimize(minimizer_options, parameters, summary);
154 summary->minimizer_time_in_seconds = time(NULL) - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700155}
156
157void SolverImpl::Solve(const Solver::Options& original_options,
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700158 ProblemImpl* original_problem_impl,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700159 Solver::Summary* summary) {
Sameer Agarwalfa015192012-06-11 14:21:42 -0700160 time_t solver_start_time = time(NULL);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700161 Solver::Options options(original_options);
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700162 Program* original_program = original_problem_impl->mutable_program();
163 ProblemImpl* problem_impl = original_problem_impl;
164 // Reset the summary object to its default values.
165 *CHECK_NOTNULL(summary) = Solver::Summary();
166
Keir Mierle8ebb0732012-04-30 23:09:08 -0700167
168#ifndef CERES_USE_OPENMP
169 if (options.num_threads > 1) {
170 LOG(WARNING)
171 << "OpenMP support is not compiled into this binary; "
172 << "only options.num_threads=1 is supported. Switching"
173 << "to single threaded mode.";
174 options.num_threads = 1;
175 }
176 if (options.num_linear_solver_threads > 1) {
177 LOG(WARNING)
178 << "OpenMP support is not compiled into this binary; "
179 << "only options.num_linear_solver_threads=1 is supported. Switching"
180 << "to single threaded mode.";
181 options.num_linear_solver_threads = 1;
182 }
183#endif
184
Keir Mierle8ebb0732012-04-30 23:09:08 -0700185 summary->linear_solver_type_given = options.linear_solver_type;
186 summary->num_eliminate_blocks_given = original_options.num_eliminate_blocks;
187 summary->num_threads_given = original_options.num_threads;
188 summary->num_linear_solver_threads_given =
189 original_options.num_linear_solver_threads;
190 summary->ordering_type = original_options.ordering_type;
191
Keir Mierle8ebb0732012-04-30 23:09:08 -0700192 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
193 summary->num_parameters = problem_impl->NumParameters();
194 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
195 summary->num_residuals = problem_impl->NumResiduals();
196
197 summary->num_threads_used = options.num_threads;
Sameer Agarwal97fb6d92012-06-17 10:08:19 -0700198 summary->sparse_linear_algebra_library =
199 options.sparse_linear_algebra_library;
200 summary->trust_region_strategy_type = options.trust_region_strategy_type;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700201
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700202 // Evaluate the initial cost, residual vector and the jacobian
203 // matrix if requested by the user. The initial cost needs to be
204 // computed on the original unpreprocessed problem, as it is used to
205 // determine the value of the "fixed" part of the objective function
206 // after the problem has undergone reduction.
207 Evaluator::Evaluate(
208 original_program,
209 options.num_threads,
210 &(summary->initial_cost),
211 options.return_initial_residuals ? &summary->initial_residuals : NULL,
212 options.return_initial_gradient ? &summary->initial_gradient : NULL,
213 options.return_initial_jacobian ? &summary->initial_jacobian : NULL);
214 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700215
216 // If the user requests gradient checking, construct a new
217 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
218 // GradientCheckingCostFunction and replacing problem_impl with
219 // gradient_checking_problem_impl.
220 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
Keir Mierle6196cba2012-06-18 11:03:40 -0700221 // Save the original problem impl so we don't use the gradient
222 // checking one when computing the residuals.
Keir Mierle8ebb0732012-04-30 23:09:08 -0700223 if (options.check_gradients) {
224 VLOG(1) << "Checking Gradients";
225 gradient_checking_problem_impl.reset(
226 CreateGradientCheckingProblemImpl(
227 problem_impl,
228 options.numeric_derivative_relative_step_size,
229 options.gradient_check_relative_precision));
230
231 // From here on, problem_impl will point to the GradientChecking version.
232 problem_impl = gradient_checking_problem_impl.get();
233 }
234
235 // Create the three objects needed to minimize: the transformed program, the
236 // evaluator, and the linear solver.
237
238 scoped_ptr<Program> reduced_program(
Markus Moll8de78db2012-07-14 15:49:11 +0200239 CreateReducedProgram(&options, problem_impl, &summary->fixed_cost, &summary->error));
Keir Mierle8ebb0732012-04-30 23:09:08 -0700240 if (reduced_program == NULL) {
241 return;
242 }
243
244 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
245 summary->num_parameters_reduced = reduced_program->NumParameters();
246 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
247 summary->num_residuals_reduced = reduced_program->NumResiduals();
248
249 scoped_ptr<LinearSolver>
250 linear_solver(CreateLinearSolver(&options, &summary->error));
251 summary->linear_solver_type_used = options.linear_solver_type;
252 summary->preconditioner_type = options.preconditioner_type;
253 summary->num_eliminate_blocks_used = options.num_eliminate_blocks;
254 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
255
256 if (linear_solver == NULL) {
257 return;
258 }
259
260 if (!MaybeReorderResidualBlocks(options,
261 reduced_program.get(),
262 &summary->error)) {
263 return;
264 }
265
266 scoped_ptr<Evaluator> evaluator(
267 CreateEvaluator(options, reduced_program.get(), &summary->error));
268 if (evaluator == NULL) {
269 return;
270 }
271
272 // The optimizer works on contiguous parameter vectors; allocate some.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700273 Vector parameters(reduced_program->NumParameters());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700274
275 // Collect the discontiguous parameters into a contiguous state vector.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700276 reduced_program->ParameterBlocksToStateVector(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700277
Sameer Agarwalfa015192012-06-11 14:21:42 -0700278 time_t minimizer_start_time = time(NULL);
279 summary->preprocessor_time_in_seconds =
280 minimizer_start_time - solver_start_time;
281
Keir Mierle8ebb0732012-04-30 23:09:08 -0700282 // Run the optimization.
283 Minimize(options,
284 reduced_program.get(),
285 evaluator.get(),
286 linear_solver.get(),
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700287 parameters.data(),
Keir Mierle8ebb0732012-04-30 23:09:08 -0700288 summary);
289
290 // If the user aborted mid-optimization or the optimization
291 // terminated because of a numerical failure, then return without
292 // updating user state.
293 if (summary->termination_type == USER_ABORT ||
294 summary->termination_type == NUMERICAL_FAILURE) {
295 return;
296 }
297
Sameer Agarwalfa015192012-06-11 14:21:42 -0700298 time_t post_process_start_time = time(NULL);
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700299
Keir Mierle8ebb0732012-04-30 23:09:08 -0700300 // Push the contiguous optimized parameters back to the user's parameters.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700301 reduced_program->StateVectorToParameterBlocks(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700302 reduced_program->CopyParameterBlockStateToUserState();
303
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700304 // Evaluate the final cost, residual vector and the jacobian
305 // matrix if requested by the user.
306 Evaluator::Evaluate(
307 original_program,
308 options.num_threads,
309 &summary->final_cost,
310 options.return_final_residuals ? &summary->final_residuals : NULL,
311 options.return_final_gradient ? &summary->final_gradient : NULL,
312 options.return_final_jacobian ? &summary->final_jacobian : NULL);
313
Keir Mierle6196cba2012-06-18 11:03:40 -0700314 // Ensure the program state is set to the user parameters on the way out.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700315 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700316 // Stick a fork in it, we're done.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700317 summary->postprocessor_time_in_seconds = time(NULL) - post_process_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700318}
319
320// Strips varying parameters and residuals, maintaining order, and updating
321// num_eliminate_blocks.
322bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
323 int* num_eliminate_blocks,
Markus Moll8de78db2012-07-14 15:49:11 +0200324 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700325 string* error) {
326 int original_num_eliminate_blocks = *num_eliminate_blocks;
327 vector<ParameterBlock*>* parameter_blocks =
328 program->mutable_parameter_blocks();
329
Markus Moll8de78db2012-07-14 15:49:11 +0200330 scoped_array<double> residual_block_evaluate_scratch;
331 if (fixed_cost != NULL) {
Keir Mierlec161a9d2012-07-18 14:01:48 -0700332 residual_block_evaluate_scratch.reset(
Markus Moll8de78db2012-07-14 15:49:11 +0200333 new double[program->MaxScratchDoublesNeededForEvaluate()]);
334 *fixed_cost = 0.0;
335 }
336
Keir Mierle8ebb0732012-04-30 23:09:08 -0700337 // Mark all the parameters as unused. Abuse the index member of the parameter
338 // blocks for the marking.
339 for (int i = 0; i < parameter_blocks->size(); ++i) {
340 (*parameter_blocks)[i]->set_index(-1);
341 }
342
343 // Filter out residual that have all-constant parameters, and mark all the
344 // parameter blocks that appear in residuals.
345 {
346 vector<ResidualBlock*>* residual_blocks =
347 program->mutable_residual_blocks();
348 int j = 0;
349 for (int i = 0; i < residual_blocks->size(); ++i) {
350 ResidualBlock* residual_block = (*residual_blocks)[i];
351 int num_parameter_blocks = residual_block->NumParameterBlocks();
352
353 // Determine if the residual block is fixed, and also mark varying
354 // parameters that appear in the residual block.
355 bool all_constant = true;
356 for (int k = 0; k < num_parameter_blocks; k++) {
357 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
358 if (!parameter_block->IsConstant()) {
359 all_constant = false;
360 parameter_block->set_index(1);
361 }
362 }
363
364 if (!all_constant) {
365 (*residual_blocks)[j++] = (*residual_blocks)[i];
Markus Moll8de78db2012-07-14 15:49:11 +0200366 } else if (fixed_cost != NULL) {
367 // The residual is constant and will be removed, so its cost is
368 // added to the variable fixed_cost.
369 double cost = 0.0;
370 if (!residual_block->Evaluate(
371 &cost, NULL, NULL, residual_block_evaluate_scratch.get())) {
372 *error = StringPrintf("Evaluation of the residual %d failed during "
373 "removal of fixed residual blocks.", i);
374 return false;
375 }
376 *fixed_cost += cost;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700377 }
378 }
379 residual_blocks->resize(j);
380 }
381
382 // Filter out unused or fixed parameter blocks, and update
383 // num_eliminate_blocks as necessary.
384 {
385 vector<ParameterBlock*>* parameter_blocks =
386 program->mutable_parameter_blocks();
387 int j = 0;
388 for (int i = 0; i < parameter_blocks->size(); ++i) {
389 ParameterBlock* parameter_block = (*parameter_blocks)[i];
390 if (parameter_block->index() == 1) {
391 (*parameter_blocks)[j++] = parameter_block;
392 } else if (i < original_num_eliminate_blocks) {
393 (*num_eliminate_blocks)--;
394 }
395 }
396 parameter_blocks->resize(j);
397 }
398
399 CHECK(((program->NumResidualBlocks() == 0) &&
400 (program->NumParameterBlocks() == 0)) ||
401 ((program->NumResidualBlocks() != 0) &&
402 (program->NumParameterBlocks() != 0)))
403 << "Congratulations, you found a bug in Ceres. Please report it.";
404 return true;
405}
406
407Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
408 ProblemImpl* problem_impl,
Markus Moll8de78db2012-07-14 15:49:11 +0200409 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700410 string* error) {
411 Program* original_program = problem_impl->mutable_program();
412 scoped_ptr<Program> transformed_program(new Program(*original_program));
413
414 if (options->ordering_type == USER &&
415 !ApplyUserOrdering(*problem_impl,
416 options->ordering,
417 transformed_program.get(),
418 error)) {
419 return NULL;
420 }
421
422 if (options->ordering_type == SCHUR && options->num_eliminate_blocks != 0) {
423 *error = "Can't specify SCHUR ordering and num_eliminate_blocks "
424 "at the same time; SCHUR ordering determines "
425 "num_eliminate_blocks automatically.";
426 return NULL;
427 }
428
429 if (options->ordering_type == SCHUR && options->ordering.size() != 0) {
430 *error = "Can't specify SCHUR ordering type and the ordering "
431 "vector at the same time; SCHUR ordering determines "
432 "a suitable parameter ordering automatically.";
433 return NULL;
434 }
435
436 int num_eliminate_blocks = options->num_eliminate_blocks;
437
438 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
439 &num_eliminate_blocks,
Markus Moll8de78db2012-07-14 15:49:11 +0200440 fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700441 error)) {
442 return NULL;
443 }
444
445 if (transformed_program->NumParameterBlocks() == 0) {
446 LOG(WARNING) << "No varying parameter blocks to optimize; "
447 << "bailing early.";
448 return transformed_program.release();
449 }
450
451 if (options->ordering_type == SCHUR) {
452 vector<ParameterBlock*> schur_ordering;
453 num_eliminate_blocks = ComputeSchurOrdering(*transformed_program,
454 &schur_ordering);
455 CHECK_EQ(schur_ordering.size(), transformed_program->NumParameterBlocks())
456 << "Congratulations, you found a Ceres bug! Please report this error "
457 << "to the developers.";
458
459 // Replace the transformed program's ordering with the schur ordering.
460 swap(*transformed_program->mutable_parameter_blocks(), schur_ordering);
461 }
462 options->num_eliminate_blocks = num_eliminate_blocks;
463 CHECK_GE(options->num_eliminate_blocks, 0)
464 << "Congratulations, you found a Ceres bug! Please report this error "
465 << "to the developers.";
466
467 // Since the transformed program is the "active" program, and it is mutated,
468 // update the parameter offsets and indices.
469 transformed_program->SetParameterOffsetsAndIndex();
470 return transformed_program.release();
471}
472
473LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
474 string* error) {
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700475 if (options->trust_region_strategy_type == DOGLEG) {
476 if (options->linear_solver_type == ITERATIVE_SCHUR ||
477 options->linear_solver_type == CGNR) {
478 *error = "DOGLEG only supports exact factorization based linear "
479 "solvers. If you want to use an iterative solver please "
480 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
481 return NULL;
482 }
483 }
484
Keir Mierle8ebb0732012-04-30 23:09:08 -0700485#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -0700486 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
487 options->sparse_linear_algebra_library == SUITE_SPARSE) {
488 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
489 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700490 return NULL;
491 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700492#endif
493
494#ifdef CERES_NO_CXSPARSE
495 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
496 options->sparse_linear_algebra_library == CX_SPARSE) {
497 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
498 "CXSparse was not enabled when Ceres was built.";
499 return NULL;
500 }
501#endif
502
Keir Mierle8ebb0732012-04-30 23:09:08 -0700503
504 if (options->linear_solver_max_num_iterations <= 0) {
505 *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
506 return NULL;
507 }
508 if (options->linear_solver_min_num_iterations <= 0) {
509 *error = "Solver::Options::linear_solver_min_num_iterations is 0.";
510 return NULL;
511 }
512 if (options->linear_solver_min_num_iterations >
513 options->linear_solver_max_num_iterations) {
514 *error = "Solver::Options::linear_solver_min_num_iterations > "
515 "Solver::Options::linear_solver_max_num_iterations.";
516 return NULL;
517 }
518
519 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700520 linear_solver_options.min_num_iterations =
521 options->linear_solver_min_num_iterations;
522 linear_solver_options.max_num_iterations =
523 options->linear_solver_max_num_iterations;
524 linear_solver_options.type = options->linear_solver_type;
525 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700526 linear_solver_options.sparse_linear_algebra_library =
527 options->sparse_linear_algebra_library;
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700528 linear_solver_options.use_block_amd = options->use_block_amd;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700529
530#ifdef CERES_NO_SUITESPARSE
531 if (linear_solver_options.preconditioner_type == SCHUR_JACOBI) {
532 *error = "SCHUR_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700533 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700534 return NULL;
535 }
536
537 if (linear_solver_options.preconditioner_type == CLUSTER_JACOBI) {
538 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700539 "with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700540 return NULL;
541 }
542
543 if (linear_solver_options.preconditioner_type == CLUSTER_TRIDIAGONAL) {
544 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
Sameer Agarwalb0518732012-05-29 00:27:57 -0700545 "Ceres with SuiteSparse support.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700546 return NULL;
547 }
548#endif
549
550 linear_solver_options.num_threads = options->num_linear_solver_threads;
551 linear_solver_options.num_eliminate_blocks =
552 options->num_eliminate_blocks;
553
554 if ((linear_solver_options.num_eliminate_blocks == 0) &&
555 IsSchurType(linear_solver_options.type)) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700556#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
557 LOG(INFO) << "No elimination block remaining switching to DENSE_QR.";
558 linear_solver_options.type = DENSE_QR;
559#else
Keir Mierle8ebb0732012-04-30 23:09:08 -0700560 LOG(INFO) << "No elimination block remaining "
561 << "switching to SPARSE_NORMAL_CHOLESKY.";
562 linear_solver_options.type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700563#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700564 }
565
Sameer Agarwalb0518732012-05-29 00:27:57 -0700566#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle8ebb0732012-04-30 23:09:08 -0700567 if (linear_solver_options.type == SPARSE_SCHUR) {
Sameer Agarwalb0518732012-05-29 00:27:57 -0700568 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
569 "CXSparse was enabled when Ceres was compiled.";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700570 return NULL;
571 }
Sameer Agarwalb0518732012-05-29 00:27:57 -0700572#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700573
574 // The matrix used for storing the dense Schur complement has a
575 // single lock guarding the whole matrix. Running the
576 // SchurComplementSolver with multiple threads leads to maximum
577 // contention and slowdown. If the problem is large enough to
578 // benefit from a multithreaded schur eliminator, you should be
579 // using a SPARSE_SCHUR solver anyways.
580 if ((linear_solver_options.num_threads > 1) &&
581 (linear_solver_options.type == DENSE_SCHUR)) {
582 LOG(WARNING) << "Warning: Solver::Options::num_linear_solver_threads = "
583 << options->num_linear_solver_threads
584 << " with DENSE_SCHUR will result in poor performance; "
585 << "switching to single-threaded.";
586 linear_solver_options.num_threads = 1;
587 }
588
589 options->linear_solver_type = linear_solver_options.type;
590 options->num_linear_solver_threads = linear_solver_options.num_threads;
591
592 return LinearSolver::Create(linear_solver_options);
593}
594
595bool SolverImpl::ApplyUserOrdering(const ProblemImpl& problem_impl,
596 vector<double*>& ordering,
597 Program* program,
598 string* error) {
599 if (ordering.size() != program->NumParameterBlocks()) {
600 *error = StringPrintf("User specified ordering does not have the same "
601 "number of parameters as the problem. The problem"
602 "has %d blocks while the ordering has %ld blocks.",
603 program->NumParameterBlocks(),
604 ordering.size());
605 return false;
606 }
607
608 // Ensure that there are no duplicates in the user's ordering.
609 {
610 vector<double*> ordering_copy(ordering);
611 sort(ordering_copy.begin(), ordering_copy.end());
612 if (unique(ordering_copy.begin(), ordering_copy.end())
613 != ordering_copy.end()) {
614 *error = "User specified ordering contains duplicates.";
615 return false;
616 }
617 }
618
619 vector<ParameterBlock*>* parameter_blocks =
620 program->mutable_parameter_blocks();
621
622 fill(parameter_blocks->begin(),
623 parameter_blocks->end(),
624 static_cast<ParameterBlock*>(NULL));
625
626 const ProblemImpl::ParameterMap& parameter_map = problem_impl.parameter_map();
627 for (int i = 0; i < ordering.size(); ++i) {
628 ProblemImpl::ParameterMap::const_iterator it =
629 parameter_map.find(ordering[i]);
630 if (it == parameter_map.end()) {
631 *error = StringPrintf("User specified ordering contains a pointer "
632 "to a double that is not a parameter block in the "
633 "problem. The invalid double is at position %d "
634 " in options.ordering.", i);
635 return false;
636 }
637 (*parameter_blocks)[i] = it->second;
638 }
639 return true;
640}
641
642// Find the minimum index of any parameter block to the given residual.
643// Parameter blocks that have indices greater than num_eliminate_blocks are
644// considered to have an index equal to num_eliminate_blocks.
645int MinParameterBlock(const ResidualBlock* residual_block,
646 int num_eliminate_blocks) {
647 int min_parameter_block_position = num_eliminate_blocks;
648 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
649 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -0700650 if (!parameter_block->IsConstant()) {
651 CHECK_NE(parameter_block->index(), -1)
652 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
653 << "This is a Ceres bug; please contact the developers!";
654 min_parameter_block_position = std::min(parameter_block->index(),
655 min_parameter_block_position);
656 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700657 }
658 return min_parameter_block_position;
659}
660
661// Reorder the residuals for program, if necessary, so that the residuals
662// involving each E block occur together. This is a necessary condition for the
663// Schur eliminator, which works on these "row blocks" in the jacobian.
664bool SolverImpl::MaybeReorderResidualBlocks(const Solver::Options& options,
665 Program* program,
666 string* error) {
667 // Only Schur types require the lexicographic reordering.
668 if (!IsSchurType(options.linear_solver_type)) {
669 return true;
670 }
671
672 CHECK_NE(0, options.num_eliminate_blocks)
673 << "Congratulations, you found a Ceres bug! Please report this error "
674 << "to the developers.";
675
676 // Create a histogram of the number of residuals for each E block. There is an
677 // extra bucket at the end to catch all non-eliminated F blocks.
678 vector<int> residual_blocks_per_e_block(options.num_eliminate_blocks + 1);
679 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
680 vector<int> min_position_per_residual(residual_blocks->size());
681 for (int i = 0; i < residual_blocks->size(); ++i) {
682 ResidualBlock* residual_block = (*residual_blocks)[i];
683 int position = MinParameterBlock(residual_block,
684 options.num_eliminate_blocks);
685 min_position_per_residual[i] = position;
686 DCHECK_LE(position, options.num_eliminate_blocks);
687 residual_blocks_per_e_block[position]++;
688 }
689
690 // Run a cumulative sum on the histogram, to obtain offsets to the start of
691 // each histogram bucket (where each bucket is for the residuals for that
692 // E-block).
693 vector<int> offsets(options.num_eliminate_blocks + 1);
694 std::partial_sum(residual_blocks_per_e_block.begin(),
695 residual_blocks_per_e_block.end(),
696 offsets.begin());
697 CHECK_EQ(offsets.back(), residual_blocks->size())
698 << "Congratulations, you found a Ceres bug! Please report this error "
699 << "to the developers.";
700
701 CHECK(find(residual_blocks_per_e_block.begin(),
702 residual_blocks_per_e_block.end() - 1, 0) !=
703 residual_blocks_per_e_block.end())
704 << "Congratulations, you found a Ceres bug! Please report this error "
705 << "to the developers.";
706
707 // Fill in each bucket with the residual blocks for its corresponding E block.
708 // Each bucket is individually filled from the back of the bucket to the front
709 // of the bucket. The filling order among the buckets is dictated by the
710 // residual blocks. This loop uses the offsets as counters; subtracting one
711 // from each offset as a residual block is placed in the bucket. When the
712 // filling is finished, the offset pointerts should have shifted down one
713 // entry (this is verified below).
714 vector<ResidualBlock*> reordered_residual_blocks(
715 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
716 for (int i = 0; i < residual_blocks->size(); ++i) {
717 int bucket = min_position_per_residual[i];
718
719 // Decrement the cursor, which should now point at the next empty position.
720 offsets[bucket]--;
721
722 // Sanity.
723 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
724 << "Congratulations, you found a Ceres bug! Please report this error "
725 << "to the developers.";
726
727 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
728 }
729
730 // Sanity check #1: The difference in bucket offsets should match the
731 // histogram sizes.
732 for (int i = 0; i < options.num_eliminate_blocks; ++i) {
733 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
734 << "Congratulations, you found a Ceres bug! Please report this error "
735 << "to the developers.";
736 }
737 // Sanity check #2: No NULL's left behind.
738 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
739 CHECK(reordered_residual_blocks[i] != NULL)
740 << "Congratulations, you found a Ceres bug! Please report this error "
741 << "to the developers.";
742 }
743
744 // Now that the residuals are collected by E block, swap them in place.
745 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
746 return true;
747}
748
749Evaluator* SolverImpl::CreateEvaluator(const Solver::Options& options,
750 Program* program,
751 string* error) {
752 Evaluator::Options evaluator_options;
753 evaluator_options.linear_solver_type = options.linear_solver_type;
754 evaluator_options.num_eliminate_blocks = options.num_eliminate_blocks;
755 evaluator_options.num_threads = options.num_threads;
756 return Evaluator::Create(evaluator_options, program, error);
757}
758
759} // namespace internal
760} // namespace ceres