blob: ffc347ec9f08a9934f2a3912619ec2f04e884667 [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>
Sameer Agarwalba8d9672012-10-02 00:48:57 -070036#include "ceres/coordinate_descent_minimizer.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070037#include "ceres/evaluator.h"
38#include "ceres/gradient_checking_cost_function.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070039#include "ceres/iteration_callback.h"
40#include "ceres/levenberg_marquardt_strategy.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070041#include "ceres/linear_solver.h"
Sameer Agarwalf4d01642012-11-26 12:55:58 -080042#include "ceres/line_search_minimizer.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070043#include "ceres/map_util.h"
44#include "ceres/minimizer.h"
Sameer Agarwal2c94eed2012-10-01 16:34:37 -070045#include "ceres/ordered_groups.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070046#include "ceres/parameter_block.h"
Sameer Agarwalba8d9672012-10-02 00:48:57 -070047#include "ceres/parameter_block_ordering.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070048#include "ceres/problem.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070049#include "ceres/problem_impl.h"
50#include "ceres/program.h"
51#include "ceres/residual_block.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070052#include "ceres/stringprintf.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070053#include "ceres/trust_region_minimizer.h"
Petter Strandmark76533b32012-09-04 22:08:50 -070054#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070055
56namespace ceres {
57namespace internal {
58namespace {
59
Keir Mierle8ebb0732012-04-30 23:09:08 -070060// Callback for updating the user's parameter blocks. Updates are only
61// done if the step is successful.
62class StateUpdatingCallback : public IterationCallback {
63 public:
64 StateUpdatingCallback(Program* program, double* parameters)
65 : program_(program), parameters_(parameters) {}
66
67 CallbackReturnType operator()(const IterationSummary& summary) {
68 if (summary.step_is_successful) {
69 program_->StateVectorToParameterBlocks(parameters_);
70 program_->CopyParameterBlockStateToUserState();
71 }
72 return SOLVER_CONTINUE;
73 }
74
75 private:
76 Program* program_;
77 double* parameters_;
78};
79
Sameer Agarwalf102a682013-02-11 15:08:40 -080080void SetSummaryFinalCost(Solver::Summary* summary) {
81 summary->final_cost = summary->initial_cost;
82 // We need the loop here, instead of just looking at the last
83 // iteration because the minimizer maybe making non-monotonic steps.
84 for (int i = 0; i < summary->iterations.size(); ++i) {
85 const IterationSummary& iteration_summary = summary->iterations[i];
86 summary->final_cost = min(iteration_summary.cost, summary->final_cost);
87 }
88}
89
Keir Mierle8ebb0732012-04-30 23:09:08 -070090// Callback for logging the state of the minimizer to STDERR or STDOUT
91// depending on the user's preferences and logging level.
Sameer Agarwalf4d01642012-11-26 12:55:58 -080092class TrustRegionLoggingCallback : public IterationCallback {
Keir Mierle8ebb0732012-04-30 23:09:08 -070093 public:
Sameer Agarwalf4d01642012-11-26 12:55:58 -080094 explicit TrustRegionLoggingCallback(bool log_to_stdout)
Keir Mierle8ebb0732012-04-30 23:09:08 -070095 : log_to_stdout_(log_to_stdout) {}
96
Sameer Agarwalf4d01642012-11-26 12:55:58 -080097 ~TrustRegionLoggingCallback() {}
Keir Mierle8ebb0732012-04-30 23:09:08 -070098
99 CallbackReturnType operator()(const IterationSummary& summary) {
100 const char* kReportRowFormat =
101 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -0700102 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700103 string output = StringPrintf(kReportRowFormat,
104 summary.iteration,
105 summary.cost,
106 summary.cost_change,
107 summary.gradient_max_norm,
108 summary.step_norm,
109 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700110 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -0700111 summary.linear_solver_iterations,
112 summary.iteration_time_in_seconds,
113 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700114 if (log_to_stdout_) {
115 cout << output << endl;
116 } else {
117 VLOG(1) << output;
118 }
119 return SOLVER_CONTINUE;
120 }
121
122 private:
123 const bool log_to_stdout_;
124};
125
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800126// Callback for logging the state of the minimizer to STDERR or STDOUT
127// depending on the user's preferences and logging level.
128class LineSearchLoggingCallback : public IterationCallback {
129 public:
130 explicit LineSearchLoggingCallback(bool log_to_stdout)
131 : log_to_stdout_(log_to_stdout) {}
132
133 ~LineSearchLoggingCallback() {}
134
135 CallbackReturnType operator()(const IterationSummary& summary) {
136 const char* kReportRowFormat =
137 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
138 "s:% 3.2e e:% 3d it:% 3.2e tt:% 3.2e";
139 string output = StringPrintf(kReportRowFormat,
140 summary.iteration,
141 summary.cost,
142 summary.cost_change,
143 summary.gradient_max_norm,
144 summary.step_norm,
145 summary.step_size,
146 summary.line_search_function_evaluations,
147 summary.iteration_time_in_seconds,
148 summary.cumulative_time_in_seconds);
149 if (log_to_stdout_) {
150 cout << output << endl;
151 } else {
152 VLOG(1) << output;
153 }
154 return SOLVER_CONTINUE;
155 }
156
157 private:
158 const bool log_to_stdout_;
159};
160
161
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700162// Basic callback to record the execution of the solver to a file for
163// offline analysis.
164class FileLoggingCallback : public IterationCallback {
165 public:
166 explicit FileLoggingCallback(const string& filename)
167 : fptr_(NULL) {
168 fptr_ = fopen(filename.c_str(), "w");
169 CHECK_NOTNULL(fptr_);
170 }
171
172 virtual ~FileLoggingCallback() {
173 if (fptr_ != NULL) {
174 fclose(fptr_);
175 }
176 }
177
178 virtual CallbackReturnType operator()(const IterationSummary& summary) {
179 fprintf(fptr_,
180 "%4d %e %e\n",
181 summary.iteration,
182 summary.cost,
183 summary.cumulative_time_in_seconds);
184 return SOLVER_CONTINUE;
185 }
186 private:
187 FILE* fptr_;
188};
189
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800190// Iterate over each of the groups in order of their priority and fill
191// summary with their sizes.
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800192void SummarizeOrdering(ParameterBlockOrdering* ordering,
193 vector<int>* summary) {
194 CHECK_NOTNULL(summary)->clear();
195 if (ordering == NULL) {
196 return;
197 }
198
199 const map<int, set<double*> >& group_to_elements =
200 ordering->group_to_elements();
201 for (map<int, set<double*> >::const_iterator it = group_to_elements.begin();
202 it != group_to_elements.end();
203 ++it) {
204 summary->push_back(it->second.size());
205 }
206}
207
Keir Mierle8ebb0732012-04-30 23:09:08 -0700208} // namespace
209
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800210void SolverImpl::TrustRegionMinimize(
211 const Solver::Options& options,
212 Program* program,
213 CoordinateDescentMinimizer* inner_iteration_minimizer,
214 Evaluator* evaluator,
215 LinearSolver* linear_solver,
216 double* parameters,
217 Solver::Summary* summary) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700218 Minimizer::Options minimizer_options(options);
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700219
220 // TODO(sameeragarwal): Add support for logging the configuration
221 // and more detailed stats.
222 scoped_ptr<IterationCallback> file_logging_callback;
223 if (!options.solver_log.empty()) {
224 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
225 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
226 file_logging_callback.get());
227 }
228
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800229 TrustRegionLoggingCallback logging_callback(
230 options.minimizer_progress_to_stdout);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700231 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700232 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
233 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700234 }
235
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700236 StateUpdatingCallback updating_callback(program, parameters);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700237 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700238 // This must get pushed to the front of the callbacks so that it is run
239 // before any of the user callbacks.
240 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
241 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700242 }
243
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700244 minimizer_options.evaluator = evaluator;
245 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800246
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700247 minimizer_options.jacobian = jacobian.get();
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700248 minimizer_options.inner_iteration_minimizer = inner_iteration_minimizer;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700249
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700250 TrustRegionStrategy::Options trust_region_strategy_options;
251 trust_region_strategy_options.linear_solver = linear_solver;
252 trust_region_strategy_options.initial_radius =
253 options.initial_trust_region_radius;
254 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
255 trust_region_strategy_options.lm_min_diagonal = options.lm_min_diagonal;
256 trust_region_strategy_options.lm_max_diagonal = options.lm_max_diagonal;
257 trust_region_strategy_options.trust_region_strategy_type =
258 options.trust_region_strategy_type;
Markus Moll51cf7cb2012-08-20 20:10:20 +0200259 trust_region_strategy_options.dogleg_type = options.dogleg_type;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700260 scoped_ptr<TrustRegionStrategy> strategy(
261 TrustRegionStrategy::Create(trust_region_strategy_options));
262 minimizer_options.trust_region_strategy = strategy.get();
263
264 TrustRegionMinimizer minimizer;
Petter Strandmark76533b32012-09-04 22:08:50 -0700265 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700266 minimizer.Minimize(minimizer_options, parameters, summary);
Petter Strandmark76533b32012-09-04 22:08:50 -0700267 summary->minimizer_time_in_seconds =
268 WallTimeInSeconds() - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700269}
270
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700271#ifndef CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800272void SolverImpl::LineSearchMinimize(
273 const Solver::Options& options,
274 Program* program,
275 Evaluator* evaluator,
276 double* parameters,
277 Solver::Summary* summary) {
278 Minimizer::Options minimizer_options(options);
279
280 // TODO(sameeragarwal): Add support for logging the configuration
281 // and more detailed stats.
282 scoped_ptr<IterationCallback> file_logging_callback;
283 if (!options.solver_log.empty()) {
284 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
285 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
286 file_logging_callback.get());
287 }
288
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800289 LineSearchLoggingCallback logging_callback(
290 options.minimizer_progress_to_stdout);
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800291 if (options.logging_type != SILENT) {
292 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
293 &logging_callback);
294 }
295
296 StateUpdatingCallback updating_callback(program, parameters);
297 if (options.update_state_every_iteration) {
298 // This must get pushed to the front of the callbacks so that it is run
299 // before any of the user callbacks.
300 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
301 &updating_callback);
302 }
303
304 minimizer_options.evaluator = evaluator;
305
306 LineSearchMinimizer minimizer;
307 double minimizer_start_time = WallTimeInSeconds();
308 minimizer.Minimize(minimizer_options, parameters, summary);
309 summary->minimizer_time_in_seconds =
310 WallTimeInSeconds() - minimizer_start_time;
311}
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700312#endif // CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800313
314void SolverImpl::Solve(const Solver::Options& options,
315 ProblemImpl* problem_impl,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700316 Solver::Summary* summary) {
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800317 if (options.minimizer_type == TRUST_REGION) {
318 TrustRegionSolve(options, problem_impl, summary);
319 } else {
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700320#ifndef CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800321 LineSearchSolve(options, problem_impl, summary);
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700322#else
323 LOG(FATAL) << "Ceres Solver was compiled with -DLINE_SEARCH_MINIMIZER=OFF";
324#endif
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800325 }
326}
327
328void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
329 ProblemImpl* original_problem_impl,
330 Solver::Summary* summary) {
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800331 EventLogger event_logger("TrustRegionSolve");
Petter Strandmark76533b32012-09-04 22:08:50 -0700332 double solver_start_time = WallTimeInSeconds();
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700333
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700334 Program* original_program = original_problem_impl->mutable_program();
335 ProblemImpl* problem_impl = original_problem_impl;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700336
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700337 // Reset the summary object to its default values.
338 *CHECK_NOTNULL(summary) = Solver::Summary();
339
Sameer Agarwald010de52013-02-15 14:26:56 -0800340 summary->minimizer_type = TRUST_REGION;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700341 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
342 summary->num_parameters = problem_impl->NumParameters();
Keir Mierleba944212013-02-25 12:46:44 -0800343 summary->num_effective_parameters =
344 original_program->NumEffectiveParameters();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700345 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
346 summary->num_residuals = problem_impl->NumResiduals();
347
348 // Empty programs are usually a user error.
349 if (summary->num_parameter_blocks == 0) {
350 summary->error = "Problem contains no parameter blocks.";
351 LOG(ERROR) << summary->error;
352 return;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700353 }
354
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700355 if (summary->num_residual_blocks == 0) {
356 summary->error = "Problem contains no residual blocks.";
357 LOG(ERROR) << summary->error;
358 return;
359 }
360
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800361 SummarizeOrdering(original_options.linear_solver_ordering,
362 &(summary->linear_solver_ordering_given));
363
364 SummarizeOrdering(original_options.inner_iteration_ordering,
365 &(summary->inner_iteration_ordering_given));
366
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700367 Solver::Options options(original_options);
368 options.linear_solver_ordering = NULL;
369 options.inner_iteration_ordering = NULL;
370
Keir Mierle8ebb0732012-04-30 23:09:08 -0700371#ifndef CERES_USE_OPENMP
372 if (options.num_threads > 1) {
373 LOG(WARNING)
374 << "OpenMP support is not compiled into this binary; "
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700375 << "only options.num_threads=1 is supported. Switching "
Keir Mierle8ebb0732012-04-30 23:09:08 -0700376 << "to single threaded mode.";
377 options.num_threads = 1;
378 }
379 if (options.num_linear_solver_threads > 1) {
380 LOG(WARNING)
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700381 << "OpenMP support is not compiled into this binary; "
382 << "only options.num_linear_solver_threads=1 is supported. Switching "
Keir Mierle8ebb0732012-04-30 23:09:08 -0700383 << "to single threaded mode.";
384 options.num_linear_solver_threads = 1;
385 }
386#endif
387
Keir Mierle8ebb0732012-04-30 23:09:08 -0700388 summary->num_threads_given = original_options.num_threads;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700389 summary->num_threads_used = options.num_threads;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700390
391 if (options.lsqp_iterations_to_dump.size() > 0) {
392 LOG(WARNING) << "Dumping linear least squares problems to disk is"
393 " currently broken. Ignoring Solver::Options::lsqp_iterations_to_dump";
394 }
395
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800396 event_logger.AddEvent("Init");
Sameer Agarwalf102a682013-02-11 15:08:40 -0800397
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700398 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800399 event_logger.AddEvent("SetParameterBlockPtrs");
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700400
401 // If the user requests gradient checking, construct a new
402 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
403 // GradientCheckingCostFunction and replacing problem_impl with
404 // gradient_checking_problem_impl.
405 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
406 if (options.check_gradients) {
407 VLOG(1) << "Checking Gradients";
408 gradient_checking_problem_impl.reset(
409 CreateGradientCheckingProblemImpl(
410 problem_impl,
411 options.numeric_derivative_relative_step_size,
412 options.gradient_check_relative_precision));
413
414 // From here on, problem_impl will point to the gradient checking
415 // version.
416 problem_impl = gradient_checking_problem_impl.get();
417 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700418
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700419 if (original_options.linear_solver_ordering != NULL) {
Sameer Agarwal65625f72012-09-17 12:06:57 -0700420 if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700421 LOG(ERROR) << summary->error;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700422 return;
423 }
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800424 event_logger.AddEvent("CheckOrdering");
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700425 options.linear_solver_ordering =
426 new ParameterBlockOrdering(*original_options.linear_solver_ordering);
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800427 event_logger.AddEvent("CopyOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -0700428 } else {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700429 options.linear_solver_ordering = new ParameterBlockOrdering;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700430 const ProblemImpl::ParameterMap& parameter_map =
431 problem_impl->parameter_map();
432 for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
433 it != parameter_map.end();
434 ++it) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700435 options.linear_solver_ordering->AddElementToGroup(it->first, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700436 }
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800437 event_logger.AddEvent("ConstructOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -0700438 }
439
Keir Mierle8ebb0732012-04-30 23:09:08 -0700440 // Create the three objects needed to minimize: the transformed program, the
441 // evaluator, and the linear solver.
Keir Mierle31c1e782012-08-17 16:16:32 -0700442 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
443 problem_impl,
444 &summary->fixed_cost,
445 &summary->error));
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800446
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800447 event_logger.AddEvent("CreateReducedProgram");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700448 if (reduced_program == NULL) {
449 return;
450 }
451
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800452 SummarizeOrdering(options.linear_solver_ordering,
453 &(summary->linear_solver_ordering_used));
454
Keir Mierle8ebb0732012-04-30 23:09:08 -0700455 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
456 summary->num_parameters_reduced = reduced_program->NumParameters();
Keir Mierleba944212013-02-25 12:46:44 -0800457 summary->num_effective_parameters_reduced =
458 reduced_program->NumEffectiveParameters();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700459 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
460 summary->num_residuals_reduced = reduced_program->NumResiduals();
461
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700462 if (summary->num_parameter_blocks_reduced == 0) {
463 summary->preprocessor_time_in_seconds =
464 WallTimeInSeconds() - solver_start_time;
465
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800466 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700467 LOG(INFO) << "Terminating: FUNCTION_TOLERANCE reached. "
468 << "No non-constant parameter blocks found.";
469
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800470 summary->initial_cost = summary->fixed_cost;
471 summary->final_cost = summary->fixed_cost;
472
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700473 // FUNCTION_TOLERANCE is the right convergence here, as we know
474 // that the objective function is constant and cannot be changed
475 // any further.
476 summary->termination_type = FUNCTION_TOLERANCE;
477
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700478 // Ensure the program state is set to the user parameters on the way out.
479 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
480
481 summary->postprocessor_time_in_seconds =
482 WallTimeInSeconds() - post_process_start_time;
483 return;
484 }
485
Keir Mierle8ebb0732012-04-30 23:09:08 -0700486 scoped_ptr<LinearSolver>
487 linear_solver(CreateLinearSolver(&options, &summary->error));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800488 event_logger.AddEvent("CreateLinearSolver");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700489 if (linear_solver == NULL) {
490 return;
491 }
492
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700493 summary->linear_solver_type_given = original_options.linear_solver_type;
494 summary->linear_solver_type_used = options.linear_solver_type;
495
496 summary->preconditioner_type = options.preconditioner_type;
497
498 summary->num_linear_solver_threads_given =
499 original_options.num_linear_solver_threads;
500 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
501
502 summary->sparse_linear_algebra_library =
503 options.sparse_linear_algebra_library;
504
505 summary->trust_region_strategy_type = options.trust_region_strategy_type;
506 summary->dogleg_type = options.dogleg_type;
507
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700508 // Only Schur types require the lexicographic reordering.
509 if (IsSchurType(options.linear_solver_type)) {
510 const int num_eliminate_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700511 options.linear_solver_ordering
512 ->group_to_elements().begin()
513 ->second.size();
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700514 if (!LexicographicallyOrderResidualBlocks(num_eliminate_blocks,
515 reduced_program.get(),
516 &summary->error)) {
517 return;
518 }
519 }
520
521 scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
522 problem_impl->parameter_map(),
523 reduced_program.get(),
524 &summary->error));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800525
526 event_logger.AddEvent("CreateEvaluator");
527
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700528 if (evaluator == NULL) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700529 return;
530 }
531
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700532 scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700533 if (options.use_inner_iterations) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700534 if (reduced_program->parameter_blocks().size() < 2) {
535 LOG(WARNING) << "Reduced problem only contains one parameter block."
536 << "Disabling inner iterations.";
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700537 } else {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700538 inner_iteration_minimizer.reset(
539 CreateInnerIterationMinimizer(original_options,
540 *reduced_program,
541 problem_impl->parameter_map(),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800542 summary));
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700543 if (inner_iteration_minimizer == NULL) {
544 LOG(ERROR) << summary->error;
545 return;
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700546 }
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700547 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700548 }
549
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800550 event_logger.AddEvent("CreateIIM");
551
Keir Mierle8ebb0732012-04-30 23:09:08 -0700552 // The optimizer works on contiguous parameter vectors; allocate some.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700553 Vector parameters(reduced_program->NumParameters());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700554
555 // Collect the discontiguous parameters into a contiguous state vector.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700556 reduced_program->ParameterBlocksToStateVector(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700557
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700558 Vector original_parameters = parameters;
559
Petter Strandmark76533b32012-09-04 22:08:50 -0700560 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalfa015192012-06-11 14:21:42 -0700561 summary->preprocessor_time_in_seconds =
562 minimizer_start_time - solver_start_time;
563
Keir Mierle8ebb0732012-04-30 23:09:08 -0700564 // Run the optimization.
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800565 TrustRegionMinimize(options,
566 reduced_program.get(),
567 inner_iteration_minimizer.get(),
568 evaluator.get(),
569 linear_solver.get(),
570 parameters.data(),
571 summary);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800572 event_logger.AddEvent("Minimize");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700573
Sameer Agarwalf102a682013-02-11 15:08:40 -0800574 SetSummaryFinalCost(summary);
575
Keir Mierle8ebb0732012-04-30 23:09:08 -0700576 // If the user aborted mid-optimization or the optimization
577 // terminated because of a numerical failure, then return without
578 // updating user state.
579 if (summary->termination_type == USER_ABORT ||
580 summary->termination_type == NUMERICAL_FAILURE) {
581 return;
582 }
583
Petter Strandmark76533b32012-09-04 22:08:50 -0700584 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700585
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800586 // Push the contiguous optimized parameters back to the user's
587 // parameters.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700588 reduced_program->StateVectorToParameterBlocks(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700589 reduced_program->CopyParameterBlockStateToUserState();
590
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800591 // Ensure the program state is set to the user parameters on the way
592 // out.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700593 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700594
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800595 const map<string, double>& linear_solver_time_statistics =
596 linear_solver->TimeStatistics();
597 summary->linear_solver_time_in_seconds =
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800598 FindWithDefault(linear_solver_time_statistics,
599 "LinearSolver::Solve",
600 0.0);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800601
602 const map<string, double>& evaluator_time_statistics =
603 evaluator->TimeStatistics();
604
605 summary->residual_evaluation_time_in_seconds =
606 FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
607 summary->jacobian_evaluation_time_in_seconds =
608 FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
609
Keir Mierle8ebb0732012-04-30 23:09:08 -0700610 // Stick a fork in it, we're done.
Petter Strandmark76533b32012-09-04 22:08:50 -0700611 summary->postprocessor_time_in_seconds =
612 WallTimeInSeconds() - post_process_start_time;
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800613 event_logger.AddEvent("PostProcess");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700614}
615
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700616
617#ifndef CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800618void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
619 ProblemImpl* original_problem_impl,
620 Solver::Summary* summary) {
621 double solver_start_time = WallTimeInSeconds();
622
623 Program* original_program = original_problem_impl->mutable_program();
624 ProblemImpl* problem_impl = original_problem_impl;
625
626 // Reset the summary object to its default values.
627 *CHECK_NOTNULL(summary) = Solver::Summary();
628
Sameer Agarwald010de52013-02-15 14:26:56 -0800629 summary->minimizer_type = LINE_SEARCH;
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800630 summary->line_search_direction_type =
631 original_options.line_search_direction_type;
Sameer Agarwald010de52013-02-15 14:26:56 -0800632 summary->max_lbfgs_rank = original_options.max_lbfgs_rank;
633 summary->line_search_type = original_options.line_search_type;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800634 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
635 summary->num_parameters = problem_impl->NumParameters();
636 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
637 summary->num_residuals = problem_impl->NumResiduals();
638
639 // Empty programs are usually a user error.
640 if (summary->num_parameter_blocks == 0) {
641 summary->error = "Problem contains no parameter blocks.";
642 LOG(ERROR) << summary->error;
643 return;
644 }
645
646 if (summary->num_residual_blocks == 0) {
647 summary->error = "Problem contains no residual blocks.";
648 LOG(ERROR) << summary->error;
649 return;
650 }
651
652 Solver::Options options(original_options);
653
654 // This ensures that we get a Block Jacobian Evaluator along with
655 // none of the Schur nonsense. This file will have to be extensively
656 // refactored to deal with the various bits of cleanups related to
657 // line search.
658 options.linear_solver_type = CGNR;
659
660 options.linear_solver_ordering = NULL;
661 options.inner_iteration_ordering = NULL;
662
663#ifndef CERES_USE_OPENMP
664 if (options.num_threads > 1) {
665 LOG(WARNING)
666 << "OpenMP support is not compiled into this binary; "
667 << "only options.num_threads=1 is supported. Switching "
668 << "to single threaded mode.";
669 options.num_threads = 1;
670 }
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700671#endif // CERES_USE_OPENMP
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800672
673 summary->num_threads_given = original_options.num_threads;
674 summary->num_threads_used = options.num_threads;
675
676 if (original_options.linear_solver_ordering != NULL) {
677 if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
678 LOG(ERROR) << summary->error;
679 return;
680 }
681 options.linear_solver_ordering =
682 new ParameterBlockOrdering(*original_options.linear_solver_ordering);
683 } else {
684 options.linear_solver_ordering = new ParameterBlockOrdering;
685 const ProblemImpl::ParameterMap& parameter_map =
686 problem_impl->parameter_map();
687 for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
688 it != parameter_map.end();
689 ++it) {
690 options.linear_solver_ordering->AddElementToGroup(it->first, 0);
691 }
692 }
693
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800694 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
695
696 // If the user requests gradient checking, construct a new
697 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
698 // GradientCheckingCostFunction and replacing problem_impl with
699 // gradient_checking_problem_impl.
700 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
701 if (options.check_gradients) {
702 VLOG(1) << "Checking Gradients";
703 gradient_checking_problem_impl.reset(
704 CreateGradientCheckingProblemImpl(
705 problem_impl,
706 options.numeric_derivative_relative_step_size,
707 options.gradient_check_relative_precision));
708
709 // From here on, problem_impl will point to the gradient checking
710 // version.
711 problem_impl = gradient_checking_problem_impl.get();
712 }
713
714 // Create the three objects needed to minimize: the transformed program, the
715 // evaluator, and the linear solver.
716 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
717 problem_impl,
718 &summary->fixed_cost,
719 &summary->error));
720 if (reduced_program == NULL) {
721 return;
722 }
723
724 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
725 summary->num_parameters_reduced = reduced_program->NumParameters();
726 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
727 summary->num_residuals_reduced = reduced_program->NumResiduals();
728
729 if (summary->num_parameter_blocks_reduced == 0) {
730 summary->preprocessor_time_in_seconds =
731 WallTimeInSeconds() - solver_start_time;
732
733 LOG(INFO) << "Terminating: FUNCTION_TOLERANCE reached. "
734 << "No non-constant parameter blocks found.";
735
736 // FUNCTION_TOLERANCE is the right convergence here, as we know
737 // that the objective function is constant and cannot be changed
738 // any further.
739 summary->termination_type = FUNCTION_TOLERANCE;
740
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800741 const double post_process_start_time = WallTimeInSeconds();
Sameer Agarwalf102a682013-02-11 15:08:40 -0800742
743 SetSummaryFinalCost(summary);
744
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800745 // Ensure the program state is set to the user parameters on the way out.
746 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal956ed7e2013-02-19 07:06:15 -0800747 summary->postprocessor_time_in_seconds =
748 WallTimeInSeconds() - post_process_start_time;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800749 return;
750 }
751
752 scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
753 problem_impl->parameter_map(),
754 reduced_program.get(),
755 &summary->error));
756 if (evaluator == NULL) {
757 return;
758 }
759
760 // The optimizer works on contiguous parameter vectors; allocate some.
761 Vector parameters(reduced_program->NumParameters());
762
763 // Collect the discontiguous parameters into a contiguous state vector.
764 reduced_program->ParameterBlocksToStateVector(parameters.data());
765
766 Vector original_parameters = parameters;
767
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800768 const double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800769 summary->preprocessor_time_in_seconds =
770 minimizer_start_time - solver_start_time;
771
772 // Run the optimization.
773 LineSearchMinimize(options,
774 reduced_program.get(),
775 evaluator.get(),
776 parameters.data(),
777 summary);
778
779 // If the user aborted mid-optimization or the optimization
780 // terminated because of a numerical failure, then return without
781 // updating user state.
782 if (summary->termination_type == USER_ABORT ||
783 summary->termination_type == NUMERICAL_FAILURE) {
784 return;
785 }
786
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800787 const double post_process_start_time = WallTimeInSeconds();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800788
789 // Push the contiguous optimized parameters back to the user's parameters.
790 reduced_program->StateVectorToParameterBlocks(parameters.data());
791 reduced_program->CopyParameterBlockStateToUserState();
792
Sameer Agarwalf102a682013-02-11 15:08:40 -0800793 SetSummaryFinalCost(summary);
794
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800795 // Ensure the program state is set to the user parameters on the way out.
796 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
797
Sameer Agarwald010de52013-02-15 14:26:56 -0800798 const map<string, double>& evaluator_time_statistics =
799 evaluator->TimeStatistics();
800
801 summary->residual_evaluation_time_in_seconds =
802 FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
803 summary->jacobian_evaluation_time_in_seconds =
804 FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
805
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800806 // Stick a fork in it, we're done.
807 summary->postprocessor_time_in_seconds =
808 WallTimeInSeconds() - post_process_start_time;
809}
Sameer Agarwal700d50d2013-03-12 16:12:42 -0700810#endif // CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800811
Sameer Agarwal65625f72012-09-17 12:06:57 -0700812bool SolverImpl::IsOrderingValid(const Solver::Options& options,
813 const ProblemImpl* problem_impl,
814 string* error) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700815 if (options.linear_solver_ordering->NumElements() !=
Sameer Agarwal65625f72012-09-17 12:06:57 -0700816 problem_impl->NumParameterBlocks()) {
817 *error = "Number of parameter blocks in user supplied ordering "
818 "does not match the number of parameter blocks in the problem";
819 return false;
820 }
821
822 const Program& program = problem_impl->program();
823 const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -0700824 for (vector<ParameterBlock*>::const_iterator it = parameter_blocks.begin();
825 it != parameter_blocks.end();
826 ++it) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700827 if (!options.linear_solver_ordering
828 ->IsMember(const_cast<double*>((*it)->user_state()))) {
Sameer Agarwal65625f72012-09-17 12:06:57 -0700829 *error = "Problem contains a parameter block that is not in "
830 "the user specified ordering.";
831 return false;
832 }
833 }
834
835 if (IsSchurType(options.linear_solver_type) &&
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700836 options.linear_solver_ordering->NumGroups() > 1) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700837 const vector<ResidualBlock*>& residual_blocks = program.residual_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -0700838 const set<double*>& e_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700839 options.linear_solver_ordering->group_to_elements().begin()->second;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700840 if (!IsParameterBlockSetIndependent(e_blocks, residual_blocks)) {
841 *error = "The user requested the use of a Schur type solver. "
842 "But the first elimination group in the ordering is not an "
843 "independent set.";
844 return false;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700845 }
846 }
847 return true;
848}
849
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800850bool SolverImpl::IsParameterBlockSetIndependent(
851 const set<double*>& parameter_block_ptrs,
852 const vector<ResidualBlock*>& residual_blocks) {
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700853 // Loop over each residual block and ensure that no two parameter
854 // blocks in the same residual block are part of
855 // parameter_block_ptrs as that would violate the assumption that it
856 // is an independent set in the Hessian matrix.
857 for (vector<ResidualBlock*>::const_iterator it = residual_blocks.begin();
858 it != residual_blocks.end();
859 ++it) {
860 ParameterBlock* const* parameter_blocks = (*it)->parameter_blocks();
861 const int num_parameter_blocks = (*it)->NumParameterBlocks();
862 int count = 0;
863 for (int i = 0; i < num_parameter_blocks; ++i) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700864 count += parameter_block_ptrs.count(
865 parameter_blocks[i]->mutable_user_state());
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700866 }
867 if (count > 1) {
868 return false;
869 }
870 }
871 return true;
872}
873
874
Keir Mierle8ebb0732012-04-30 23:09:08 -0700875// Strips varying parameters and residuals, maintaining order, and updating
876// num_eliminate_blocks.
877bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700878 ParameterBlockOrdering* ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200879 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700880 string* error) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700881 vector<ParameterBlock*>* parameter_blocks =
882 program->mutable_parameter_blocks();
883
Markus Moll8de78db2012-07-14 15:49:11 +0200884 scoped_array<double> residual_block_evaluate_scratch;
885 if (fixed_cost != NULL) {
Keir Mierlec161a9d2012-07-18 14:01:48 -0700886 residual_block_evaluate_scratch.reset(
Markus Moll8de78db2012-07-14 15:49:11 +0200887 new double[program->MaxScratchDoublesNeededForEvaluate()]);
888 *fixed_cost = 0.0;
889 }
890
Keir Mierle8ebb0732012-04-30 23:09:08 -0700891 // Mark all the parameters as unused. Abuse the index member of the parameter
892 // blocks for the marking.
893 for (int i = 0; i < parameter_blocks->size(); ++i) {
894 (*parameter_blocks)[i]->set_index(-1);
895 }
896
897 // Filter out residual that have all-constant parameters, and mark all the
898 // parameter blocks that appear in residuals.
899 {
900 vector<ResidualBlock*>* residual_blocks =
901 program->mutable_residual_blocks();
902 int j = 0;
903 for (int i = 0; i < residual_blocks->size(); ++i) {
904 ResidualBlock* residual_block = (*residual_blocks)[i];
905 int num_parameter_blocks = residual_block->NumParameterBlocks();
906
907 // Determine if the residual block is fixed, and also mark varying
908 // parameters that appear in the residual block.
909 bool all_constant = true;
910 for (int k = 0; k < num_parameter_blocks; k++) {
911 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
912 if (!parameter_block->IsConstant()) {
913 all_constant = false;
914 parameter_block->set_index(1);
915 }
916 }
917
918 if (!all_constant) {
919 (*residual_blocks)[j++] = (*residual_blocks)[i];
Markus Moll8de78db2012-07-14 15:49:11 +0200920 } else if (fixed_cost != NULL) {
921 // The residual is constant and will be removed, so its cost is
922 // added to the variable fixed_cost.
923 double cost = 0.0;
Sameer Agarwal039ff072013-02-26 09:15:39 -0800924 if (!residual_block->Evaluate(true,
925 &cost,
926 NULL,
927 NULL,
928 residual_block_evaluate_scratch.get())) {
Markus Moll8de78db2012-07-14 15:49:11 +0200929 *error = StringPrintf("Evaluation of the residual %d failed during "
930 "removal of fixed residual blocks.", i);
931 return false;
932 }
933 *fixed_cost += cost;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700934 }
935 }
936 residual_blocks->resize(j);
937 }
938
939 // Filter out unused or fixed parameter blocks, and update
Sameer Agarwal65625f72012-09-17 12:06:57 -0700940 // the ordering.
Keir Mierle8ebb0732012-04-30 23:09:08 -0700941 {
942 vector<ParameterBlock*>* parameter_blocks =
943 program->mutable_parameter_blocks();
944 int j = 0;
945 for (int i = 0; i < parameter_blocks->size(); ++i) {
946 ParameterBlock* parameter_block = (*parameter_blocks)[i];
947 if (parameter_block->index() == 1) {
948 (*parameter_blocks)[j++] = parameter_block;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700949 } else {
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700950 ordering->Remove(parameter_block->mutable_user_state());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700951 }
952 }
953 parameter_blocks->resize(j);
954 }
955
956 CHECK(((program->NumResidualBlocks() == 0) &&
957 (program->NumParameterBlocks() == 0)) ||
958 ((program->NumResidualBlocks() != 0) &&
959 (program->NumParameterBlocks() != 0)))
960 << "Congratulations, you found a bug in Ceres. Please report it.";
961 return true;
962}
963
964Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
965 ProblemImpl* problem_impl,
Markus Moll8de78db2012-07-14 15:49:11 +0200966 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700967 string* error) {
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800968 EventLogger event_logger("CreateReducedProgram");
969
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700970 CHECK_NOTNULL(options->linear_solver_ordering);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700971 Program* original_program = problem_impl->mutable_program();
972 scoped_ptr<Program> transformed_program(new Program(*original_program));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800973 event_logger.AddEvent("TransformedProgram");
974
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700975 ParameterBlockOrdering* linear_solver_ordering =
976 options->linear_solver_ordering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700977
Sameer Agarwal65625f72012-09-17 12:06:57 -0700978 const int min_group_id =
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700979 linear_solver_ordering->group_to_elements().begin()->first;
980 const int original_num_groups = linear_solver_ordering->NumGroups();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700981
982 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700983 linear_solver_ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200984 fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700985 error)) {
986 return NULL;
987 }
988
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800989 event_logger.AddEvent("RemoveFixedBlocks");
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800990
Keir Mierle8ebb0732012-04-30 23:09:08 -0700991 if (transformed_program->NumParameterBlocks() == 0) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700992 if (transformed_program->NumResidualBlocks() > 0) {
993 *error = "Zero parameter blocks but non-zero residual blocks"
994 " in the reduced program. Congratulations, you found a "
995 "Ceres bug! Please report this error to the developers.";
996 return NULL;
997 }
998
Keir Mierle8ebb0732012-04-30 23:09:08 -0700999 LOG(WARNING) << "No varying parameter blocks to optimize; "
1000 << "bailing early.";
1001 return transformed_program.release();
1002 }
1003
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001004 // If the user supplied an linear_solver_ordering with just one
1005 // group, it is equivalent to the user supplying NULL as
1006 // ordering. Ceres is completely free to choose the parameter block
1007 // ordering as it sees fit. For Schur type solvers, this means that
1008 // the user wishes for Ceres to identify the e_blocks, which we do
1009 // by computing a maximal independent set.
Sameer Agarwalba8d9672012-10-02 00:48:57 -07001010 if (original_num_groups == 1 && IsSchurType(options->linear_solver_type)) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001011 vector<ParameterBlock*> schur_ordering;
Sameer Agarwalc1ffad62012-10-05 16:29:37 -07001012 const int num_eliminate_blocks = ComputeSchurOrdering(*transformed_program,
Sameer Agarwal65625f72012-09-17 12:06:57 -07001013 &schur_ordering);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001014 CHECK_EQ(schur_ordering.size(), transformed_program->NumParameterBlocks())
1015 << "Congratulations, you found a Ceres bug! Please report this error "
1016 << "to the developers.";
1017
Sameer Agarwal65625f72012-09-17 12:06:57 -07001018 for (int i = 0; i < schur_ordering.size(); ++i) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001019 linear_solver_ordering->AddElementToGroup(
1020 schur_ordering[i]->mutable_user_state(),
1021 (i < num_eliminate_blocks) ? 0 : 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -07001022 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001023 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001024 event_logger.AddEvent("SchurOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -07001025
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001026 if (!ApplyUserOrdering(problem_impl->parameter_map(),
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001027 linear_solver_ordering,
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001028 transformed_program.get(),
1029 error)) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001030 return NULL;
1031 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001032 event_logger.AddEvent("ApplyOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -07001033
1034 // If the user requested the use of a Schur type solver, and
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001035 // supplied a non-NULL linear_solver_ordering object with more than
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001036 // one elimination group, then it can happen that after all the
Sameer Agarwal65625f72012-09-17 12:06:57 -07001037 // parameter blocks which are fixed or unused have been removed from
1038 // the program and the ordering, there are no more parameter blocks
1039 // in the first elimination group.
1040 //
1041 // In such a case, the use of a Schur type solver is not possible,
1042 // as they assume there is at least one e_block. Thus, we
1043 // automatically switch to one of the other solvers, depending on
1044 // the user's indicated preferences.
1045 if (IsSchurType(options->linear_solver_type) &&
1046 original_num_groups > 1 &&
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001047 linear_solver_ordering->GroupSize(min_group_id) == 0) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001048 string msg = "No e_blocks remaining. Switching from ";
1049 if (options->linear_solver_type == SPARSE_SCHUR) {
1050 options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
1051 msg += "SPARSE_SCHUR to SPARSE_NORMAL_CHOLESKY.";
1052 } else if (options->linear_solver_type == DENSE_SCHUR) {
1053 // TODO(sameeragarwal): This is probably not a great choice.
1054 // Ideally, we should have a DENSE_NORMAL_CHOLESKY, that can
1055 // take a BlockSparseMatrix as input.
1056 options->linear_solver_type = DENSE_QR;
1057 msg += "DENSE_SCHUR to DENSE_QR.";
1058 } else if (options->linear_solver_type == ITERATIVE_SCHUR) {
1059 msg += StringPrintf("ITERATIVE_SCHUR with %s preconditioner "
1060 "to CGNR with JACOBI preconditioner.",
1061 PreconditionerTypeToString(
1062 options->preconditioner_type));
1063 options->linear_solver_type = CGNR;
1064 if (options->preconditioner_type != IDENTITY) {
1065 // CGNR currently only supports the JACOBI preconditioner.
1066 options->preconditioner_type = JACOBI;
1067 }
1068 }
1069
1070 LOG(WARNING) << msg;
1071 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001072
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001073 event_logger.AddEvent("AlternateSolver");
1074
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001075 // Since the transformed program is the "active" program, and it is
1076 // mutated, update the parameter offsets and indices.
Keir Mierle8ebb0732012-04-30 23:09:08 -07001077 transformed_program->SetParameterOffsetsAndIndex();
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001078
1079 event_logger.AddEvent("SetOffsets");
Keir Mierle8ebb0732012-04-30 23:09:08 -07001080 return transformed_program.release();
1081}
1082
1083LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
1084 string* error) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001085 CHECK_NOTNULL(options);
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001086 CHECK_NOTNULL(options->linear_solver_ordering);
Sameer Agarwal65625f72012-09-17 12:06:57 -07001087 CHECK_NOTNULL(error);
1088
Sameer Agarwal5ecd2512012-06-17 16:34:03 -07001089 if (options->trust_region_strategy_type == DOGLEG) {
1090 if (options->linear_solver_type == ITERATIVE_SCHUR ||
1091 options->linear_solver_type == CGNR) {
1092 *error = "DOGLEG only supports exact factorization based linear "
1093 "solvers. If you want to use an iterative solver please "
1094 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
1095 return NULL;
1096 }
1097 }
1098
Keir Mierle8ebb0732012-04-30 23:09:08 -07001099#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -07001100 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
1101 options->sparse_linear_algebra_library == SUITE_SPARSE) {
1102 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
1103 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001104 return NULL;
1105 }
Sameer Agarwal65625f72012-09-17 12:06:57 -07001106
Petter Strandmarkdd1a2762012-09-19 12:55:05 +02001107 if (options->preconditioner_type == CLUSTER_JACOBI) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001108 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
1109 "with SuiteSparse support.";
1110 return NULL;
1111 }
1112
Petter Strandmarkdd1a2762012-09-19 12:55:05 +02001113 if (options->preconditioner_type == CLUSTER_TRIDIAGONAL) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001114 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
1115 "Ceres with SuiteSparse support.";
1116 return NULL;
1117 }
Sameer Agarwalb0518732012-05-29 00:27:57 -07001118#endif
1119
1120#ifdef CERES_NO_CXSPARSE
1121 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
1122 options->sparse_linear_algebra_library == CX_SPARSE) {
1123 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
1124 "CXSparse was not enabled when Ceres was built.";
1125 return NULL;
1126 }
1127#endif
1128
Sameer Agarwal65625f72012-09-17 12:06:57 -07001129#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle27dd0d32012-10-15 13:52:36 -07001130 if (options->linear_solver_type == SPARSE_SCHUR) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001131 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
1132 "CXSparse was enabled when Ceres was compiled.";
1133 return NULL;
1134 }
1135#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -07001136
1137 if (options->linear_solver_max_num_iterations <= 0) {
1138 *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
1139 return NULL;
1140 }
1141 if (options->linear_solver_min_num_iterations <= 0) {
1142 *error = "Solver::Options::linear_solver_min_num_iterations is 0.";
1143 return NULL;
1144 }
1145 if (options->linear_solver_min_num_iterations >
1146 options->linear_solver_max_num_iterations) {
1147 *error = "Solver::Options::linear_solver_min_num_iterations > "
1148 "Solver::Options::linear_solver_max_num_iterations.";
1149 return NULL;
1150 }
1151
1152 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001153 linear_solver_options.min_num_iterations =
1154 options->linear_solver_min_num_iterations;
1155 linear_solver_options.max_num_iterations =
1156 options->linear_solver_max_num_iterations;
1157 linear_solver_options.type = options->linear_solver_type;
1158 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalb0518732012-05-29 00:27:57 -07001159 linear_solver_options.sparse_linear_algebra_library =
1160 options->sparse_linear_algebra_library;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001161
1162 linear_solver_options.num_threads = options->num_linear_solver_threads;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001163 options->num_linear_solver_threads = linear_solver_options.num_threads;
1164
Sameer Agarwal65625f72012-09-17 12:06:57 -07001165 linear_solver_options.use_block_amd = options->use_block_amd;
1166 const map<int, set<double*> >& groups =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001167 options->linear_solver_ordering->group_to_elements();
Sameer Agarwal65625f72012-09-17 12:06:57 -07001168 for (map<int, set<double*> >::const_iterator it = groups.begin();
1169 it != groups.end();
1170 ++it) {
1171 linear_solver_options.elimination_groups.push_back(it->second.size());
1172 }
1173 // Schur type solvers, expect at least two elimination groups. If
Sameer Agarwal1dc544a2012-09-18 18:09:03 -07001174 // there is only one elimination group, then CreateReducedProgram
Sameer Agarwal65625f72012-09-17 12:06:57 -07001175 // guarantees that this group only contains e_blocks. Thus we add a
1176 // dummy elimination group with zero blocks in it.
1177 if (IsSchurType(linear_solver_options.type) &&
1178 linear_solver_options.elimination_groups.size() == 1) {
1179 linear_solver_options.elimination_groups.push_back(0);
1180 }
1181
Keir Mierle8ebb0732012-04-30 23:09:08 -07001182 return LinearSolver::Create(linear_solver_options);
1183}
1184
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001185bool SolverImpl::ApplyUserOrdering(
1186 const ProblemImpl::ParameterMap& parameter_map,
1187 const ParameterBlockOrdering* ordering,
1188 Program* program,
1189 string* error) {
Sameer Agarwal2c94eed2012-10-01 16:34:37 -07001190 if (ordering->NumElements() != program->NumParameterBlocks()) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001191 *error = StringPrintf("User specified ordering does not have the same "
1192 "number of parameters as the problem. The problem"
Sameer Agarwal65625f72012-09-17 12:06:57 -07001193 "has %d blocks while the ordering has %d blocks.",
Keir Mierle8ebb0732012-04-30 23:09:08 -07001194 program->NumParameterBlocks(),
Sameer Agarwal2c94eed2012-10-01 16:34:37 -07001195 ordering->NumElements());
Keir Mierle8ebb0732012-04-30 23:09:08 -07001196 return false;
1197 }
1198
Keir Mierle8ebb0732012-04-30 23:09:08 -07001199 vector<ParameterBlock*>* parameter_blocks =
1200 program->mutable_parameter_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -07001201 parameter_blocks->clear();
Keir Mierle8ebb0732012-04-30 23:09:08 -07001202
Sameer Agarwal65625f72012-09-17 12:06:57 -07001203 const map<int, set<double*> >& groups =
Sameer Agarwal2c94eed2012-10-01 16:34:37 -07001204 ordering->group_to_elements();
Keir Mierle8ebb0732012-04-30 23:09:08 -07001205
Sameer Agarwal65625f72012-09-17 12:06:57 -07001206 for (map<int, set<double*> >::const_iterator group_it = groups.begin();
1207 group_it != groups.end();
1208 ++group_it) {
1209 const set<double*>& group = group_it->second;
1210 for (set<double*>::const_iterator parameter_block_ptr_it = group.begin();
1211 parameter_block_ptr_it != group.end();
1212 ++parameter_block_ptr_it) {
1213 ProblemImpl::ParameterMap::const_iterator parameter_block_it =
1214 parameter_map.find(*parameter_block_ptr_it);
1215 if (parameter_block_it == parameter_map.end()) {
1216 *error = StringPrintf("User specified ordering contains a pointer "
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001217 "to a double that is not a parameter block in "
1218 "the problem. The invalid double is in group: %d",
Sameer Agarwal65625f72012-09-17 12:06:57 -07001219 group_it->first);
1220 return false;
1221 }
1222 parameter_blocks->push_back(parameter_block_it->second);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001223 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001224 }
1225 return true;
1226}
1227
1228// Find the minimum index of any parameter block to the given residual.
1229// Parameter blocks that have indices greater than num_eliminate_blocks are
1230// considered to have an index equal to num_eliminate_blocks.
Sergey Sharybinb53c9662013-02-25 01:14:26 +06001231static int MinParameterBlock(const ResidualBlock* residual_block,
1232 int num_eliminate_blocks) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001233 int min_parameter_block_position = num_eliminate_blocks;
1234 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
1235 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -07001236 if (!parameter_block->IsConstant()) {
1237 CHECK_NE(parameter_block->index(), -1)
1238 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
1239 << "This is a Ceres bug; please contact the developers!";
1240 min_parameter_block_position = std::min(parameter_block->index(),
1241 min_parameter_block_position);
1242 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001243 }
1244 return min_parameter_block_position;
1245}
1246
1247// Reorder the residuals for program, if necessary, so that the residuals
1248// involving each E block occur together. This is a necessary condition for the
1249// Schur eliminator, which works on these "row blocks" in the jacobian.
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001250bool SolverImpl::LexicographicallyOrderResidualBlocks(
1251 const int num_eliminate_blocks,
1252 Program* program,
1253 string* error) {
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001254 CHECK_GE(num_eliminate_blocks, 1)
1255 << "Congratulations, you found a Ceres bug! Please report this error "
1256 << "to the developers.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001257
1258 // Create a histogram of the number of residuals for each E block. There is an
1259 // extra bucket at the end to catch all non-eliminated F blocks.
Sameer Agarwal65625f72012-09-17 12:06:57 -07001260 vector<int> residual_blocks_per_e_block(num_eliminate_blocks + 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001261 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
1262 vector<int> min_position_per_residual(residual_blocks->size());
1263 for (int i = 0; i < residual_blocks->size(); ++i) {
1264 ResidualBlock* residual_block = (*residual_blocks)[i];
Sameer Agarwal65625f72012-09-17 12:06:57 -07001265 int position = MinParameterBlock(residual_block, num_eliminate_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001266 min_position_per_residual[i] = position;
Sameer Agarwal65625f72012-09-17 12:06:57 -07001267 DCHECK_LE(position, num_eliminate_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001268 residual_blocks_per_e_block[position]++;
1269 }
1270
1271 // Run a cumulative sum on the histogram, to obtain offsets to the start of
1272 // each histogram bucket (where each bucket is for the residuals for that
1273 // E-block).
Sameer Agarwal65625f72012-09-17 12:06:57 -07001274 vector<int> offsets(num_eliminate_blocks + 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001275 std::partial_sum(residual_blocks_per_e_block.begin(),
1276 residual_blocks_per_e_block.end(),
1277 offsets.begin());
1278 CHECK_EQ(offsets.back(), residual_blocks->size())
1279 << "Congratulations, you found a Ceres bug! Please report this error "
1280 << "to the developers.";
1281
1282 CHECK(find(residual_blocks_per_e_block.begin(),
1283 residual_blocks_per_e_block.end() - 1, 0) !=
1284 residual_blocks_per_e_block.end())
1285 << "Congratulations, you found a Ceres bug! Please report this error "
1286 << "to the developers.";
1287
1288 // Fill in each bucket with the residual blocks for its corresponding E block.
1289 // Each bucket is individually filled from the back of the bucket to the front
1290 // of the bucket. The filling order among the buckets is dictated by the
1291 // residual blocks. This loop uses the offsets as counters; subtracting one
1292 // from each offset as a residual block is placed in the bucket. When the
1293 // filling is finished, the offset pointerts should have shifted down one
1294 // entry (this is verified below).
1295 vector<ResidualBlock*> reordered_residual_blocks(
1296 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
1297 for (int i = 0; i < residual_blocks->size(); ++i) {
1298 int bucket = min_position_per_residual[i];
1299
1300 // Decrement the cursor, which should now point at the next empty position.
1301 offsets[bucket]--;
1302
1303 // Sanity.
1304 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
1305 << "Congratulations, you found a Ceres bug! Please report this error "
1306 << "to the developers.";
1307
1308 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
1309 }
1310
1311 // Sanity check #1: The difference in bucket offsets should match the
1312 // histogram sizes.
Sameer Agarwal65625f72012-09-17 12:06:57 -07001313 for (int i = 0; i < num_eliminate_blocks; ++i) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001314 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
1315 << "Congratulations, you found a Ceres bug! Please report this error "
1316 << "to the developers.";
1317 }
1318 // Sanity check #2: No NULL's left behind.
1319 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
1320 CHECK(reordered_residual_blocks[i] != NULL)
1321 << "Congratulations, you found a Ceres bug! Please report this error "
1322 << "to the developers.";
1323 }
1324
1325 // Now that the residuals are collected by E block, swap them in place.
1326 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
1327 return true;
1328}
1329
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001330Evaluator* SolverImpl::CreateEvaluator(
1331 const Solver::Options& options,
1332 const ProblemImpl::ParameterMap& parameter_map,
1333 Program* program,
1334 string* error) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001335 Evaluator::Options evaluator_options;
1336 evaluator_options.linear_solver_type = options.linear_solver_type;
Sameer Agarwal65625f72012-09-17 12:06:57 -07001337 evaluator_options.num_eliminate_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001338 (options.linear_solver_ordering->NumGroups() > 0 &&
Sameer Agarwal65625f72012-09-17 12:06:57 -07001339 IsSchurType(options.linear_solver_type))
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001340 ? (options.linear_solver_ordering
1341 ->group_to_elements().begin()
1342 ->second.size())
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001343 : 0;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001344 evaluator_options.num_threads = options.num_threads;
1345 return Evaluator::Create(evaluator_options, program, error);
1346}
1347
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001348CoordinateDescentMinimizer* SolverImpl::CreateInnerIterationMinimizer(
1349 const Solver::Options& options,
1350 const Program& program,
1351 const ProblemImpl::ParameterMap& parameter_map,
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001352 Solver::Summary* summary) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001353 scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer(
1354 new CoordinateDescentMinimizer);
1355 scoped_ptr<ParameterBlockOrdering> inner_iteration_ordering;
1356 ParameterBlockOrdering* ordering_ptr = NULL;
1357
1358 if (options.inner_iteration_ordering == NULL) {
1359 // Find a recursive decomposition of the Hessian matrix as a set
1360 // of independent sets of decreasing size and invert it. This
1361 // seems to work better in practice, i.e., Cameras before
1362 // points.
1363 inner_iteration_ordering.reset(new ParameterBlockOrdering);
1364 ComputeRecursiveIndependentSetOrdering(program,
1365 inner_iteration_ordering.get());
1366 inner_iteration_ordering->Reverse();
1367 ordering_ptr = inner_iteration_ordering.get();
1368 } else {
1369 const map<int, set<double*> >& group_to_elements =
1370 options.inner_iteration_ordering->group_to_elements();
1371
1372 // Iterate over each group and verify that it is an independent
1373 // set.
1374 map<int, set<double*> >::const_iterator it = group_to_elements.begin();
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001375 for ( ; it != group_to_elements.end(); ++it) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001376 if (!IsParameterBlockSetIndependent(it->second,
1377 program.residual_blocks())) {
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001378 summary->error =
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001379 StringPrintf("The user-provided "
1380 "parameter_blocks_for_inner_iterations does not "
1381 "form an independent set. Group Id: %d", it->first);
1382 return NULL;
1383 }
1384 }
1385 ordering_ptr = options.inner_iteration_ordering;
1386 }
1387
1388 if (!inner_iteration_minimizer->Init(program,
1389 parameter_map,
1390 *ordering_ptr,
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001391 &summary->error)) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001392 return NULL;
1393 }
1394
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001395 summary->inner_iterations = true;
1396 SummarizeOrdering(ordering_ptr, &(summary->inner_iteration_ordering_used));
1397
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001398 return inner_iteration_minimizer.release();
1399}
1400
Keir Mierle8ebb0732012-04-30 23:09:08 -07001401} // namespace internal
1402} // namespace ceres