blob: 43c0be6180d5b567109c07ec5fce5d2f1fb952ed [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"
Sameer Agarwalf4d01642012-11-26 12:55:58 -080041#include "ceres/line_search_minimizer.h"
Sameer Agarwalc8f07902013-04-19 08:01:04 -070042#include "ceres/linear_solver.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 Agarwalf7ed22e2013-04-19 14:24:48 -070053#include "ceres/suitesparse.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070054#include "ceres/trust_region_minimizer.h"
Petter Strandmark76533b32012-09-04 22:08:50 -070055#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070056
57namespace ceres {
58namespace internal {
59namespace {
60
Keir Mierle8ebb0732012-04-30 23:09:08 -070061// Callback for updating the user's parameter blocks. Updates are only
62// done if the step is successful.
63class StateUpdatingCallback : public IterationCallback {
64 public:
65 StateUpdatingCallback(Program* program, double* parameters)
66 : program_(program), parameters_(parameters) {}
67
68 CallbackReturnType operator()(const IterationSummary& summary) {
69 if (summary.step_is_successful) {
70 program_->StateVectorToParameterBlocks(parameters_);
71 program_->CopyParameterBlockStateToUserState();
72 }
73 return SOLVER_CONTINUE;
74 }
75
76 private:
77 Program* program_;
78 double* parameters_;
79};
80
Sameer Agarwalf102a682013-02-11 15:08:40 -080081void SetSummaryFinalCost(Solver::Summary* summary) {
82 summary->final_cost = summary->initial_cost;
83 // We need the loop here, instead of just looking at the last
84 // iteration because the minimizer maybe making non-monotonic steps.
85 for (int i = 0; i < summary->iterations.size(); ++i) {
86 const IterationSummary& iteration_summary = summary->iterations[i];
87 summary->final_cost = min(iteration_summary.cost, summary->final_cost);
88 }
89}
90
Keir Mierle8ebb0732012-04-30 23:09:08 -070091// Callback for logging the state of the minimizer to STDERR or STDOUT
92// depending on the user's preferences and logging level.
Sameer Agarwalf4d01642012-11-26 12:55:58 -080093class TrustRegionLoggingCallback : public IterationCallback {
Keir Mierle8ebb0732012-04-30 23:09:08 -070094 public:
Sameer Agarwalf4d01642012-11-26 12:55:58 -080095 explicit TrustRegionLoggingCallback(bool log_to_stdout)
Keir Mierle8ebb0732012-04-30 23:09:08 -070096 : log_to_stdout_(log_to_stdout) {}
97
Sameer Agarwalf4d01642012-11-26 12:55:58 -080098 ~TrustRegionLoggingCallback() {}
Keir Mierle8ebb0732012-04-30 23:09:08 -070099
100 CallbackReturnType operator()(const IterationSummary& summary) {
101 const char* kReportRowFormat =
102 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -0700103 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700104 string output = StringPrintf(kReportRowFormat,
105 summary.iteration,
106 summary.cost,
107 summary.cost_change,
108 summary.gradient_max_norm,
109 summary.step_norm,
110 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700111 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -0700112 summary.linear_solver_iterations,
113 summary.iteration_time_in_seconds,
114 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700115 if (log_to_stdout_) {
116 cout << output << endl;
117 } else {
118 VLOG(1) << output;
119 }
120 return SOLVER_CONTINUE;
121 }
122
123 private:
124 const bool log_to_stdout_;
125};
126
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800127// Callback for logging the state of the minimizer to STDERR or STDOUT
128// depending on the user's preferences and logging level.
129class LineSearchLoggingCallback : public IterationCallback {
130 public:
131 explicit LineSearchLoggingCallback(bool log_to_stdout)
132 : log_to_stdout_(log_to_stdout) {}
133
134 ~LineSearchLoggingCallback() {}
135
136 CallbackReturnType operator()(const IterationSummary& summary) {
137 const char* kReportRowFormat =
138 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
139 "s:% 3.2e e:% 3d it:% 3.2e tt:% 3.2e";
140 string output = StringPrintf(kReportRowFormat,
141 summary.iteration,
142 summary.cost,
143 summary.cost_change,
144 summary.gradient_max_norm,
145 summary.step_norm,
146 summary.step_size,
147 summary.line_search_function_evaluations,
148 summary.iteration_time_in_seconds,
149 summary.cumulative_time_in_seconds);
150 if (log_to_stdout_) {
151 cout << output << endl;
152 } else {
153 VLOG(1) << output;
154 }
155 return SOLVER_CONTINUE;
156 }
157
158 private:
159 const bool log_to_stdout_;
160};
161
162
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700163// Basic callback to record the execution of the solver to a file for
164// offline analysis.
165class FileLoggingCallback : public IterationCallback {
166 public:
167 explicit FileLoggingCallback(const string& filename)
168 : fptr_(NULL) {
169 fptr_ = fopen(filename.c_str(), "w");
170 CHECK_NOTNULL(fptr_);
171 }
172
173 virtual ~FileLoggingCallback() {
174 if (fptr_ != NULL) {
175 fclose(fptr_);
176 }
177 }
178
179 virtual CallbackReturnType operator()(const IterationSummary& summary) {
180 fprintf(fptr_,
181 "%4d %e %e\n",
182 summary.iteration,
183 summary.cost,
184 summary.cumulative_time_in_seconds);
185 return SOLVER_CONTINUE;
186 }
187 private:
188 FILE* fptr_;
189};
190
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800191// Iterate over each of the groups in order of their priority and fill
192// summary with their sizes.
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800193void SummarizeOrdering(ParameterBlockOrdering* ordering,
194 vector<int>* summary) {
195 CHECK_NOTNULL(summary)->clear();
196 if (ordering == NULL) {
197 return;
198 }
199
200 const map<int, set<double*> >& group_to_elements =
201 ordering->group_to_elements();
202 for (map<int, set<double*> >::const_iterator it = group_to_elements.begin();
203 it != group_to_elements.end();
204 ++it) {
205 summary->push_back(it->second.size());
206 }
207}
208
Keir Mierle8ebb0732012-04-30 23:09:08 -0700209} // namespace
210
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800211void SolverImpl::TrustRegionMinimize(
212 const Solver::Options& options,
213 Program* program,
214 CoordinateDescentMinimizer* inner_iteration_minimizer,
215 Evaluator* evaluator,
216 LinearSolver* linear_solver,
217 double* parameters,
218 Solver::Summary* summary) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700219 Minimizer::Options minimizer_options(options);
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700220
221 // TODO(sameeragarwal): Add support for logging the configuration
222 // and more detailed stats.
223 scoped_ptr<IterationCallback> file_logging_callback;
224 if (!options.solver_log.empty()) {
225 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
226 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
227 file_logging_callback.get());
228 }
229
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800230 TrustRegionLoggingCallback logging_callback(
231 options.minimizer_progress_to_stdout);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700232 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700233 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
234 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700235 }
236
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700237 StateUpdatingCallback updating_callback(program, parameters);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700238 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700239 // This must get pushed to the front of the callbacks so that it is run
240 // before any of the user callbacks.
241 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
242 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700243 }
244
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700245 minimizer_options.evaluator = evaluator;
246 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800247
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700248 minimizer_options.jacobian = jacobian.get();
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700249 minimizer_options.inner_iteration_minimizer = inner_iteration_minimizer;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700250
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700251 TrustRegionStrategy::Options trust_region_strategy_options;
252 trust_region_strategy_options.linear_solver = linear_solver;
253 trust_region_strategy_options.initial_radius =
254 options.initial_trust_region_radius;
255 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
256 trust_region_strategy_options.lm_min_diagonal = options.lm_min_diagonal;
257 trust_region_strategy_options.lm_max_diagonal = options.lm_max_diagonal;
258 trust_region_strategy_options.trust_region_strategy_type =
259 options.trust_region_strategy_type;
Markus Moll51cf7cb2012-08-20 20:10:20 +0200260 trust_region_strategy_options.dogleg_type = options.dogleg_type;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700261 scoped_ptr<TrustRegionStrategy> strategy(
262 TrustRegionStrategy::Create(trust_region_strategy_options));
263 minimizer_options.trust_region_strategy = strategy.get();
264
265 TrustRegionMinimizer minimizer;
Petter Strandmark76533b32012-09-04 22:08:50 -0700266 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700267 minimizer.Minimize(minimizer_options, parameters, summary);
Petter Strandmark76533b32012-09-04 22:08:50 -0700268 summary->minimizer_time_in_seconds =
269 WallTimeInSeconds() - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700270}
271
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700272#ifndef CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800273void SolverImpl::LineSearchMinimize(
274 const Solver::Options& options,
275 Program* program,
276 Evaluator* evaluator,
277 double* parameters,
278 Solver::Summary* summary) {
279 Minimizer::Options minimizer_options(options);
280
281 // TODO(sameeragarwal): Add support for logging the configuration
282 // and more detailed stats.
283 scoped_ptr<IterationCallback> file_logging_callback;
284 if (!options.solver_log.empty()) {
285 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
286 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
287 file_logging_callback.get());
288 }
289
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800290 LineSearchLoggingCallback logging_callback(
291 options.minimizer_progress_to_stdout);
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800292 if (options.logging_type != SILENT) {
293 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
294 &logging_callback);
295 }
296
297 StateUpdatingCallback updating_callback(program, parameters);
298 if (options.update_state_every_iteration) {
299 // This must get pushed to the front of the callbacks so that it is run
300 // before any of the user callbacks.
301 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
302 &updating_callback);
303 }
304
305 minimizer_options.evaluator = evaluator;
306
307 LineSearchMinimizer minimizer;
308 double minimizer_start_time = WallTimeInSeconds();
309 minimizer.Minimize(minimizer_options, parameters, summary);
310 summary->minimizer_time_in_seconds =
311 WallTimeInSeconds() - minimizer_start_time;
312}
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700313#endif // CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800314
315void SolverImpl::Solve(const Solver::Options& options,
316 ProblemImpl* problem_impl,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700317 Solver::Summary* summary) {
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800318 if (options.minimizer_type == TRUST_REGION) {
319 TrustRegionSolve(options, problem_impl, summary);
320 } else {
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700321#ifndef CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800322 LineSearchSolve(options, problem_impl, summary);
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700323#else
324 LOG(FATAL) << "Ceres Solver was compiled with -DLINE_SEARCH_MINIMIZER=OFF";
325#endif
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800326 }
327}
328
329void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
330 ProblemImpl* original_problem_impl,
331 Solver::Summary* summary) {
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800332 EventLogger event_logger("TrustRegionSolve");
Petter Strandmark76533b32012-09-04 22:08:50 -0700333 double solver_start_time = WallTimeInSeconds();
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700334
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700335 Program* original_program = original_problem_impl->mutable_program();
336 ProblemImpl* problem_impl = original_problem_impl;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700337
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700338 // Reset the summary object to its default values.
339 *CHECK_NOTNULL(summary) = Solver::Summary();
340
Sameer Agarwald010de52013-02-15 14:26:56 -0800341 summary->minimizer_type = TRUST_REGION;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700342 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
343 summary->num_parameters = problem_impl->NumParameters();
Keir Mierleba944212013-02-25 12:46:44 -0800344 summary->num_effective_parameters =
345 original_program->NumEffectiveParameters();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700346 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
347 summary->num_residuals = problem_impl->NumResiduals();
348
349 // Empty programs are usually a user error.
350 if (summary->num_parameter_blocks == 0) {
351 summary->error = "Problem contains no parameter blocks.";
352 LOG(ERROR) << summary->error;
353 return;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700354 }
355
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700356 if (summary->num_residual_blocks == 0) {
357 summary->error = "Problem contains no residual blocks.";
358 LOG(ERROR) << summary->error;
359 return;
360 }
361
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800362 SummarizeOrdering(original_options.linear_solver_ordering,
363 &(summary->linear_solver_ordering_given));
364
365 SummarizeOrdering(original_options.inner_iteration_ordering,
366 &(summary->inner_iteration_ordering_given));
367
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700368 Solver::Options options(original_options);
369 options.linear_solver_ordering = NULL;
370 options.inner_iteration_ordering = NULL;
371
Keir Mierle8ebb0732012-04-30 23:09:08 -0700372#ifndef CERES_USE_OPENMP
373 if (options.num_threads > 1) {
374 LOG(WARNING)
375 << "OpenMP support is not compiled into this binary; "
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700376 << "only options.num_threads=1 is supported. Switching "
Keir Mierle8ebb0732012-04-30 23:09:08 -0700377 << "to single threaded mode.";
378 options.num_threads = 1;
379 }
380 if (options.num_linear_solver_threads > 1) {
381 LOG(WARNING)
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700382 << "OpenMP support is not compiled into this binary; "
383 << "only options.num_linear_solver_threads=1 is supported. Switching "
Keir Mierle8ebb0732012-04-30 23:09:08 -0700384 << "to single threaded mode.";
385 options.num_linear_solver_threads = 1;
386 }
387#endif
388
Keir Mierle8ebb0732012-04-30 23:09:08 -0700389 summary->num_threads_given = original_options.num_threads;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700390 summary->num_threads_used = options.num_threads;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700391
392 if (options.lsqp_iterations_to_dump.size() > 0) {
393 LOG(WARNING) << "Dumping linear least squares problems to disk is"
394 " currently broken. Ignoring Solver::Options::lsqp_iterations_to_dump";
395 }
396
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800397 event_logger.AddEvent("Init");
Sameer Agarwalf102a682013-02-11 15:08:40 -0800398
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700399 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800400 event_logger.AddEvent("SetParameterBlockPtrs");
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700401
402 // If the user requests gradient checking, construct a new
403 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
404 // GradientCheckingCostFunction and replacing problem_impl with
405 // gradient_checking_problem_impl.
406 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
407 if (options.check_gradients) {
408 VLOG(1) << "Checking Gradients";
409 gradient_checking_problem_impl.reset(
410 CreateGradientCheckingProblemImpl(
411 problem_impl,
412 options.numeric_derivative_relative_step_size,
413 options.gradient_check_relative_precision));
414
415 // From here on, problem_impl will point to the gradient checking
416 // version.
417 problem_impl = gradient_checking_problem_impl.get();
418 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700419
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700420 if (original_options.linear_solver_ordering != NULL) {
Sameer Agarwal65625f72012-09-17 12:06:57 -0700421 if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700422 LOG(ERROR) << summary->error;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700423 return;
424 }
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800425 event_logger.AddEvent("CheckOrdering");
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700426 options.linear_solver_ordering =
427 new ParameterBlockOrdering(*original_options.linear_solver_ordering);
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800428 event_logger.AddEvent("CopyOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -0700429 } else {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700430 options.linear_solver_ordering = new ParameterBlockOrdering;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700431 const ProblemImpl::ParameterMap& parameter_map =
432 problem_impl->parameter_map();
433 for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
434 it != parameter_map.end();
435 ++it) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700436 options.linear_solver_ordering->AddElementToGroup(it->first, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700437 }
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800438 event_logger.AddEvent("ConstructOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -0700439 }
440
Keir Mierle8ebb0732012-04-30 23:09:08 -0700441 // Create the three objects needed to minimize: the transformed program, the
442 // evaluator, and the linear solver.
Keir Mierle31c1e782012-08-17 16:16:32 -0700443 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
444 problem_impl,
445 &summary->fixed_cost,
446 &summary->error));
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800447
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800448 event_logger.AddEvent("CreateReducedProgram");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700449 if (reduced_program == NULL) {
450 return;
451 }
452
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800453 SummarizeOrdering(options.linear_solver_ordering,
454 &(summary->linear_solver_ordering_used));
455
Keir Mierle8ebb0732012-04-30 23:09:08 -0700456 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
457 summary->num_parameters_reduced = reduced_program->NumParameters();
Keir Mierleba944212013-02-25 12:46:44 -0800458 summary->num_effective_parameters_reduced =
459 reduced_program->NumEffectiveParameters();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700460 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
461 summary->num_residuals_reduced = reduced_program->NumResiduals();
462
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700463 if (summary->num_parameter_blocks_reduced == 0) {
464 summary->preprocessor_time_in_seconds =
465 WallTimeInSeconds() - solver_start_time;
466
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800467 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700468 LOG(INFO) << "Terminating: FUNCTION_TOLERANCE reached. "
469 << "No non-constant parameter blocks found.";
470
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800471 summary->initial_cost = summary->fixed_cost;
472 summary->final_cost = summary->fixed_cost;
473
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700474 // FUNCTION_TOLERANCE is the right convergence here, as we know
475 // that the objective function is constant and cannot be changed
476 // any further.
477 summary->termination_type = FUNCTION_TOLERANCE;
478
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700479 // Ensure the program state is set to the user parameters on the way out.
480 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
481
482 summary->postprocessor_time_in_seconds =
483 WallTimeInSeconds() - post_process_start_time;
484 return;
485 }
486
Keir Mierle8ebb0732012-04-30 23:09:08 -0700487 scoped_ptr<LinearSolver>
488 linear_solver(CreateLinearSolver(&options, &summary->error));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800489 event_logger.AddEvent("CreateLinearSolver");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700490 if (linear_solver == NULL) {
491 return;
492 }
493
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700494 summary->linear_solver_type_given = original_options.linear_solver_type;
495 summary->linear_solver_type_used = options.linear_solver_type;
496
497 summary->preconditioner_type = options.preconditioner_type;
498
499 summary->num_linear_solver_threads_given =
500 original_options.num_linear_solver_threads;
501 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
502
503 summary->sparse_linear_algebra_library =
504 options.sparse_linear_algebra_library;
505
506 summary->trust_region_strategy_type = options.trust_region_strategy_type;
507 summary->dogleg_type = options.dogleg_type;
508
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700509 scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
510 problem_impl->parameter_map(),
511 reduced_program.get(),
512 &summary->error));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800513
514 event_logger.AddEvent("CreateEvaluator");
515
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700516 if (evaluator == NULL) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700517 return;
518 }
519
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700520 scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700521 if (options.use_inner_iterations) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700522 if (reduced_program->parameter_blocks().size() < 2) {
523 LOG(WARNING) << "Reduced problem only contains one parameter block."
524 << "Disabling inner iterations.";
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700525 } else {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700526 inner_iteration_minimizer.reset(
527 CreateInnerIterationMinimizer(original_options,
528 *reduced_program,
529 problem_impl->parameter_map(),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800530 summary));
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700531 if (inner_iteration_minimizer == NULL) {
532 LOG(ERROR) << summary->error;
533 return;
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700534 }
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700535 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700536 }
537
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800538 event_logger.AddEvent("CreateIIM");
539
Keir Mierle8ebb0732012-04-30 23:09:08 -0700540 // The optimizer works on contiguous parameter vectors; allocate some.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700541 Vector parameters(reduced_program->NumParameters());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700542
543 // Collect the discontiguous parameters into a contiguous state vector.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700544 reduced_program->ParameterBlocksToStateVector(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700545
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700546 Vector original_parameters = parameters;
547
Petter Strandmark76533b32012-09-04 22:08:50 -0700548 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalfa015192012-06-11 14:21:42 -0700549 summary->preprocessor_time_in_seconds =
550 minimizer_start_time - solver_start_time;
551
Keir Mierle8ebb0732012-04-30 23:09:08 -0700552 // Run the optimization.
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800553 TrustRegionMinimize(options,
554 reduced_program.get(),
555 inner_iteration_minimizer.get(),
556 evaluator.get(),
557 linear_solver.get(),
558 parameters.data(),
559 summary);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800560 event_logger.AddEvent("Minimize");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700561
Sameer Agarwalf102a682013-02-11 15:08:40 -0800562 SetSummaryFinalCost(summary);
563
Keir Mierle8ebb0732012-04-30 23:09:08 -0700564 // If the user aborted mid-optimization or the optimization
565 // terminated because of a numerical failure, then return without
566 // updating user state.
567 if (summary->termination_type == USER_ABORT ||
568 summary->termination_type == NUMERICAL_FAILURE) {
569 return;
570 }
571
Petter Strandmark76533b32012-09-04 22:08:50 -0700572 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700573
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800574 // Push the contiguous optimized parameters back to the user's
575 // parameters.
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700576 reduced_program->StateVectorToParameterBlocks(parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700577 reduced_program->CopyParameterBlockStateToUserState();
578
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800579 // Ensure the program state is set to the user parameters on the way
580 // out.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700581 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700582
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800583 const map<string, double>& linear_solver_time_statistics =
584 linear_solver->TimeStatistics();
585 summary->linear_solver_time_in_seconds =
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800586 FindWithDefault(linear_solver_time_statistics,
587 "LinearSolver::Solve",
588 0.0);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800589
590 const map<string, double>& evaluator_time_statistics =
591 evaluator->TimeStatistics();
592
593 summary->residual_evaluation_time_in_seconds =
594 FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
595 summary->jacobian_evaluation_time_in_seconds =
596 FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
597
Keir Mierle8ebb0732012-04-30 23:09:08 -0700598 // Stick a fork in it, we're done.
Petter Strandmark76533b32012-09-04 22:08:50 -0700599 summary->postprocessor_time_in_seconds =
600 WallTimeInSeconds() - post_process_start_time;
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800601 event_logger.AddEvent("PostProcess");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700602}
603
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700604
605#ifndef CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800606void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
607 ProblemImpl* original_problem_impl,
608 Solver::Summary* summary) {
609 double solver_start_time = WallTimeInSeconds();
610
611 Program* original_program = original_problem_impl->mutable_program();
612 ProblemImpl* problem_impl = original_problem_impl;
613
614 // Reset the summary object to its default values.
615 *CHECK_NOTNULL(summary) = Solver::Summary();
616
Sameer Agarwald010de52013-02-15 14:26:56 -0800617 summary->minimizer_type = LINE_SEARCH;
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800618 summary->line_search_direction_type =
619 original_options.line_search_direction_type;
Sameer Agarwald010de52013-02-15 14:26:56 -0800620 summary->max_lbfgs_rank = original_options.max_lbfgs_rank;
621 summary->line_search_type = original_options.line_search_type;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800622 summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
623 summary->num_parameters = problem_impl->NumParameters();
624 summary->num_residual_blocks = problem_impl->NumResidualBlocks();
625 summary->num_residuals = problem_impl->NumResiduals();
626
627 // Empty programs are usually a user error.
628 if (summary->num_parameter_blocks == 0) {
629 summary->error = "Problem contains no parameter blocks.";
630 LOG(ERROR) << summary->error;
631 return;
632 }
633
634 if (summary->num_residual_blocks == 0) {
635 summary->error = "Problem contains no residual blocks.";
636 LOG(ERROR) << summary->error;
637 return;
638 }
639
640 Solver::Options options(original_options);
641
642 // This ensures that we get a Block Jacobian Evaluator along with
643 // none of the Schur nonsense. This file will have to be extensively
644 // refactored to deal with the various bits of cleanups related to
645 // line search.
646 options.linear_solver_type = CGNR;
647
648 options.linear_solver_ordering = NULL;
649 options.inner_iteration_ordering = NULL;
650
651#ifndef CERES_USE_OPENMP
652 if (options.num_threads > 1) {
653 LOG(WARNING)
654 << "OpenMP support is not compiled into this binary; "
655 << "only options.num_threads=1 is supported. Switching "
656 << "to single threaded mode.";
657 options.num_threads = 1;
658 }
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700659#endif // CERES_USE_OPENMP
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800660
661 summary->num_threads_given = original_options.num_threads;
662 summary->num_threads_used = options.num_threads;
663
664 if (original_options.linear_solver_ordering != NULL) {
665 if (!IsOrderingValid(original_options, problem_impl, &summary->error)) {
666 LOG(ERROR) << summary->error;
667 return;
668 }
669 options.linear_solver_ordering =
670 new ParameterBlockOrdering(*original_options.linear_solver_ordering);
671 } else {
672 options.linear_solver_ordering = new ParameterBlockOrdering;
673 const ProblemImpl::ParameterMap& parameter_map =
674 problem_impl->parameter_map();
675 for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
676 it != parameter_map.end();
677 ++it) {
678 options.linear_solver_ordering->AddElementToGroup(it->first, 0);
679 }
680 }
681
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800682 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
683
684 // If the user requests gradient checking, construct a new
685 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
686 // GradientCheckingCostFunction and replacing problem_impl with
687 // gradient_checking_problem_impl.
688 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
689 if (options.check_gradients) {
690 VLOG(1) << "Checking Gradients";
691 gradient_checking_problem_impl.reset(
692 CreateGradientCheckingProblemImpl(
693 problem_impl,
694 options.numeric_derivative_relative_step_size,
695 options.gradient_check_relative_precision));
696
697 // From here on, problem_impl will point to the gradient checking
698 // version.
699 problem_impl = gradient_checking_problem_impl.get();
700 }
701
702 // Create the three objects needed to minimize: the transformed program, the
703 // evaluator, and the linear solver.
704 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
705 problem_impl,
706 &summary->fixed_cost,
707 &summary->error));
708 if (reduced_program == NULL) {
709 return;
710 }
711
712 summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
713 summary->num_parameters_reduced = reduced_program->NumParameters();
714 summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
715 summary->num_residuals_reduced = reduced_program->NumResiduals();
716
717 if (summary->num_parameter_blocks_reduced == 0) {
718 summary->preprocessor_time_in_seconds =
719 WallTimeInSeconds() - solver_start_time;
720
721 LOG(INFO) << "Terminating: FUNCTION_TOLERANCE reached. "
722 << "No non-constant parameter blocks found.";
723
724 // FUNCTION_TOLERANCE is the right convergence here, as we know
725 // that the objective function is constant and cannot be changed
726 // any further.
727 summary->termination_type = FUNCTION_TOLERANCE;
728
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800729 const double post_process_start_time = WallTimeInSeconds();
Sameer Agarwalf102a682013-02-11 15:08:40 -0800730
731 SetSummaryFinalCost(summary);
732
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800733 // Ensure the program state is set to the user parameters on the way out.
734 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal956ed7e2013-02-19 07:06:15 -0800735 summary->postprocessor_time_in_seconds =
736 WallTimeInSeconds() - post_process_start_time;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800737 return;
738 }
739
740 scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
741 problem_impl->parameter_map(),
742 reduced_program.get(),
743 &summary->error));
744 if (evaluator == NULL) {
745 return;
746 }
747
748 // The optimizer works on contiguous parameter vectors; allocate some.
749 Vector parameters(reduced_program->NumParameters());
750
751 // Collect the discontiguous parameters into a contiguous state vector.
752 reduced_program->ParameterBlocksToStateVector(parameters.data());
753
754 Vector original_parameters = parameters;
755
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800756 const double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800757 summary->preprocessor_time_in_seconds =
758 minimizer_start_time - solver_start_time;
759
760 // Run the optimization.
761 LineSearchMinimize(options,
762 reduced_program.get(),
763 evaluator.get(),
764 parameters.data(),
765 summary);
766
767 // If the user aborted mid-optimization or the optimization
768 // terminated because of a numerical failure, then return without
769 // updating user state.
770 if (summary->termination_type == USER_ABORT ||
771 summary->termination_type == NUMERICAL_FAILURE) {
772 return;
773 }
774
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800775 const double post_process_start_time = WallTimeInSeconds();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800776
777 // Push the contiguous optimized parameters back to the user's parameters.
778 reduced_program->StateVectorToParameterBlocks(parameters.data());
779 reduced_program->CopyParameterBlockStateToUserState();
780
Sameer Agarwalf102a682013-02-11 15:08:40 -0800781 SetSummaryFinalCost(summary);
782
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800783 // Ensure the program state is set to the user parameters on the way out.
784 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
785
Sameer Agarwald010de52013-02-15 14:26:56 -0800786 const map<string, double>& evaluator_time_statistics =
787 evaluator->TimeStatistics();
788
789 summary->residual_evaluation_time_in_seconds =
790 FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
791 summary->jacobian_evaluation_time_in_seconds =
792 FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
793
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800794 // Stick a fork in it, we're done.
795 summary->postprocessor_time_in_seconds =
796 WallTimeInSeconds() - post_process_start_time;
797}
Sameer Agarwal700d50d2013-03-12 16:12:42 -0700798#endif // CERES_NO_LINE_SEARCH_MINIMIZER
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800799
Sameer Agarwal65625f72012-09-17 12:06:57 -0700800bool SolverImpl::IsOrderingValid(const Solver::Options& options,
801 const ProblemImpl* problem_impl,
802 string* error) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700803 if (options.linear_solver_ordering->NumElements() !=
Sameer Agarwal65625f72012-09-17 12:06:57 -0700804 problem_impl->NumParameterBlocks()) {
805 *error = "Number of parameter blocks in user supplied ordering "
806 "does not match the number of parameter blocks in the problem";
807 return false;
808 }
809
810 const Program& program = problem_impl->program();
811 const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -0700812 for (vector<ParameterBlock*>::const_iterator it = parameter_blocks.begin();
813 it != parameter_blocks.end();
814 ++it) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700815 if (!options.linear_solver_ordering
816 ->IsMember(const_cast<double*>((*it)->user_state()))) {
Sameer Agarwal65625f72012-09-17 12:06:57 -0700817 *error = "Problem contains a parameter block that is not in "
818 "the user specified ordering.";
819 return false;
820 }
821 }
822
823 if (IsSchurType(options.linear_solver_type) &&
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700824 options.linear_solver_ordering->NumGroups() > 1) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700825 const vector<ResidualBlock*>& residual_blocks = program.residual_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -0700826 const set<double*>& e_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700827 options.linear_solver_ordering->group_to_elements().begin()->second;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700828 if (!IsParameterBlockSetIndependent(e_blocks, residual_blocks)) {
829 *error = "The user requested the use of a Schur type solver. "
830 "But the first elimination group in the ordering is not an "
831 "independent set.";
832 return false;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700833 }
834 }
835 return true;
836}
837
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800838bool SolverImpl::IsParameterBlockSetIndependent(
839 const set<double*>& parameter_block_ptrs,
840 const vector<ResidualBlock*>& residual_blocks) {
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700841 // Loop over each residual block and ensure that no two parameter
842 // blocks in the same residual block are part of
843 // parameter_block_ptrs as that would violate the assumption that it
844 // is an independent set in the Hessian matrix.
845 for (vector<ResidualBlock*>::const_iterator it = residual_blocks.begin();
846 it != residual_blocks.end();
847 ++it) {
848 ParameterBlock* const* parameter_blocks = (*it)->parameter_blocks();
849 const int num_parameter_blocks = (*it)->NumParameterBlocks();
850 int count = 0;
851 for (int i = 0; i < num_parameter_blocks; ++i) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700852 count += parameter_block_ptrs.count(
853 parameter_blocks[i]->mutable_user_state());
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700854 }
855 if (count > 1) {
856 return false;
857 }
858 }
859 return true;
860}
861
862
Keir Mierle8ebb0732012-04-30 23:09:08 -0700863// Strips varying parameters and residuals, maintaining order, and updating
864// num_eliminate_blocks.
865bool SolverImpl::RemoveFixedBlocksFromProgram(Program* program,
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700866 ParameterBlockOrdering* ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200867 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700868 string* error) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700869 vector<ParameterBlock*>* parameter_blocks =
870 program->mutable_parameter_blocks();
871
Markus Moll8de78db2012-07-14 15:49:11 +0200872 scoped_array<double> residual_block_evaluate_scratch;
873 if (fixed_cost != NULL) {
Keir Mierlec161a9d2012-07-18 14:01:48 -0700874 residual_block_evaluate_scratch.reset(
Markus Moll8de78db2012-07-14 15:49:11 +0200875 new double[program->MaxScratchDoublesNeededForEvaluate()]);
876 *fixed_cost = 0.0;
877 }
878
Keir Mierle8ebb0732012-04-30 23:09:08 -0700879 // Mark all the parameters as unused. Abuse the index member of the parameter
880 // blocks for the marking.
881 for (int i = 0; i < parameter_blocks->size(); ++i) {
882 (*parameter_blocks)[i]->set_index(-1);
883 }
884
885 // Filter out residual that have all-constant parameters, and mark all the
886 // parameter blocks that appear in residuals.
887 {
888 vector<ResidualBlock*>* residual_blocks =
889 program->mutable_residual_blocks();
890 int j = 0;
891 for (int i = 0; i < residual_blocks->size(); ++i) {
892 ResidualBlock* residual_block = (*residual_blocks)[i];
893 int num_parameter_blocks = residual_block->NumParameterBlocks();
894
895 // Determine if the residual block is fixed, and also mark varying
896 // parameters that appear in the residual block.
897 bool all_constant = true;
898 for (int k = 0; k < num_parameter_blocks; k++) {
899 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
900 if (!parameter_block->IsConstant()) {
901 all_constant = false;
902 parameter_block->set_index(1);
903 }
904 }
905
906 if (!all_constant) {
907 (*residual_blocks)[j++] = (*residual_blocks)[i];
Markus Moll8de78db2012-07-14 15:49:11 +0200908 } else if (fixed_cost != NULL) {
909 // The residual is constant and will be removed, so its cost is
910 // added to the variable fixed_cost.
911 double cost = 0.0;
Sameer Agarwal039ff072013-02-26 09:15:39 -0800912 if (!residual_block->Evaluate(true,
913 &cost,
914 NULL,
915 NULL,
916 residual_block_evaluate_scratch.get())) {
Markus Moll8de78db2012-07-14 15:49:11 +0200917 *error = StringPrintf("Evaluation of the residual %d failed during "
918 "removal of fixed residual blocks.", i);
919 return false;
920 }
921 *fixed_cost += cost;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700922 }
923 }
924 residual_blocks->resize(j);
925 }
926
927 // Filter out unused or fixed parameter blocks, and update
Sameer Agarwal65625f72012-09-17 12:06:57 -0700928 // the ordering.
Keir Mierle8ebb0732012-04-30 23:09:08 -0700929 {
930 vector<ParameterBlock*>* parameter_blocks =
931 program->mutable_parameter_blocks();
932 int j = 0;
933 for (int i = 0; i < parameter_blocks->size(); ++i) {
934 ParameterBlock* parameter_block = (*parameter_blocks)[i];
935 if (parameter_block->index() == 1) {
936 (*parameter_blocks)[j++] = parameter_block;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700937 } else {
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700938 ordering->Remove(parameter_block->mutable_user_state());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700939 }
940 }
941 parameter_blocks->resize(j);
942 }
943
Sameer Agarwalc8f07902013-04-19 08:01:04 -0700944 if (!(((program->NumResidualBlocks() == 0) &&
Keir Mierle8ebb0732012-04-30 23:09:08 -0700945 (program->NumParameterBlocks() == 0)) ||
946 ((program->NumResidualBlocks() != 0) &&
Sameer Agarwalc8f07902013-04-19 08:01:04 -0700947 (program->NumParameterBlocks() != 0)))) {
948 *error = "Congratulations, you found a bug in Ceres. Please report it.";
949 return false;
950 }
951
Keir Mierle8ebb0732012-04-30 23:09:08 -0700952 return true;
953}
954
955Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
956 ProblemImpl* problem_impl,
Markus Moll8de78db2012-07-14 15:49:11 +0200957 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700958 string* error) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700959 CHECK_NOTNULL(options->linear_solver_ordering);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700960 Program* original_program = problem_impl->mutable_program();
961 scoped_ptr<Program> transformed_program(new Program(*original_program));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800962
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700963 ParameterBlockOrdering* linear_solver_ordering =
964 options->linear_solver_ordering;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700965 const int min_group_id =
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700966 linear_solver_ordering->group_to_elements().begin()->first;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700967
968 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700969 linear_solver_ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200970 fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700971 error)) {
972 return NULL;
973 }
974
975 if (transformed_program->NumParameterBlocks() == 0) {
976 LOG(WARNING) << "No varying parameter blocks to optimize; "
977 << "bailing early.";
978 return transformed_program.release();
979 }
980
Sameer Agarwal65625f72012-09-17 12:06:57 -0700981 if (IsSchurType(options->linear_solver_type) &&
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700982 linear_solver_ordering->GroupSize(min_group_id) == 0) {
Sameer Agarwalc8f07902013-04-19 08:01:04 -0700983 // If the user requested the use of a Schur type solver, and
984 // supplied a non-NULL linear_solver_ordering object with more than
985 // one elimination group, then it can happen that after all the
986 // parameter blocks which are fixed or unused have been removed from
987 // the program and the ordering, there are no more parameter blocks
988 // in the first elimination group.
989 //
990 // In such a case, the use of a Schur type solver is not possible,
991 // as they assume there is at least one e_block. Thus, we
992 // automatically switch to the closest solver to the one indicated
993 // by the user.
994 AlternateLinearSolverForSchurTypeLinearSolver(options);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700995 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700996
Sameer Agarwalc8f07902013-04-19 08:01:04 -0700997 if (IsSchurType(options->linear_solver_type)) {
998 if (!ReorderProgramForSchurTypeLinearSolver(problem_impl->parameter_map(),
999 linear_solver_ordering,
1000 transformed_program.get(),
1001 error)) {
1002 return NULL;
1003 }
1004 return transformed_program.release();
1005 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001006
Sameer Agarwal9189f4e2013-04-19 17:09:49 -07001007 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
1008 options->sparse_linear_algebra_library == SUITE_SPARSE) {
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001009 ReorderProgramForSparseNormalCholesky(transformed_program.get());
1010 return transformed_program.release();
1011 }
1012
Keir Mierle8ebb0732012-04-30 23:09:08 -07001013 transformed_program->SetParameterOffsetsAndIndex();
1014 return transformed_program.release();
1015}
1016
1017LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
1018 string* error) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001019 CHECK_NOTNULL(options);
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001020 CHECK_NOTNULL(options->linear_solver_ordering);
Sameer Agarwal65625f72012-09-17 12:06:57 -07001021 CHECK_NOTNULL(error);
1022
Sameer Agarwal5ecd2512012-06-17 16:34:03 -07001023 if (options->trust_region_strategy_type == DOGLEG) {
1024 if (options->linear_solver_type == ITERATIVE_SCHUR ||
1025 options->linear_solver_type == CGNR) {
1026 *error = "DOGLEG only supports exact factorization based linear "
1027 "solvers. If you want to use an iterative solver please "
1028 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
1029 return NULL;
1030 }
1031 }
1032
Keir Mierle8ebb0732012-04-30 23:09:08 -07001033#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -07001034 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
1035 options->sparse_linear_algebra_library == SUITE_SPARSE) {
1036 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
1037 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001038 return NULL;
1039 }
Sameer Agarwal65625f72012-09-17 12:06:57 -07001040
Petter Strandmarkdd1a2762012-09-19 12:55:05 +02001041 if (options->preconditioner_type == CLUSTER_JACOBI) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001042 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
1043 "with SuiteSparse support.";
1044 return NULL;
1045 }
1046
Petter Strandmarkdd1a2762012-09-19 12:55:05 +02001047 if (options->preconditioner_type == CLUSTER_TRIDIAGONAL) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001048 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
1049 "Ceres with SuiteSparse support.";
1050 return NULL;
1051 }
Sameer Agarwalb0518732012-05-29 00:27:57 -07001052#endif
1053
1054#ifdef CERES_NO_CXSPARSE
1055 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
1056 options->sparse_linear_algebra_library == CX_SPARSE) {
1057 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
1058 "CXSparse was not enabled when Ceres was built.";
1059 return NULL;
1060 }
1061#endif
1062
Sameer Agarwal65625f72012-09-17 12:06:57 -07001063#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle27dd0d32012-10-15 13:52:36 -07001064 if (options->linear_solver_type == SPARSE_SCHUR) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001065 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
1066 "CXSparse was enabled when Ceres was compiled.";
1067 return NULL;
1068 }
1069#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -07001070
1071 if (options->linear_solver_max_num_iterations <= 0) {
1072 *error = "Solver::Options::linear_solver_max_num_iterations is 0.";
1073 return NULL;
1074 }
1075 if (options->linear_solver_min_num_iterations <= 0) {
1076 *error = "Solver::Options::linear_solver_min_num_iterations is 0.";
1077 return NULL;
1078 }
1079 if (options->linear_solver_min_num_iterations >
1080 options->linear_solver_max_num_iterations) {
1081 *error = "Solver::Options::linear_solver_min_num_iterations > "
1082 "Solver::Options::linear_solver_max_num_iterations.";
1083 return NULL;
1084 }
1085
1086 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001087 linear_solver_options.min_num_iterations =
1088 options->linear_solver_min_num_iterations;
1089 linear_solver_options.max_num_iterations =
1090 options->linear_solver_max_num_iterations;
1091 linear_solver_options.type = options->linear_solver_type;
1092 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalb0518732012-05-29 00:27:57 -07001093 linear_solver_options.sparse_linear_algebra_library =
1094 options->sparse_linear_algebra_library;
Sameer Agarwal9189f4e2013-04-19 17:09:49 -07001095 linear_solver_options.use_postordering = options->use_postordering;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001096 linear_solver_options.num_threads = options->num_linear_solver_threads;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001097 options->num_linear_solver_threads = linear_solver_options.num_threads;
1098
Sameer Agarwal65625f72012-09-17 12:06:57 -07001099 const map<int, set<double*> >& groups =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001100 options->linear_solver_ordering->group_to_elements();
Sameer Agarwal65625f72012-09-17 12:06:57 -07001101 for (map<int, set<double*> >::const_iterator it = groups.begin();
1102 it != groups.end();
1103 ++it) {
1104 linear_solver_options.elimination_groups.push_back(it->second.size());
1105 }
1106 // Schur type solvers, expect at least two elimination groups. If
Sameer Agarwal1dc544a2012-09-18 18:09:03 -07001107 // there is only one elimination group, then CreateReducedProgram
Sameer Agarwal65625f72012-09-17 12:06:57 -07001108 // guarantees that this group only contains e_blocks. Thus we add a
1109 // dummy elimination group with zero blocks in it.
1110 if (IsSchurType(linear_solver_options.type) &&
1111 linear_solver_options.elimination_groups.size() == 1) {
1112 linear_solver_options.elimination_groups.push_back(0);
1113 }
1114
Keir Mierle8ebb0732012-04-30 23:09:08 -07001115 return LinearSolver::Create(linear_solver_options);
1116}
1117
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001118bool SolverImpl::ApplyUserOrdering(
1119 const ProblemImpl::ParameterMap& parameter_map,
1120 const ParameterBlockOrdering* ordering,
1121 Program* program,
1122 string* error) {
Sameer Agarwal2c94eed2012-10-01 16:34:37 -07001123 if (ordering->NumElements() != program->NumParameterBlocks()) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001124 *error = StringPrintf("User specified ordering does not have the same "
1125 "number of parameters as the problem. The problem"
Sameer Agarwal65625f72012-09-17 12:06:57 -07001126 "has %d blocks while the ordering has %d blocks.",
Keir Mierle8ebb0732012-04-30 23:09:08 -07001127 program->NumParameterBlocks(),
Sameer Agarwal2c94eed2012-10-01 16:34:37 -07001128 ordering->NumElements());
Keir Mierle8ebb0732012-04-30 23:09:08 -07001129 return false;
1130 }
1131
Keir Mierle8ebb0732012-04-30 23:09:08 -07001132 vector<ParameterBlock*>* parameter_blocks =
1133 program->mutable_parameter_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -07001134 parameter_blocks->clear();
Keir Mierle8ebb0732012-04-30 23:09:08 -07001135
Sameer Agarwal65625f72012-09-17 12:06:57 -07001136 const map<int, set<double*> >& groups =
Sameer Agarwal2c94eed2012-10-01 16:34:37 -07001137 ordering->group_to_elements();
Keir Mierle8ebb0732012-04-30 23:09:08 -07001138
Sameer Agarwal65625f72012-09-17 12:06:57 -07001139 for (map<int, set<double*> >::const_iterator group_it = groups.begin();
1140 group_it != groups.end();
1141 ++group_it) {
1142 const set<double*>& group = group_it->second;
1143 for (set<double*>::const_iterator parameter_block_ptr_it = group.begin();
1144 parameter_block_ptr_it != group.end();
1145 ++parameter_block_ptr_it) {
1146 ProblemImpl::ParameterMap::const_iterator parameter_block_it =
1147 parameter_map.find(*parameter_block_ptr_it);
1148 if (parameter_block_it == parameter_map.end()) {
1149 *error = StringPrintf("User specified ordering contains a pointer "
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001150 "to a double that is not a parameter block in "
1151 "the problem. The invalid double is in group: %d",
Sameer Agarwal65625f72012-09-17 12:06:57 -07001152 group_it->first);
1153 return false;
1154 }
1155 parameter_blocks->push_back(parameter_block_it->second);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001156 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001157 }
1158 return true;
1159}
1160
1161// Find the minimum index of any parameter block to the given residual.
1162// Parameter blocks that have indices greater than num_eliminate_blocks are
1163// considered to have an index equal to num_eliminate_blocks.
Sergey Sharybinb53c9662013-02-25 01:14:26 +06001164static int MinParameterBlock(const ResidualBlock* residual_block,
1165 int num_eliminate_blocks) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001166 int min_parameter_block_position = num_eliminate_blocks;
1167 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
1168 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -07001169 if (!parameter_block->IsConstant()) {
1170 CHECK_NE(parameter_block->index(), -1)
1171 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
1172 << "This is a Ceres bug; please contact the developers!";
1173 min_parameter_block_position = std::min(parameter_block->index(),
1174 min_parameter_block_position);
1175 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001176 }
1177 return min_parameter_block_position;
1178}
1179
1180// Reorder the residuals for program, if necessary, so that the residuals
1181// involving each E block occur together. This is a necessary condition for the
1182// Schur eliminator, which works on these "row blocks" in the jacobian.
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001183bool SolverImpl::LexicographicallyOrderResidualBlocks(
1184 const int num_eliminate_blocks,
1185 Program* program,
1186 string* error) {
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001187 CHECK_GE(num_eliminate_blocks, 1)
1188 << "Congratulations, you found a Ceres bug! Please report this error "
1189 << "to the developers.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001190
1191 // Create a histogram of the number of residuals for each E block. There is an
1192 // extra bucket at the end to catch all non-eliminated F blocks.
Sameer Agarwal65625f72012-09-17 12:06:57 -07001193 vector<int> residual_blocks_per_e_block(num_eliminate_blocks + 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001194 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
1195 vector<int> min_position_per_residual(residual_blocks->size());
1196 for (int i = 0; i < residual_blocks->size(); ++i) {
1197 ResidualBlock* residual_block = (*residual_blocks)[i];
Sameer Agarwal65625f72012-09-17 12:06:57 -07001198 int position = MinParameterBlock(residual_block, num_eliminate_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001199 min_position_per_residual[i] = position;
Sameer Agarwal65625f72012-09-17 12:06:57 -07001200 DCHECK_LE(position, num_eliminate_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001201 residual_blocks_per_e_block[position]++;
1202 }
1203
1204 // Run a cumulative sum on the histogram, to obtain offsets to the start of
1205 // each histogram bucket (where each bucket is for the residuals for that
1206 // E-block).
Sameer Agarwal65625f72012-09-17 12:06:57 -07001207 vector<int> offsets(num_eliminate_blocks + 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001208 std::partial_sum(residual_blocks_per_e_block.begin(),
1209 residual_blocks_per_e_block.end(),
1210 offsets.begin());
1211 CHECK_EQ(offsets.back(), residual_blocks->size())
1212 << "Congratulations, you found a Ceres bug! Please report this error "
1213 << "to the developers.";
1214
1215 CHECK(find(residual_blocks_per_e_block.begin(),
1216 residual_blocks_per_e_block.end() - 1, 0) !=
1217 residual_blocks_per_e_block.end())
1218 << "Congratulations, you found a Ceres bug! Please report this error "
1219 << "to the developers.";
1220
1221 // Fill in each bucket with the residual blocks for its corresponding E block.
1222 // Each bucket is individually filled from the back of the bucket to the front
1223 // of the bucket. The filling order among the buckets is dictated by the
1224 // residual blocks. This loop uses the offsets as counters; subtracting one
1225 // from each offset as a residual block is placed in the bucket. When the
1226 // filling is finished, the offset pointerts should have shifted down one
1227 // entry (this is verified below).
1228 vector<ResidualBlock*> reordered_residual_blocks(
1229 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
1230 for (int i = 0; i < residual_blocks->size(); ++i) {
1231 int bucket = min_position_per_residual[i];
1232
1233 // Decrement the cursor, which should now point at the next empty position.
1234 offsets[bucket]--;
1235
1236 // Sanity.
1237 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
1238 << "Congratulations, you found a Ceres bug! Please report this error "
1239 << "to the developers.";
1240
1241 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
1242 }
1243
1244 // Sanity check #1: The difference in bucket offsets should match the
1245 // histogram sizes.
Sameer Agarwal65625f72012-09-17 12:06:57 -07001246 for (int i = 0; i < num_eliminate_blocks; ++i) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001247 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
1248 << "Congratulations, you found a Ceres bug! Please report this error "
1249 << "to the developers.";
1250 }
1251 // Sanity check #2: No NULL's left behind.
1252 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
1253 CHECK(reordered_residual_blocks[i] != NULL)
1254 << "Congratulations, you found a Ceres bug! Please report this error "
1255 << "to the developers.";
1256 }
1257
1258 // Now that the residuals are collected by E block, swap them in place.
1259 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
1260 return true;
1261}
1262
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001263Evaluator* SolverImpl::CreateEvaluator(
1264 const Solver::Options& options,
1265 const ProblemImpl::ParameterMap& parameter_map,
1266 Program* program,
1267 string* error) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001268 Evaluator::Options evaluator_options;
1269 evaluator_options.linear_solver_type = options.linear_solver_type;
Sameer Agarwal65625f72012-09-17 12:06:57 -07001270 evaluator_options.num_eliminate_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001271 (options.linear_solver_ordering->NumGroups() > 0 &&
Sameer Agarwal65625f72012-09-17 12:06:57 -07001272 IsSchurType(options.linear_solver_type))
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001273 ? (options.linear_solver_ordering
1274 ->group_to_elements().begin()
1275 ->second.size())
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001276 : 0;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001277 evaluator_options.num_threads = options.num_threads;
1278 return Evaluator::Create(evaluator_options, program, error);
1279}
1280
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001281CoordinateDescentMinimizer* SolverImpl::CreateInnerIterationMinimizer(
1282 const Solver::Options& options,
1283 const Program& program,
1284 const ProblemImpl::ParameterMap& parameter_map,
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001285 Solver::Summary* summary) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001286 scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer(
1287 new CoordinateDescentMinimizer);
1288 scoped_ptr<ParameterBlockOrdering> inner_iteration_ordering;
1289 ParameterBlockOrdering* ordering_ptr = NULL;
1290
1291 if (options.inner_iteration_ordering == NULL) {
1292 // Find a recursive decomposition of the Hessian matrix as a set
1293 // of independent sets of decreasing size and invert it. This
1294 // seems to work better in practice, i.e., Cameras before
1295 // points.
1296 inner_iteration_ordering.reset(new ParameterBlockOrdering);
1297 ComputeRecursiveIndependentSetOrdering(program,
1298 inner_iteration_ordering.get());
1299 inner_iteration_ordering->Reverse();
1300 ordering_ptr = inner_iteration_ordering.get();
1301 } else {
1302 const map<int, set<double*> >& group_to_elements =
1303 options.inner_iteration_ordering->group_to_elements();
1304
1305 // Iterate over each group and verify that it is an independent
1306 // set.
1307 map<int, set<double*> >::const_iterator it = group_to_elements.begin();
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001308 for ( ; it != group_to_elements.end(); ++it) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001309 if (!IsParameterBlockSetIndependent(it->second,
1310 program.residual_blocks())) {
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001311 summary->error =
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001312 StringPrintf("The user-provided "
1313 "parameter_blocks_for_inner_iterations does not "
1314 "form an independent set. Group Id: %d", it->first);
1315 return NULL;
1316 }
1317 }
1318 ordering_ptr = options.inner_iteration_ordering;
1319 }
1320
1321 if (!inner_iteration_minimizer->Init(program,
1322 parameter_map,
1323 *ordering_ptr,
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001324 &summary->error)) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001325 return NULL;
1326 }
1327
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001328 summary->inner_iterations = true;
1329 SummarizeOrdering(ordering_ptr, &(summary->inner_iteration_ordering_used));
1330
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001331 return inner_iteration_minimizer.release();
1332}
1333
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001334void SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(
1335 Solver::Options* options) {
1336 if (!IsSchurType(options->linear_solver_type)) {
1337 return;
1338 }
1339
1340 string msg = "No e_blocks remaining. Switching from ";
1341 if (options->linear_solver_type == SPARSE_SCHUR) {
1342 options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
1343 msg += "SPARSE_SCHUR to SPARSE_NORMAL_CHOLESKY.";
1344 } else if (options->linear_solver_type == DENSE_SCHUR) {
1345 // TODO(sameeragarwal): This is probably not a great choice.
1346 // Ideally, we should have a DENSE_NORMAL_CHOLESKY, that can
1347 // take a BlockSparseMatrix as input.
1348 options->linear_solver_type = DENSE_QR;
1349 msg += "DENSE_SCHUR to DENSE_QR.";
1350 } else if (options->linear_solver_type == ITERATIVE_SCHUR) {
1351 options->linear_solver_type = CGNR;
1352 if (options->preconditioner_type != IDENTITY) {
1353 msg += StringPrintf("ITERATIVE_SCHUR with %s preconditioner "
1354 "to CGNR with JACOBI preconditioner.",
1355 PreconditionerTypeToString(
1356 options->preconditioner_type));
1357 // CGNR currently only supports the JACOBI preconditioner.
1358 options->preconditioner_type = JACOBI;
1359 } else {
1360 msg += StringPrintf("ITERATIVE_SCHUR with IDENTITY preconditioner "
1361 "to CGNR with IDENTITY preconditioner.");
1362 }
1363 }
1364 LOG(WARNING) << msg;
1365}
1366
1367bool SolverImpl::ReorderProgramForSchurTypeLinearSolver(
1368 const ProblemImpl::ParameterMap& parameter_map,
1369 ParameterBlockOrdering* ordering,
1370 Program* program,
1371 string* error) {
1372 // At this point one of two things is true.
1373 //
1374 // 1. The user did not specify an ordering - ordering has one
1375 // group containined all the parameter blocks.
1376
1377 // 2. The user specified an ordering, and the first group has
1378 // non-zero elements.
1379 //
1380 // We handle these two cases in turn.
1381 if (ordering->NumGroups() == 1) {
1382 // If the user supplied an ordering with just one
1383 // group, it is equivalent to the user supplying NULL as an
1384 // ordering. Ceres is completely free to choose the parameter
1385 // block ordering as it sees fit. For Schur type solvers, this
1386 // means that the user wishes for Ceres to identify the e_blocks,
1387 // which we do by computing a maximal independent set.
1388 vector<ParameterBlock*> schur_ordering;
1389 const int num_eliminate_blocks = ComputeSchurOrdering(*program,
1390 &schur_ordering);
1391
1392 CHECK_EQ(schur_ordering.size(), program->NumParameterBlocks())
1393 << "Congratulations, you found a Ceres bug! Please report this error "
1394 << "to the developers.";
1395
1396 // Update the ordering object.
1397 for (int i = 0; i < schur_ordering.size(); ++i) {
1398 double* parameter_block = schur_ordering[i]->mutable_user_state();
1399 const int group_id = (i < num_eliminate_blocks) ? 0 : 1;
1400 ordering->AddElementToGroup(parameter_block, group_id);
1401 }
1402
1403 // Apply the parameter block re-ordering. Technically we could
1404 // call ApplyUserOrdering, but this is cheaper and simpler.
1405 swap(*program->mutable_parameter_blocks(), schur_ordering);
1406 } else {
1407 // The user supplied an ordering.
1408 if (!ApplyUserOrdering(parameter_map, ordering, program, error)) {
1409 return false;
1410 }
1411 }
1412
1413 program->SetParameterOffsetsAndIndex();
1414
1415 const int num_eliminate_blocks =
1416 ordering->group_to_elements().begin()->second.size();
1417
1418 // Schur type solvers also require that their residual blocks be
1419 // lexicographically ordered.
1420 return LexicographicallyOrderResidualBlocks(num_eliminate_blocks,
1421 program,
1422 error);
1423}
1424
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001425TripletSparseMatrix* SolverImpl::CreateJacobianBlockSparsityTranspose(
1426 const Program* program) {
1427
1428 // Matrix to store the block sparsity structure of
1429 TripletSparseMatrix* tsm =
1430 new TripletSparseMatrix(program->NumParameterBlocks(),
1431 program->NumResidualBlocks(),
1432 10 * program->NumResidualBlocks());
1433 int num_nonzeros = 0;
1434 int* rows = tsm->mutable_rows();
1435 int* cols = tsm->mutable_cols();
1436 double* values = tsm->mutable_values();
1437
1438 const vector<ResidualBlock*>& residual_blocks = program->residual_blocks();
1439 for (int c = 0; c < residual_blocks.size(); ++c) {
1440 const ResidualBlock* residual_block = residual_blocks[c];
1441 const int num_parameter_blocks = residual_block->NumParameterBlocks();
1442 ParameterBlock* const* parameter_blocks =
1443 residual_block->parameter_blocks();
1444
1445 for (int j = 0; j < num_parameter_blocks; ++j) {
1446 if (parameter_blocks[j]->IsConstant()) {
1447 continue;
1448 }
1449
1450 // Re-size the matrix if needed.
1451 if (num_nonzeros >= tsm->max_num_nonzeros()) {
1452 tsm->Reserve(2 * num_nonzeros);
1453 rows = tsm->mutable_rows();
1454 cols = tsm->mutable_cols();
1455 values = tsm->mutable_values();
1456 }
1457 CHECK_LT(num_nonzeros, tsm->max_num_nonzeros());
1458
1459 const int r = parameter_blocks[j]->index();
1460 rows[num_nonzeros] = r;
1461 cols[num_nonzeros] = c;
1462 values[num_nonzeros] = 1.0;
1463 ++num_nonzeros;
1464 }
1465 }
1466
1467 tsm->set_num_nonzeros(num_nonzeros);
1468 return tsm;
1469}
1470
1471void SolverImpl::ReorderProgramForSparseNormalCholesky(Program* program) {
1472#ifndef CERES_NO_SUITESPARSE
1473 // Set the offsets and index for CreateJacobianSparsityTranspose.
1474 program->SetParameterOffsetsAndIndex();
1475
1476 // Compute a block sparse presentation of J'.
1477 scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
1478 SolverImpl::CreateJacobianBlockSparsityTranspose(program));
1479
1480 // Order rows using AMD.
1481 SuiteSparse ss;
1482 cholmod_sparse* block_jacobian_transpose =
1483 ss.CreateSparseMatrix(tsm_block_jacobian_transpose.get());
1484
Sameer Agarwal2b749702013-04-19 19:52:58 -07001485 vector<int> ordering(program->NumParameterBlocks(), -1);
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001486 ss.ApproximateMinimumDegreeOrdering(block_jacobian_transpose, &ordering[0]);
1487 ss.Free(block_jacobian_transpose);
1488
1489 // Apply ordering.
1490 vector<ParameterBlock*>& parameter_blocks =
1491 *(program->mutable_parameter_blocks());
1492 const vector<ParameterBlock*> parameter_blocks_copy(parameter_blocks);
1493 for (int i = 0; i < program->NumParameterBlocks(); ++i) {
1494 parameter_blocks[i] = parameter_blocks_copy[ordering[i]];
1495 }
1496#endif
1497
1498 program->SetParameterOffsetsAndIndex();
1499}
1500
Keir Mierle8ebb0732012-04-30 23:09:08 -07001501} // namespace internal
1502} // namespace ceres