blob: 2bf6cd26212a55ec02b9b8a3120831f9ac52b807 [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 Agarwald5b93bf2013-04-26 21:17:49 -070036#include <string>
Sameer Agarwalba8d9672012-10-02 00:48:57 -070037#include "ceres/coordinate_descent_minimizer.h"
Sameer Agarwald5b93bf2013-04-26 21:17:49 -070038#include "ceres/cxsparse.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070039#include "ceres/evaluator.h"
40#include "ceres/gradient_checking_cost_function.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070041#include "ceres/iteration_callback.h"
42#include "ceres/levenberg_marquardt_strategy.h"
Sameer Agarwalf4d01642012-11-26 12:55:58 -080043#include "ceres/line_search_minimizer.h"
Sameer Agarwalc8f07902013-04-19 08:01:04 -070044#include "ceres/linear_solver.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070045#include "ceres/map_util.h"
46#include "ceres/minimizer.h"
Sameer Agarwal2c94eed2012-10-01 16:34:37 -070047#include "ceres/ordered_groups.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070048#include "ceres/parameter_block.h"
Sameer Agarwalba8d9672012-10-02 00:48:57 -070049#include "ceres/parameter_block_ordering.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070050#include "ceres/problem.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070051#include "ceres/problem_impl.h"
52#include "ceres/program.h"
53#include "ceres/residual_block.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070054#include "ceres/stringprintf.h"
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -070055#include "ceres/suitesparse.h"
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -070056#include "ceres/trust_region_minimizer.h"
Petter Strandmark76533b32012-09-04 22:08:50 -070057#include "ceres/wall_time.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070058
59namespace ceres {
60namespace internal {
61namespace {
62
Keir Mierle8ebb0732012-04-30 23:09:08 -070063// Callback for updating the user's parameter blocks. Updates are only
64// done if the step is successful.
65class StateUpdatingCallback : public IterationCallback {
66 public:
67 StateUpdatingCallback(Program* program, double* parameters)
68 : program_(program), parameters_(parameters) {}
69
70 CallbackReturnType operator()(const IterationSummary& summary) {
71 if (summary.step_is_successful) {
72 program_->StateVectorToParameterBlocks(parameters_);
73 program_->CopyParameterBlockStateToUserState();
74 }
75 return SOLVER_CONTINUE;
76 }
77
78 private:
79 Program* program_;
80 double* parameters_;
81};
82
Sameer Agarwalf102a682013-02-11 15:08:40 -080083void SetSummaryFinalCost(Solver::Summary* summary) {
84 summary->final_cost = summary->initial_cost;
85 // We need the loop here, instead of just looking at the last
86 // iteration because the minimizer maybe making non-monotonic steps.
87 for (int i = 0; i < summary->iterations.size(); ++i) {
88 const IterationSummary& iteration_summary = summary->iterations[i];
89 summary->final_cost = min(iteration_summary.cost, summary->final_cost);
90 }
91}
92
Keir Mierle8ebb0732012-04-30 23:09:08 -070093// Callback for logging the state of the minimizer to STDERR or STDOUT
94// depending on the user's preferences and logging level.
Sameer Agarwalf4d01642012-11-26 12:55:58 -080095class TrustRegionLoggingCallback : public IterationCallback {
Keir Mierle8ebb0732012-04-30 23:09:08 -070096 public:
Sameer Agarwalf4d01642012-11-26 12:55:58 -080097 explicit TrustRegionLoggingCallback(bool log_to_stdout)
Keir Mierle8ebb0732012-04-30 23:09:08 -070098 : log_to_stdout_(log_to_stdout) {}
99
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800100 ~TrustRegionLoggingCallback() {}
Keir Mierle8ebb0732012-04-30 23:09:08 -0700101
102 CallbackReturnType operator()(const IterationSummary& summary) {
103 const char* kReportRowFormat =
104 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
Sameer Agarwalfa015192012-06-11 14:21:42 -0700105 "rho:% 3.2e mu:% 3.2e li:% 3d it:% 3.2e tt:% 3.2e";
Keir Mierle8ebb0732012-04-30 23:09:08 -0700106 string output = StringPrintf(kReportRowFormat,
107 summary.iteration,
108 summary.cost,
109 summary.cost_change,
110 summary.gradient_max_norm,
111 summary.step_norm,
112 summary.relative_decrease,
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700113 summary.trust_region_radius,
Sameer Agarwalfa015192012-06-11 14:21:42 -0700114 summary.linear_solver_iterations,
115 summary.iteration_time_in_seconds,
116 summary.cumulative_time_in_seconds);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700117 if (log_to_stdout_) {
118 cout << output << endl;
119 } else {
120 VLOG(1) << output;
121 }
122 return SOLVER_CONTINUE;
123 }
124
125 private:
126 const bool log_to_stdout_;
127};
128
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800129// Callback for logging the state of the minimizer to STDERR or STDOUT
130// depending on the user's preferences and logging level.
131class LineSearchLoggingCallback : public IterationCallback {
132 public:
133 explicit LineSearchLoggingCallback(bool log_to_stdout)
134 : log_to_stdout_(log_to_stdout) {}
135
136 ~LineSearchLoggingCallback() {}
137
138 CallbackReturnType operator()(const IterationSummary& summary) {
139 const char* kReportRowFormat =
140 "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e "
141 "s:% 3.2e e:% 3d it:% 3.2e tt:% 3.2e";
142 string output = StringPrintf(kReportRowFormat,
143 summary.iteration,
144 summary.cost,
145 summary.cost_change,
146 summary.gradient_max_norm,
147 summary.step_norm,
148 summary.step_size,
149 summary.line_search_function_evaluations,
150 summary.iteration_time_in_seconds,
151 summary.cumulative_time_in_seconds);
152 if (log_to_stdout_) {
153 cout << output << endl;
154 } else {
155 VLOG(1) << output;
156 }
157 return SOLVER_CONTINUE;
158 }
159
160 private:
161 const bool log_to_stdout_;
162};
163
164
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700165// Basic callback to record the execution of the solver to a file for
166// offline analysis.
167class FileLoggingCallback : public IterationCallback {
168 public:
169 explicit FileLoggingCallback(const string& filename)
170 : fptr_(NULL) {
171 fptr_ = fopen(filename.c_str(), "w");
172 CHECK_NOTNULL(fptr_);
173 }
174
175 virtual ~FileLoggingCallback() {
176 if (fptr_ != NULL) {
177 fclose(fptr_);
178 }
179 }
180
181 virtual CallbackReturnType operator()(const IterationSummary& summary) {
182 fprintf(fptr_,
183 "%4d %e %e\n",
184 summary.iteration,
185 summary.cost,
186 summary.cumulative_time_in_seconds);
187 return SOLVER_CONTINUE;
188 }
189 private:
190 FILE* fptr_;
191};
192
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800193// Iterate over each of the groups in order of their priority and fill
194// summary with their sizes.
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800195void SummarizeOrdering(ParameterBlockOrdering* ordering,
196 vector<int>* summary) {
197 CHECK_NOTNULL(summary)->clear();
198 if (ordering == NULL) {
199 return;
200 }
201
202 const map<int, set<double*> >& group_to_elements =
203 ordering->group_to_elements();
204 for (map<int, set<double*> >::const_iterator it = group_to_elements.begin();
205 it != group_to_elements.end();
206 ++it) {
207 summary->push_back(it->second.size());
208 }
209}
210
Sameer Agarwal27bb4a82013-12-18 13:06:58 -0800211void SummarizeGivenProgram(const Program& program, Solver::Summary* summary) {
212 summary->num_parameter_blocks = program.NumParameterBlocks();
213 summary->num_parameters = program.NumParameters();
214 summary->num_effective_parameters = program.NumEffectiveParameters();
215 summary->num_residual_blocks = program.NumResidualBlocks();
216 summary->num_residuals = program.NumResiduals();
217}
218
219void SummarizeReducedProgram(const Program& program, Solver::Summary* summary) {
220 summary->num_parameter_blocks_reduced = program.NumParameterBlocks();
221 summary->num_parameters_reduced = program.NumParameters();
222 summary->num_effective_parameters_reduced = program.NumEffectiveParameters();
223 summary->num_residual_blocks_reduced = program.NumResidualBlocks();
224 summary->num_residuals_reduced = program.NumResiduals();
225}
226
Sameer Agarwal5e8321c2014-01-22 17:23:27 -0800227bool ParameterBlocksAreFinite(const ProblemImpl* problem,
228 string* message) {
229 CHECK_NOTNULL(message);
230 const Program& program = problem->program();
231 const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
232 for (int i = 0; i < parameter_blocks.size(); ++i) {
233 const double* array = parameter_blocks[i]->user_state();
234 const int size = parameter_blocks[i]->Size();
235 const int invalid_index = FindInvalidValue(size, array);
236 if (invalid_index != size) {
237 *message = StringPrintf(
238 "ParameterBlock: %p with size %d has at least one invalid value.\n"
239 "First invalid value is at index: %d.\n"
240 "Parameter block values: ",
241 array, size, invalid_index);
242 AppendArrayToString(size, array, message);
243 return false;
244 }
245 }
246 return true;
247}
248
Sergey Sharybin15bf0d52014-01-13 20:38:28 +0600249bool LineSearchOptionsAreValid(const Solver::Options& options,
250 string* message) {
251 // Validate values for configuration parameters supplied by user.
252 if ((options.line_search_direction_type == ceres::BFGS ||
253 options.line_search_direction_type == ceres::LBFGS) &&
254 options.line_search_type != ceres::WOLFE) {
255 *message =
256 string("Invalid configuration: require line_search_type == "
257 "ceres::WOLFE when using (L)BFGS to ensure that underlying "
258 "assumptions are guaranteed to be satisfied.");
259 return false;
260 }
261 if (options.max_lbfgs_rank <= 0) {
262 *message =
263 string("Invalid configuration: require max_lbfgs_rank > 0");
264 return false;
265 }
266 if (options.min_line_search_step_size <= 0.0) {
267 *message =
268 "Invalid configuration: require min_line_search_step_size > 0.0.";
269 return false;
270 }
271 if (options.line_search_sufficient_function_decrease <= 0.0) {
272 *message =
273 string("Invalid configuration: require ") +
274 string("line_search_sufficient_function_decrease > 0.0.");
275 return false;
276 }
277 if (options.max_line_search_step_contraction <= 0.0 ||
278 options.max_line_search_step_contraction >= 1.0) {
279 *message = string("Invalid configuration: require ") +
280 string("0.0 < max_line_search_step_contraction < 1.0.");
281 return false;
282 }
283 if (options.min_line_search_step_contraction <=
284 options.max_line_search_step_contraction ||
285 options.min_line_search_step_contraction > 1.0) {
286 *message = string("Invalid configuration: require ") +
287 string("max_line_search_step_contraction < ") +
288 string("min_line_search_step_contraction <= 1.0.");
289 return false;
290 }
291 // Warn user if they have requested BISECTION interpolation, but constraints
292 // on max/min step size change during line search prevent bisection scaling
293 // from occurring. Warn only, as this is likely a user mistake, but one which
294 // does not prevent us from continuing.
295 LOG_IF(WARNING,
296 (options.line_search_interpolation_type == ceres::BISECTION &&
297 (options.max_line_search_step_contraction > 0.5 ||
298 options.min_line_search_step_contraction < 0.5)))
299 << "Line search interpolation type is BISECTION, but specified "
300 << "max_line_search_step_contraction: "
301 << options.max_line_search_step_contraction << ", and "
302 << "min_line_search_step_contraction: "
303 << options.min_line_search_step_contraction
304 << ", prevent bisection (0.5) scaling, continuing with solve regardless.";
305 if (options.max_num_line_search_step_size_iterations <= 0) {
306 *message = string("Invalid configuration: require ") +
307 string("max_num_line_search_step_size_iterations > 0.");
308 return false;
309 }
310 if (options.line_search_sufficient_curvature_decrease <=
311 options.line_search_sufficient_function_decrease ||
312 options.line_search_sufficient_curvature_decrease > 1.0) {
313 *message = string("Invalid configuration: require ") +
314 string("line_search_sufficient_function_decrease < ") +
315 string("line_search_sufficient_curvature_decrease < 1.0.");
316 return false;
317 }
318 if (options.max_line_search_step_expansion <= 1.0) {
319 *message = string("Invalid configuration: require ") +
320 string("max_line_search_step_expansion > 1.0.");
321 return false;
322 }
323 return true;
324}
325
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800326// Returns true if the program has any non-constant parameter blocks
327// which have non-trivial bounds constraints.
328bool IsBoundsConstrained(const Program& program) {
329 const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
330 for (int i = 0; i < parameter_blocks.size(); ++i) {
Sameer Agarwala536ae72014-05-04 21:18:42 -0700331 const ParameterBlock* parameter_block = parameter_blocks[i];
332 if (parameter_block->IsConstant()) {
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800333 continue;
334 }
Sameer Agarwala536ae72014-05-04 21:18:42 -0700335 const int size = parameter_block->Size();
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800336 for (int j = 0; j < size; ++j) {
Sameer Agarwala536ae72014-05-04 21:18:42 -0700337 const double lower_bound = parameter_block->LowerBoundForParameter(j);
338 const double upper_bound = parameter_block->UpperBoundForParameter(j);
339 if (lower_bound > -std::numeric_limits<double>::max() ||
340 upper_bound < std::numeric_limits<double>::max()) {
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800341 return true;
342 }
343 }
344 }
345 return false;
346}
347
Sameer Agarwald789b002014-02-19 00:13:58 -0800348// Returns false, if the problem has any constant parameter blocks
349// which are not feasible, or any variable parameter blocks which have
350// a lower bound greater than or equal to the upper bound.
351bool ParameterBlocksAreFeasible(const ProblemImpl* problem, string* message) {
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800352 CHECK_NOTNULL(message);
353 const Program& program = problem->program();
354 const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
355 for (int i = 0; i < parameter_blocks.size(); ++i) {
Sameer Agarwala536ae72014-05-04 21:18:42 -0700356 const ParameterBlock* parameter_block = parameter_blocks[i];
357 const double* parameters = parameter_block->user_state();
358 const int size = parameter_block->Size();
359 if (parameter_block->IsConstant()) {
Sameer Agarwald789b002014-02-19 00:13:58 -0800360 // Constant parameter blocks must start in the feasible region
361 // to ultimately produce a feasible solution, since Ceres cannot
362 // change them.
363 for (int j = 0; j < size; ++j) {
Sameer Agarwala536ae72014-05-04 21:18:42 -0700364 const double lower_bound = parameter_block->LowerBoundForParameter(j);
365 const double upper_bound = parameter_block->UpperBoundForParameter(j);
366 if (parameters[j] < lower_bound || parameters[j] > upper_bound) {
Sameer Agarwald789b002014-02-19 00:13:58 -0800367 *message = StringPrintf(
Sameer Agarwal6e3aad22014-02-24 13:35:26 -0800368 "ParameterBlock: %p with size %d has at least one infeasible "
369 "value."
Sameer Agarwald789b002014-02-19 00:13:58 -0800370 "\nFirst infeasible value is at index: %d."
371 "\nLower bound: %e, value: %e, upper bound: %e"
372 "\nParameter block values: ",
Sameer Agarwala536ae72014-05-04 21:18:42 -0700373 parameters, size, j, lower_bound, parameters[j], upper_bound);
374 AppendArrayToString(size, parameters, message);
Sameer Agarwald789b002014-02-19 00:13:58 -0800375 return false;
376 }
377 }
378 } else {
379 // Variable parameter blocks must have non-empty feasible
380 // regions, otherwise there is no way to produce a feasible
381 // solution.
382 for (int j = 0; j < size; ++j) {
Sameer Agarwala536ae72014-05-04 21:18:42 -0700383 const double lower_bound = parameter_block->LowerBoundForParameter(j);
384 const double upper_bound = parameter_block->UpperBoundForParameter(j);
385 if (lower_bound >= upper_bound) {
Sameer Agarwald789b002014-02-19 00:13:58 -0800386 *message = StringPrintf(
Sameer Agarwal6e3aad22014-02-24 13:35:26 -0800387 "ParameterBlock: %p with size %d has at least one infeasible "
388 "bound."
Sameer Agarwald789b002014-02-19 00:13:58 -0800389 "\nFirst infeasible bound is at index: %d."
390 "\nLower bound: %e, upper bound: %e"
391 "\nParameter block values: ",
Sameer Agarwala536ae72014-05-04 21:18:42 -0700392 parameters, size, j, lower_bound, upper_bound);
393 AppendArrayToString(size, parameters, message);
Sameer Agarwald789b002014-02-19 00:13:58 -0800394 return false;
395 }
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800396 }
397 }
398 }
Sameer Agarwald789b002014-02-19 00:13:58 -0800399
400 return true;
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800401}
402
Sameer Agarwald789b002014-02-19 00:13:58 -0800403
Keir Mierle8ebb0732012-04-30 23:09:08 -0700404} // namespace
405
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800406void SolverImpl::TrustRegionMinimize(
407 const Solver::Options& options,
408 Program* program,
409 CoordinateDescentMinimizer* inner_iteration_minimizer,
410 Evaluator* evaluator,
411 LinearSolver* linear_solver,
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800412 Solver::Summary* summary) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700413 Minimizer::Options minimizer_options(options);
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800414 minimizer_options.is_constrained = IsBoundsConstrained(*program);
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700415
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800416 // The optimizer works on contiguous parameter vectors; allocate
417 // some.
418 Vector parameters(program->NumParameters());
419
420 // Collect the discontiguous parameters into a contiguous state
421 // vector.
422 program->ParameterBlocksToStateVector(parameters.data());
423
Sameer Agarwal1b7f3b52012-08-09 21:46:19 -0700424 scoped_ptr<IterationCallback> file_logging_callback;
425 if (!options.solver_log.empty()) {
426 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
427 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
428 file_logging_callback.get());
429 }
430
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800431 TrustRegionLoggingCallback logging_callback(
432 options.minimizer_progress_to_stdout);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700433 if (options.logging_type != SILENT) {
Keir Mierlef7471832012-06-14 11:31:53 -0700434 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
435 &logging_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700436 }
437
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800438 StateUpdatingCallback updating_callback(program, parameters.data());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700439 if (options.update_state_every_iteration) {
Keir Mierlef7471832012-06-14 11:31:53 -0700440 // This must get pushed to the front of the callbacks so that it is run
441 // before any of the user callbacks.
442 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
443 &updating_callback);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700444 }
445
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700446 minimizer_options.evaluator = evaluator;
447 scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800448
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700449 minimizer_options.jacobian = jacobian.get();
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700450 minimizer_options.inner_iteration_minimizer = inner_iteration_minimizer;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700451
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700452 TrustRegionStrategy::Options trust_region_strategy_options;
453 trust_region_strategy_options.linear_solver = linear_solver;
454 trust_region_strategy_options.initial_radius =
455 options.initial_trust_region_radius;
456 trust_region_strategy_options.max_radius = options.max_trust_region_radius;
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -0700457 trust_region_strategy_options.min_lm_diagonal = options.min_lm_diagonal;
458 trust_region_strategy_options.max_lm_diagonal = options.max_lm_diagonal;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700459 trust_region_strategy_options.trust_region_strategy_type =
460 options.trust_region_strategy_type;
Markus Moll51cf7cb2012-08-20 20:10:20 +0200461 trust_region_strategy_options.dogleg_type = options.dogleg_type;
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700462 scoped_ptr<TrustRegionStrategy> strategy(
463 TrustRegionStrategy::Create(trust_region_strategy_options));
464 minimizer_options.trust_region_strategy = strategy.get();
465
466 TrustRegionMinimizer minimizer;
Petter Strandmark76533b32012-09-04 22:08:50 -0700467 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800468 minimizer.Minimize(minimizer_options, parameters.data(), summary);
469
470 // If the user aborted mid-optimization or the optimization
471 // terminated because of a numerical failure, then do not update
472 // user state.
473 if (summary->termination_type != USER_FAILURE &&
474 summary->termination_type != FAILURE) {
475 program->StateVectorToParameterBlocks(parameters.data());
476 program->CopyParameterBlockStateToUserState();
477 }
478
Petter Strandmark76533b32012-09-04 22:08:50 -0700479 summary->minimizer_time_in_seconds =
480 WallTimeInSeconds() - minimizer_start_time;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700481}
482
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800483void SolverImpl::LineSearchMinimize(
484 const Solver::Options& options,
485 Program* program,
486 Evaluator* evaluator,
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800487 Solver::Summary* summary) {
488 Minimizer::Options minimizer_options(options);
489
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800490 // The optimizer works on contiguous parameter vectors; allocate some.
491 Vector parameters(program->NumParameters());
492
493 // Collect the discontiguous parameters into a contiguous state vector.
494 program->ParameterBlocksToStateVector(parameters.data());
495
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800496 // TODO(sameeragarwal): Add support for logging the configuration
497 // and more detailed stats.
498 scoped_ptr<IterationCallback> file_logging_callback;
499 if (!options.solver_log.empty()) {
500 file_logging_callback.reset(new FileLoggingCallback(options.solver_log));
501 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
502 file_logging_callback.get());
503 }
504
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800505 LineSearchLoggingCallback logging_callback(
506 options.minimizer_progress_to_stdout);
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800507 if (options.logging_type != SILENT) {
508 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
509 &logging_callback);
510 }
511
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800512 StateUpdatingCallback updating_callback(program, parameters.data());
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800513 if (options.update_state_every_iteration) {
514 // This must get pushed to the front of the callbacks so that it is run
515 // before any of the user callbacks.
516 minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(),
517 &updating_callback);
518 }
519
520 minimizer_options.evaluator = evaluator;
521
522 LineSearchMinimizer minimizer;
523 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800524 minimizer.Minimize(minimizer_options, parameters.data(), summary);
525
526 // If the user aborted mid-optimization or the optimization
527 // terminated because of a numerical failure, then do not update
528 // user state.
529 if (summary->termination_type != USER_FAILURE &&
530 summary->termination_type != FAILURE) {
531 program->StateVectorToParameterBlocks(parameters.data());
532 program->CopyParameterBlockStateToUserState();
533 }
534
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800535 summary->minimizer_time_in_seconds =
536 WallTimeInSeconds() - minimizer_start_time;
537}
538
539void SolverImpl::Solve(const Solver::Options& options,
540 ProblemImpl* problem_impl,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700541 Solver::Summary* summary) {
Sameer Agarwal126dfbe2013-08-20 22:34:34 -0700542 VLOG(2) << "Initial problem: "
543 << problem_impl->NumParameterBlocks()
544 << " parameter blocks, "
545 << problem_impl->NumParameters()
546 << " parameters, "
547 << problem_impl->NumResidualBlocks()
548 << " residual blocks, "
549 << problem_impl->NumResiduals()
550 << " residuals.";
Sameer Agarwal5e8321c2014-01-22 17:23:27 -0800551 *CHECK_NOTNULL(summary) = Solver::Summary();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800552 if (options.minimizer_type == TRUST_REGION) {
553 TrustRegionSolve(options, problem_impl, summary);
554 } else {
555 LineSearchSolve(options, problem_impl, summary);
556 }
557}
558
559void SolverImpl::TrustRegionSolve(const Solver::Options& original_options,
560 ProblemImpl* original_problem_impl,
561 Solver::Summary* summary) {
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800562 EventLogger event_logger("TrustRegionSolve");
Petter Strandmark76533b32012-09-04 22:08:50 -0700563 double solver_start_time = WallTimeInSeconds();
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700564
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700565 Program* original_program = original_problem_impl->mutable_program();
566 ProblemImpl* problem_impl = original_problem_impl;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700567
Sameer Agarwald010de52013-02-15 14:26:56 -0800568 summary->minimizer_type = TRUST_REGION;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700569
Sameer Agarwal27bb4a82013-12-18 13:06:58 -0800570 SummarizeGivenProgram(*original_program, summary);
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700571 SummarizeOrdering(original_options.linear_solver_ordering.get(),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800572 &(summary->linear_solver_ordering_given));
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700573 SummarizeOrdering(original_options.inner_iteration_ordering.get(),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800574 &(summary->inner_iteration_ordering_given));
575
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700576 Solver::Options options(original_options);
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700577
Keir Mierle8ebb0732012-04-30 23:09:08 -0700578#ifndef CERES_USE_OPENMP
579 if (options.num_threads > 1) {
580 LOG(WARNING)
581 << "OpenMP support is not compiled into this binary; "
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700582 << "only options.num_threads=1 is supported. Switching "
Keir Mierle8ebb0732012-04-30 23:09:08 -0700583 << "to single threaded mode.";
584 options.num_threads = 1;
585 }
586 if (options.num_linear_solver_threads > 1) {
587 LOG(WARNING)
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700588 << "OpenMP support is not compiled into this binary; "
589 << "only options.num_linear_solver_threads=1 is supported. Switching "
Keir Mierle8ebb0732012-04-30 23:09:08 -0700590 << "to single threaded mode.";
591 options.num_linear_solver_threads = 1;
592 }
593#endif
594
Keir Mierle8ebb0732012-04-30 23:09:08 -0700595 summary->num_threads_given = original_options.num_threads;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700596 summary->num_threads_used = options.num_threads;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700597
Sameer Agarwalc4a32912013-06-13 22:00:48 -0700598 if (options.trust_region_minimizer_iterations_to_dump.size() > 0 &&
599 options.trust_region_problem_dump_format_type != CONSOLE &&
600 options.trust_region_problem_dump_directory.empty()) {
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800601 summary->message =
Sameer Agarwalc4a32912013-06-13 22:00:48 -0700602 "Solver::Options::trust_region_problem_dump_directory is empty.";
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800603 LOG(ERROR) << summary->message;
Sameer Agarwalc4a32912013-06-13 22:00:48 -0700604 return;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700605 }
606
Sameer Agarwal5e8321c2014-01-22 17:23:27 -0800607 if (!ParameterBlocksAreFinite(problem_impl, &summary->message)) {
608 LOG(ERROR) << "Terminating: " << summary->message;
609 return;
610 }
611
Sameer Agarwald789b002014-02-19 00:13:58 -0800612 if (!ParameterBlocksAreFeasible(problem_impl, &summary->message)) {
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800613 LOG(ERROR) << "Terminating: " << summary->message;
614 return;
615 }
616
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800617 event_logger.AddEvent("Init");
Sameer Agarwalf102a682013-02-11 15:08:40 -0800618
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700619 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800620 event_logger.AddEvent("SetParameterBlockPtrs");
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700621
622 // If the user requests gradient checking, construct a new
623 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
624 // GradientCheckingCostFunction and replacing problem_impl with
625 // gradient_checking_problem_impl.
626 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
627 if (options.check_gradients) {
628 VLOG(1) << "Checking Gradients";
629 gradient_checking_problem_impl.reset(
630 CreateGradientCheckingProblemImpl(
631 problem_impl,
632 options.numeric_derivative_relative_step_size,
633 options.gradient_check_relative_precision));
634
635 // From here on, problem_impl will point to the gradient checking
636 // version.
637 problem_impl = gradient_checking_problem_impl.get();
638 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700639
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700640 if (options.linear_solver_ordering.get() != NULL) {
641 if (!IsOrderingValid(options, problem_impl, &summary->message)) {
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800642 LOG(ERROR) << summary->message;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700643 return;
644 }
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800645 event_logger.AddEvent("CheckOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -0700646 } else {
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700647 options.linear_solver_ordering.reset(new ParameterBlockOrdering);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700648 const ProblemImpl::ParameterMap& parameter_map =
649 problem_impl->parameter_map();
650 for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
651 it != parameter_map.end();
652 ++it) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700653 options.linear_solver_ordering->AddElementToGroup(it->first, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700654 }
Sameer Agarwalc58e6dc2013-02-11 16:41:06 -0800655 event_logger.AddEvent("ConstructOrdering");
Sameer Agarwal65625f72012-09-17 12:06:57 -0700656 }
657
Keir Mierle8ebb0732012-04-30 23:09:08 -0700658 // Create the three objects needed to minimize: the transformed program, the
659 // evaluator, and the linear solver.
Keir Mierle31c1e782012-08-17 16:16:32 -0700660 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
661 problem_impl,
662 &summary->fixed_cost,
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800663 &summary->message));
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800664
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800665 event_logger.AddEvent("CreateReducedProgram");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700666 if (reduced_program == NULL) {
667 return;
668 }
669
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700670 SummarizeOrdering(options.linear_solver_ordering.get(),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800671 &(summary->linear_solver_ordering_used));
Sameer Agarwal27bb4a82013-12-18 13:06:58 -0800672 SummarizeReducedProgram(*reduced_program, summary);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700673
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700674 if (summary->num_parameter_blocks_reduced == 0) {
675 summary->preprocessor_time_in_seconds =
676 WallTimeInSeconds() - solver_start_time;
677
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800678 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800679
680 summary->message =
681 "Terminating: Function tolerance reached. "
682 "No non-constant parameter blocks found.";
683 summary->termination_type = CONVERGENCE;
Sameer Agarwalf5578002014-01-08 10:43:31 -0800684 VLOG_IF(1, options.logging_type != SILENT) << summary->message;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700685
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800686 summary->initial_cost = summary->fixed_cost;
687 summary->final_cost = summary->fixed_cost;
688
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700689 // Ensure the program state is set to the user parameters on the way out.
690 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal8af13222013-09-23 22:10:24 -0700691 original_program->SetParameterOffsetsAndIndex();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700692
693 summary->postprocessor_time_in_seconds =
694 WallTimeInSeconds() - post_process_start_time;
695 return;
696 }
697
Keir Mierle8ebb0732012-04-30 23:09:08 -0700698 scoped_ptr<LinearSolver>
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800699 linear_solver(CreateLinearSolver(&options, &summary->message));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800700 event_logger.AddEvent("CreateLinearSolver");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700701 if (linear_solver == NULL) {
702 return;
703 }
704
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700705 summary->linear_solver_type_given = original_options.linear_solver_type;
706 summary->linear_solver_type_used = options.linear_solver_type;
707
708 summary->preconditioner_type = options.preconditioner_type;
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700709 summary->visibility_clustering_type = options.visibility_clustering_type;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700710
711 summary->num_linear_solver_threads_given =
712 original_options.num_linear_solver_threads;
713 summary->num_linear_solver_threads_used = options.num_linear_solver_threads;
714
Sameer Agarwal367b65e2013-08-09 10:35:37 -0700715 summary->dense_linear_algebra_library_type =
716 options.dense_linear_algebra_library_type;
717 summary->sparse_linear_algebra_library_type =
718 options.sparse_linear_algebra_library_type;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700719
720 summary->trust_region_strategy_type = options.trust_region_strategy_type;
721 summary->dogleg_type = options.dogleg_type;
722
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700723 scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
724 problem_impl->parameter_map(),
725 reduced_program.get(),
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800726 &summary->message));
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800727
728 event_logger.AddEvent("CreateEvaluator");
729
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700730 if (evaluator == NULL) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700731 return;
732 }
733
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700734 scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700735 if (options.use_inner_iterations) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700736 if (reduced_program->parameter_blocks().size() < 2) {
737 LOG(WARNING) << "Reduced problem only contains one parameter block."
738 << "Disabling inner iterations.";
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700739 } else {
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700740 inner_iteration_minimizer.reset(
Sameer Agarwala9334d62013-11-20 10:12:23 -0800741 CreateInnerIterationMinimizer(options,
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700742 *reduced_program,
743 problem_impl->parameter_map(),
Sameer Agarwal977be7c2013-01-26 16:01:54 -0800744 summary));
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700745 if (inner_iteration_minimizer == NULL) {
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800746 LOG(ERROR) << summary->message;
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700747 return;
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700748 }
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700749 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700750 }
Sameer Agarwal9f9488b2013-05-23 09:40:40 -0700751 event_logger.AddEvent("CreateInnerIterationMinimizer");
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800752
Petter Strandmark76533b32012-09-04 22:08:50 -0700753 double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalfa015192012-06-11 14:21:42 -0700754 summary->preprocessor_time_in_seconds =
755 minimizer_start_time - solver_start_time;
756
Keir Mierle8ebb0732012-04-30 23:09:08 -0700757 // Run the optimization.
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800758 TrustRegionMinimize(options,
759 reduced_program.get(),
760 inner_iteration_minimizer.get(),
761 evaluator.get(),
762 linear_solver.get(),
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800763 summary);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800764 event_logger.AddEvent("Minimize");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700765
Petter Strandmark76533b32012-09-04 22:08:50 -0700766 double post_process_start_time = WallTimeInSeconds();
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700767
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800768 SetSummaryFinalCost(summary);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700769
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800770 // Ensure the program state is set to the user parameters on the way
771 // out.
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700772 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal8af13222013-09-23 22:10:24 -0700773 original_program->SetParameterOffsetsAndIndex();
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700774
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800775 const map<string, double>& linear_solver_time_statistics =
776 linear_solver->TimeStatistics();
777 summary->linear_solver_time_in_seconds =
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800778 FindWithDefault(linear_solver_time_statistics,
779 "LinearSolver::Solve",
780 0.0);
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800781
782 const map<string, double>& evaluator_time_statistics =
783 evaluator->TimeStatistics();
784
785 summary->residual_evaluation_time_in_seconds =
786 FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
787 summary->jacobian_evaluation_time_in_seconds =
788 FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
789
Keir Mierle8ebb0732012-04-30 23:09:08 -0700790 // Stick a fork in it, we're done.
Petter Strandmark76533b32012-09-04 22:08:50 -0700791 summary->postprocessor_time_in_seconds =
792 WallTimeInSeconds() - post_process_start_time;
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800793 event_logger.AddEvent("PostProcess");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700794}
795
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800796void SolverImpl::LineSearchSolve(const Solver::Options& original_options,
797 ProblemImpl* original_problem_impl,
798 Solver::Summary* summary) {
799 double solver_start_time = WallTimeInSeconds();
800
801 Program* original_program = original_problem_impl->mutable_program();
802 ProblemImpl* problem_impl = original_problem_impl;
803
Sameer Agarwal27bb4a82013-12-18 13:06:58 -0800804 SummarizeGivenProgram(*original_program, summary);
Sameer Agarwald79f8862013-12-30 07:39:10 -0800805 summary->minimizer_type = LINE_SEARCH;
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800806 summary->line_search_direction_type =
807 original_options.line_search_direction_type;
Sameer Agarwald010de52013-02-15 14:26:56 -0800808 summary->max_lbfgs_rank = original_options.max_lbfgs_rank;
809 summary->line_search_type = original_options.line_search_type;
Sameer Agarwal4f010b22013-07-01 08:01:01 -0700810 summary->line_search_interpolation_type =
811 original_options.line_search_interpolation_type;
812 summary->nonlinear_conjugate_gradient_type =
813 original_options.nonlinear_conjugate_gradient_type;
814
Sameer Agarwald79f8862013-12-30 07:39:10 -0800815 if (!LineSearchOptionsAreValid(original_options, &summary->message)) {
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800816 LOG(ERROR) << summary->message;
Alex Stewart9aa0e3c2013-07-05 20:22:37 +0100817 return;
818 }
819
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800820 if (IsBoundsConstrained(problem_impl->program())) {
821 summary->message = "LINE_SEARCH Minimizer does not support bounds.";
822 LOG(ERROR) << "Terminating: " << summary->message;
823 return;
824 }
825
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800826 Solver::Options options(original_options);
827
828 // This ensures that we get a Block Jacobian Evaluator along with
829 // none of the Schur nonsense. This file will have to be extensively
830 // refactored to deal with the various bits of cleanups related to
831 // line search.
832 options.linear_solver_type = CGNR;
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700833
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800834
835#ifndef CERES_USE_OPENMP
836 if (options.num_threads > 1) {
837 LOG(WARNING)
838 << "OpenMP support is not compiled into this binary; "
839 << "only options.num_threads=1 is supported. Switching "
840 << "to single threaded mode.";
841 options.num_threads = 1;
842 }
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700843#endif // CERES_USE_OPENMP
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800844
845 summary->num_threads_given = original_options.num_threads;
846 summary->num_threads_used = options.num_threads;
847
Sameer Agarwal5e8321c2014-01-22 17:23:27 -0800848 if (!ParameterBlocksAreFinite(problem_impl, &summary->message)) {
849 LOG(ERROR) << "Terminating: " << summary->message;
850 return;
851 }
852
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700853 if (options.linear_solver_ordering.get() != NULL) {
854 if (!IsOrderingValid(options, problem_impl, &summary->message)) {
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800855 LOG(ERROR) << summary->message;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800856 return;
857 }
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800858 } else {
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700859 options.linear_solver_ordering.reset(new ParameterBlockOrdering);
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800860 const ProblemImpl::ParameterMap& parameter_map =
861 problem_impl->parameter_map();
862 for (ProblemImpl::ParameterMap::const_iterator it = parameter_map.begin();
863 it != parameter_map.end();
864 ++it) {
865 options.linear_solver_ordering->AddElementToGroup(it->first, 0);
866 }
867 }
868
Sameer Agarwalbb05be32014-04-13 14:22:19 -0700869
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800870 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
871
872 // If the user requests gradient checking, construct a new
873 // ProblemImpl by wrapping the CostFunctions of problem_impl inside
874 // GradientCheckingCostFunction and replacing problem_impl with
875 // gradient_checking_problem_impl.
876 scoped_ptr<ProblemImpl> gradient_checking_problem_impl;
877 if (options.check_gradients) {
878 VLOG(1) << "Checking Gradients";
879 gradient_checking_problem_impl.reset(
880 CreateGradientCheckingProblemImpl(
881 problem_impl,
882 options.numeric_derivative_relative_step_size,
883 options.gradient_check_relative_precision));
884
885 // From here on, problem_impl will point to the gradient checking
886 // version.
887 problem_impl = gradient_checking_problem_impl.get();
888 }
889
890 // Create the three objects needed to minimize: the transformed program, the
891 // evaluator, and the linear solver.
892 scoped_ptr<Program> reduced_program(CreateReducedProgram(&options,
893 problem_impl,
894 &summary->fixed_cost,
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800895 &summary->message));
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800896 if (reduced_program == NULL) {
897 return;
898 }
899
Sameer Agarwal27bb4a82013-12-18 13:06:58 -0800900 SummarizeReducedProgram(*reduced_program, summary);
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800901 if (summary->num_parameter_blocks_reduced == 0) {
902 summary->preprocessor_time_in_seconds =
903 WallTimeInSeconds() - solver_start_time;
904
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800905 summary->message =
906 "Terminating: Function tolerance reached. "
907 "No non-constant parameter blocks found.";
908 summary->termination_type = CONVERGENCE;
Sameer Agarwalf5578002014-01-08 10:43:31 -0800909 VLOG_IF(1, options.logging_type != SILENT) << summary->message;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800910
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800911 const double post_process_start_time = WallTimeInSeconds();
Sameer Agarwalf102a682013-02-11 15:08:40 -0800912 SetSummaryFinalCost(summary);
913
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800914 // Ensure the program state is set to the user parameters on the way out.
915 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal8af13222013-09-23 22:10:24 -0700916 original_program->SetParameterOffsetsAndIndex();
917
Sameer Agarwal956ed7e2013-02-19 07:06:15 -0800918 summary->postprocessor_time_in_seconds =
919 WallTimeInSeconds() - post_process_start_time;
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800920 return;
921 }
922
923 scoped_ptr<Evaluator> evaluator(CreateEvaluator(options,
924 problem_impl->parameter_map(),
925 reduced_program.get(),
Sameer Agarwaldcee1202013-12-07 21:48:56 -0800926 &summary->message));
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800927 if (evaluator == NULL) {
928 return;
929 }
930
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800931 const double minimizer_start_time = WallTimeInSeconds();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800932 summary->preprocessor_time_in_seconds =
933 minimizer_start_time - solver_start_time;
934
935 // Run the optimization.
Sameer Agarwalfc827de2014-02-18 21:24:50 -0800936 LineSearchMinimize(options, reduced_program.get(), evaluator.get(), summary);
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800937
Sameer Agarwal42a84b82013-02-01 12:22:53 -0800938 const double post_process_start_time = WallTimeInSeconds();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800939
Sameer Agarwalf102a682013-02-11 15:08:40 -0800940 SetSummaryFinalCost(summary);
941
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800942 // Ensure the program state is set to the user parameters on the way out.
943 original_program->SetParameterBlockStatePtrsToUserStatePtrs();
Sameer Agarwal8af13222013-09-23 22:10:24 -0700944 original_program->SetParameterOffsetsAndIndex();
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800945
Sameer Agarwald010de52013-02-15 14:26:56 -0800946 const map<string, double>& evaluator_time_statistics =
947 evaluator->TimeStatistics();
948
949 summary->residual_evaluation_time_in_seconds =
950 FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
951 summary->jacobian_evaluation_time_in_seconds =
952 FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
953
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800954 // Stick a fork in it, we're done.
955 summary->postprocessor_time_in_seconds =
956 WallTimeInSeconds() - post_process_start_time;
957}
Sameer Agarwalf4d01642012-11-26 12:55:58 -0800958
Sameer Agarwal65625f72012-09-17 12:06:57 -0700959bool SolverImpl::IsOrderingValid(const Solver::Options& options,
960 const ProblemImpl* problem_impl,
961 string* error) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700962 if (options.linear_solver_ordering->NumElements() !=
Sameer Agarwal65625f72012-09-17 12:06:57 -0700963 problem_impl->NumParameterBlocks()) {
964 *error = "Number of parameter blocks in user supplied ordering "
965 "does not match the number of parameter blocks in the problem";
966 return false;
967 }
968
969 const Program& program = problem_impl->program();
970 const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -0700971 for (vector<ParameterBlock*>::const_iterator it = parameter_blocks.begin();
972 it != parameter_blocks.end();
973 ++it) {
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700974 if (!options.linear_solver_ordering
975 ->IsMember(const_cast<double*>((*it)->user_state()))) {
Sameer Agarwal65625f72012-09-17 12:06:57 -0700976 *error = "Problem contains a parameter block that is not in "
977 "the user specified ordering.";
978 return false;
979 }
980 }
981
982 if (IsSchurType(options.linear_solver_type) &&
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700983 options.linear_solver_ordering->NumGroups() > 1) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -0700984 const vector<ResidualBlock*>& residual_blocks = program.residual_blocks();
Sameer Agarwal65625f72012-09-17 12:06:57 -0700985 const set<double*>& e_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700986 options.linear_solver_ordering->group_to_elements().begin()->second;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700987 if (!IsParameterBlockSetIndependent(e_blocks, residual_blocks)) {
988 *error = "The user requested the use of a Schur type solver. "
989 "But the first elimination group in the ordering is not an "
990 "independent set.";
991 return false;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700992 }
993 }
994 return true;
995}
996
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800997bool SolverImpl::IsParameterBlockSetIndependent(
998 const set<double*>& parameter_block_ptrs,
999 const vector<ResidualBlock*>& residual_blocks) {
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001000 // Loop over each residual block and ensure that no two parameter
1001 // blocks in the same residual block are part of
1002 // parameter_block_ptrs as that would violate the assumption that it
1003 // is an independent set in the Hessian matrix.
1004 for (vector<ResidualBlock*>::const_iterator it = residual_blocks.begin();
1005 it != residual_blocks.end();
1006 ++it) {
1007 ParameterBlock* const* parameter_blocks = (*it)->parameter_blocks();
1008 const int num_parameter_blocks = (*it)->NumParameterBlocks();
1009 int count = 0;
1010 for (int i = 0; i < num_parameter_blocks; ++i) {
Sameer Agarwalba8d9672012-10-02 00:48:57 -07001011 count += parameter_block_ptrs.count(
1012 parameter_blocks[i]->mutable_user_state());
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001013 }
1014 if (count > 1) {
1015 return false;
1016 }
1017 }
1018 return true;
1019}
1020
1021
Keir Mierle8ebb0732012-04-30 23:09:08 -07001022// Strips varying parameters and residuals, maintaining order, and updating
Sameer Agarwala9334d62013-11-20 10:12:23 -08001023// orderings.
1024bool SolverImpl::RemoveFixedBlocksFromProgram(
1025 Program* program,
1026 ParameterBlockOrdering* linear_solver_ordering,
1027 ParameterBlockOrdering* inner_iteration_ordering,
1028 double* fixed_cost,
1029 string* error) {
Markus Moll8de78db2012-07-14 15:49:11 +02001030 scoped_array<double> residual_block_evaluate_scratch;
1031 if (fixed_cost != NULL) {
Keir Mierlec161a9d2012-07-18 14:01:48 -07001032 residual_block_evaluate_scratch.reset(
Markus Moll8de78db2012-07-14 15:49:11 +02001033 new double[program->MaxScratchDoublesNeededForEvaluate()]);
1034 *fixed_cost = 0.0;
1035 }
1036
Sameer Agarwala9334d62013-11-20 10:12:23 -08001037 vector<ParameterBlock*>* parameter_blocks =
1038 program->mutable_parameter_blocks();
1039 vector<ResidualBlock*>* residual_blocks =
1040 program->mutable_residual_blocks();
1041
1042 // Mark all the parameters as unused. Abuse the index member of the
1043 // parameter blocks for the marking.
Keir Mierle8ebb0732012-04-30 23:09:08 -07001044 for (int i = 0; i < parameter_blocks->size(); ++i) {
1045 (*parameter_blocks)[i]->set_index(-1);
1046 }
1047
1048 // Filter out residual that have all-constant parameters, and mark all the
1049 // parameter blocks that appear in residuals.
Sameer Agarwala9334d62013-11-20 10:12:23 -08001050 int num_active_residual_blocks = 0;
1051 for (int i = 0; i < residual_blocks->size(); ++i) {
1052 ResidualBlock* residual_block = (*residual_blocks)[i];
1053 int num_parameter_blocks = residual_block->NumParameterBlocks();
Keir Mierle8ebb0732012-04-30 23:09:08 -07001054
Sameer Agarwala9334d62013-11-20 10:12:23 -08001055 // Determine if the residual block is fixed, and also mark varying
1056 // parameters that appear in the residual block.
1057 bool all_constant = true;
1058 for (int k = 0; k < num_parameter_blocks; k++) {
1059 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
1060 if (!parameter_block->IsConstant()) {
1061 all_constant = false;
1062 parameter_block->set_index(1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001063 }
1064 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001065
Sameer Agarwala9334d62013-11-20 10:12:23 -08001066 if (!all_constant) {
1067 (*residual_blocks)[num_active_residual_blocks++] = residual_block;
1068 } else if (fixed_cost != NULL) {
1069 // The residual is constant and will be removed, so its cost is
1070 // added to the variable fixed_cost.
1071 double cost = 0.0;
1072 if (!residual_block->Evaluate(true,
1073 &cost,
1074 NULL,
1075 NULL,
1076 residual_block_evaluate_scratch.get())) {
1077 *error = StringPrintf("Evaluation of the residual %d failed during "
1078 "removal of fixed residual blocks.", i);
1079 return false;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001080 }
Sameer Agarwala9334d62013-11-20 10:12:23 -08001081 *fixed_cost += cost;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001082 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001083 }
Sameer Agarwala9334d62013-11-20 10:12:23 -08001084 residual_blocks->resize(num_active_residual_blocks);
1085
1086 // Filter out unused or fixed parameter blocks, and update the
1087 // linear_solver_ordering and the inner_iteration_ordering (if
1088 // present).
1089 int num_active_parameter_blocks = 0;
1090 for (int i = 0; i < parameter_blocks->size(); ++i) {
1091 ParameterBlock* parameter_block = (*parameter_blocks)[i];
1092 if (parameter_block->index() == -1) {
1093 // Parameter block is constant.
1094 if (linear_solver_ordering != NULL) {
1095 linear_solver_ordering->Remove(parameter_block->mutable_user_state());
1096 }
1097
1098 // It is not necessary that the inner iteration ordering contain
1099 // this parameter block. But calling Remove is safe, as it will
1100 // just return false.
1101 if (inner_iteration_ordering != NULL) {
1102 inner_iteration_ordering->Remove(parameter_block->mutable_user_state());
1103 }
1104 continue;
1105 }
1106
1107 (*parameter_blocks)[num_active_parameter_blocks++] = parameter_block;
1108 }
1109 parameter_blocks->resize(num_active_parameter_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001110
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001111 if (!(((program->NumResidualBlocks() == 0) &&
Keir Mierle8ebb0732012-04-30 23:09:08 -07001112 (program->NumParameterBlocks() == 0)) ||
1113 ((program->NumResidualBlocks() != 0) &&
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001114 (program->NumParameterBlocks() != 0)))) {
1115 *error = "Congratulations, you found a bug in Ceres. Please report it.";
1116 return false;
1117 }
1118
Keir Mierle8ebb0732012-04-30 23:09:08 -07001119 return true;
1120}
1121
1122Program* SolverImpl::CreateReducedProgram(Solver::Options* options,
1123 ProblemImpl* problem_impl,
Markus Moll8de78db2012-07-14 15:49:11 +02001124 double* fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -07001125 string* error) {
Sameer Agarwalbb05be32014-04-13 14:22:19 -07001126 CHECK_NOTNULL(options->linear_solver_ordering.get());
Keir Mierle8ebb0732012-04-30 23:09:08 -07001127 Program* original_program = problem_impl->mutable_program();
1128 scoped_ptr<Program> transformed_program(new Program(*original_program));
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001129
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001130 ParameterBlockOrdering* linear_solver_ordering =
Sameer Agarwalbb05be32014-04-13 14:22:19 -07001131 options->linear_solver_ordering.get();
Sameer Agarwal65625f72012-09-17 12:06:57 -07001132 const int min_group_id =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001133 linear_solver_ordering->group_to_elements().begin()->first;
Sameer Agarwala9334d62013-11-20 10:12:23 -08001134 ParameterBlockOrdering* inner_iteration_ordering =
Sameer Agarwalbb05be32014-04-13 14:22:19 -07001135 options->inner_iteration_ordering.get();
Keir Mierle8ebb0732012-04-30 23:09:08 -07001136 if (!RemoveFixedBlocksFromProgram(transformed_program.get(),
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001137 linear_solver_ordering,
Sameer Agarwala9334d62013-11-20 10:12:23 -08001138 inner_iteration_ordering,
Markus Moll8de78db2012-07-14 15:49:11 +02001139 fixed_cost,
Keir Mierle8ebb0732012-04-30 23:09:08 -07001140 error)) {
1141 return NULL;
1142 }
1143
Sameer Agarwal126dfbe2013-08-20 22:34:34 -07001144 VLOG(2) << "Reduced problem: "
1145 << transformed_program->NumParameterBlocks()
1146 << " parameter blocks, "
1147 << transformed_program->NumParameters()
1148 << " parameters, "
1149 << transformed_program->NumResidualBlocks()
1150 << " residual blocks, "
1151 << transformed_program->NumResiduals()
1152 << " residuals.";
1153
Keir Mierle8ebb0732012-04-30 23:09:08 -07001154 if (transformed_program->NumParameterBlocks() == 0) {
1155 LOG(WARNING) << "No varying parameter blocks to optimize; "
1156 << "bailing early.";
1157 return transformed_program.release();
1158 }
1159
Sameer Agarwal65625f72012-09-17 12:06:57 -07001160 if (IsSchurType(options->linear_solver_type) &&
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001161 linear_solver_ordering->GroupSize(min_group_id) == 0) {
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001162 // If the user requested the use of a Schur type solver, and
1163 // supplied a non-NULL linear_solver_ordering object with more than
1164 // one elimination group, then it can happen that after all the
1165 // parameter blocks which are fixed or unused have been removed from
1166 // the program and the ordering, there are no more parameter blocks
1167 // in the first elimination group.
1168 //
1169 // In such a case, the use of a Schur type solver is not possible,
1170 // as they assume there is at least one e_block. Thus, we
1171 // automatically switch to the closest solver to the one indicated
1172 // by the user.
1173 AlternateLinearSolverForSchurTypeLinearSolver(options);
Sameer Agarwal65625f72012-09-17 12:06:57 -07001174 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001175
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001176 if (IsSchurType(options->linear_solver_type)) {
Sameer Agarwalac626962013-05-06 07:04:26 -07001177 if (!ReorderProgramForSchurTypeLinearSolver(
1178 options->linear_solver_type,
Sameer Agarwal367b65e2013-08-09 10:35:37 -07001179 options->sparse_linear_algebra_library_type,
Sameer Agarwalac626962013-05-06 07:04:26 -07001180 problem_impl->parameter_map(),
1181 linear_solver_ordering,
1182 transformed_program.get(),
1183 error)) {
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001184 return NULL;
1185 }
1186 return transformed_program.release();
1187 }
Sameer Agarwal42a84b82013-02-01 12:22:53 -08001188
Richard Stebbing32530782014-04-26 07:42:23 +01001189 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
1190 !options->dynamic_sparsity) {
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001191 if (!ReorderProgramForSparseNormalCholesky(
Sameer Agarwal367b65e2013-08-09 10:35:37 -07001192 options->sparse_linear_algebra_library_type,
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001193 linear_solver_ordering,
1194 transformed_program.get(),
1195 error)) {
1196 return NULL;
1197 }
1198
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001199 return transformed_program.release();
1200 }
1201
Keir Mierle8ebb0732012-04-30 23:09:08 -07001202 transformed_program->SetParameterOffsetsAndIndex();
1203 return transformed_program.release();
1204}
1205
1206LinearSolver* SolverImpl::CreateLinearSolver(Solver::Options* options,
1207 string* error) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001208 CHECK_NOTNULL(options);
Sameer Agarwalbb05be32014-04-13 14:22:19 -07001209 CHECK_NOTNULL(options->linear_solver_ordering.get());
Sameer Agarwal65625f72012-09-17 12:06:57 -07001210 CHECK_NOTNULL(error);
1211
Sameer Agarwal5ecd2512012-06-17 16:34:03 -07001212 if (options->trust_region_strategy_type == DOGLEG) {
1213 if (options->linear_solver_type == ITERATIVE_SCHUR ||
1214 options->linear_solver_type == CGNR) {
1215 *error = "DOGLEG only supports exact factorization based linear "
1216 "solvers. If you want to use an iterative solver please "
1217 "use LEVENBERG_MARQUARDT as the trust_region_strategy_type";
1218 return NULL;
1219 }
1220 }
1221
Sameer Agarwal367b65e2013-08-09 10:35:37 -07001222#ifdef CERES_NO_LAPACK
1223 if (options->linear_solver_type == DENSE_NORMAL_CHOLESKY &&
1224 options->dense_linear_algebra_library_type == LAPACK) {
1225 *error = "Can't use DENSE_NORMAL_CHOLESKY with LAPACK because "
1226 "LAPACK was not enabled when Ceres was built.";
1227 return NULL;
1228 }
1229
1230 if (options->linear_solver_type == DENSE_QR &&
1231 options->dense_linear_algebra_library_type == LAPACK) {
1232 *error = "Can't use DENSE_QR with LAPACK because "
1233 "LAPACK was not enabled when Ceres was built.";
1234 return NULL;
1235 }
1236
1237 if (options->linear_solver_type == DENSE_SCHUR &&
1238 options->dense_linear_algebra_library_type == LAPACK) {
1239 *error = "Can't use DENSE_SCHUR with LAPACK because "
1240 "LAPACK was not enabled when Ceres was built.";
1241 return NULL;
1242 }
1243#endif
1244
Keir Mierle8ebb0732012-04-30 23:09:08 -07001245#ifdef CERES_NO_SUITESPARSE
Sameer Agarwalb0518732012-05-29 00:27:57 -07001246 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
Sameer Agarwal367b65e2013-08-09 10:35:37 -07001247 options->sparse_linear_algebra_library_type == SUITE_SPARSE) {
Sameer Agarwalb0518732012-05-29 00:27:57 -07001248 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITESPARSE because "
1249 "SuiteSparse was not enabled when Ceres was built.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001250 return NULL;
1251 }
Sameer Agarwal65625f72012-09-17 12:06:57 -07001252
Petter Strandmarkdd1a2762012-09-19 12:55:05 +02001253 if (options->preconditioner_type == CLUSTER_JACOBI) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001254 *error = "CLUSTER_JACOBI preconditioner not suppored. Please build Ceres "
1255 "with SuiteSparse support.";
1256 return NULL;
1257 }
1258
Petter Strandmarkdd1a2762012-09-19 12:55:05 +02001259 if (options->preconditioner_type == CLUSTER_TRIDIAGONAL) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001260 *error = "CLUSTER_TRIDIAGONAL preconditioner not suppored. Please build "
1261 "Ceres with SuiteSparse support.";
1262 return NULL;
1263 }
Sameer Agarwalb0518732012-05-29 00:27:57 -07001264#endif
1265
1266#ifdef CERES_NO_CXSPARSE
1267 if (options->linear_solver_type == SPARSE_NORMAL_CHOLESKY &&
Sameer Agarwal367b65e2013-08-09 10:35:37 -07001268 options->sparse_linear_algebra_library_type == CX_SPARSE) {
Sameer Agarwalb0518732012-05-29 00:27:57 -07001269 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CXSPARSE because "
1270 "CXSparse was not enabled when Ceres was built.";
1271 return NULL;
1272 }
1273#endif
1274
Sameer Agarwal65625f72012-09-17 12:06:57 -07001275#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle27dd0d32012-10-15 13:52:36 -07001276 if (options->linear_solver_type == SPARSE_SCHUR) {
Sameer Agarwal65625f72012-09-17 12:06:57 -07001277 *error = "Can't use SPARSE_SCHUR because neither SuiteSparse nor"
1278 "CXSparse was enabled when Ceres was compiled.";
1279 return NULL;
1280 }
1281#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -07001282
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -07001283 if (options->max_linear_solver_iterations <= 0) {
1284 *error = "Solver::Options::max_linear_solver_iterations is not positive.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001285 return NULL;
1286 }
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -07001287 if (options->min_linear_solver_iterations <= 0) {
1288 *error = "Solver::Options::min_linear_solver_iterations is not positive.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001289 return NULL;
1290 }
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -07001291 if (options->min_linear_solver_iterations >
1292 options->max_linear_solver_iterations) {
1293 *error = "Solver::Options::min_linear_solver_iterations > "
1294 "Solver::Options::max_linear_solver_iterations.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001295 return NULL;
1296 }
1297
1298 LinearSolver::Options linear_solver_options;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001299 linear_solver_options.min_num_iterations =
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -07001300 options->min_linear_solver_iterations;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001301 linear_solver_options.max_num_iterations =
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -07001302 options->max_linear_solver_iterations;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001303 linear_solver_options.type = options->linear_solver_type;
1304 linear_solver_options.preconditioner_type = options->preconditioner_type;
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -07001305 linear_solver_options.visibility_clustering_type =
1306 options->visibility_clustering_type;
Sameer Agarwal367b65e2013-08-09 10:35:37 -07001307 linear_solver_options.sparse_linear_algebra_library_type =
1308 options->sparse_linear_algebra_library_type;
1309 linear_solver_options.dense_linear_algebra_library_type =
1310 options->dense_linear_algebra_library_type;
Sameer Agarwal9189f4e2013-04-19 17:09:49 -07001311 linear_solver_options.use_postordering = options->use_postordering;
Richard Stebbing32530782014-04-26 07:42:23 +01001312 linear_solver_options.dynamic_sparsity = options->dynamic_sparsity;
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001313
1314 // Ignore user's postordering preferences and force it to be true if
1315 // cholmod_camd is not available. This ensures that the linear
1316 // solver does not assume that a fill-reducing pre-ordering has been
1317 // done.
1318#if !defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CAMD)
1319 if (IsSchurType(linear_solver_options.type) &&
Sameer Agarwald61b68a2013-08-16 17:02:56 -07001320 options->sparse_linear_algebra_library_type == SUITE_SPARSE) {
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001321 linear_solver_options.use_postordering = true;
1322 }
1323#endif
1324
Keir Mierle8ebb0732012-04-30 23:09:08 -07001325 linear_solver_options.num_threads = options->num_linear_solver_threads;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001326 options->num_linear_solver_threads = linear_solver_options.num_threads;
1327
Sameer Agarwal65625f72012-09-17 12:06:57 -07001328 const map<int, set<double*> >& groups =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001329 options->linear_solver_ordering->group_to_elements();
Sameer Agarwal65625f72012-09-17 12:06:57 -07001330 for (map<int, set<double*> >::const_iterator it = groups.begin();
1331 it != groups.end();
1332 ++it) {
1333 linear_solver_options.elimination_groups.push_back(it->second.size());
1334 }
1335 // Schur type solvers, expect at least two elimination groups. If
Sameer Agarwal1dc544a2012-09-18 18:09:03 -07001336 // there is only one elimination group, then CreateReducedProgram
Sameer Agarwal65625f72012-09-17 12:06:57 -07001337 // guarantees that this group only contains e_blocks. Thus we add a
1338 // dummy elimination group with zero blocks in it.
1339 if (IsSchurType(linear_solver_options.type) &&
1340 linear_solver_options.elimination_groups.size() == 1) {
1341 linear_solver_options.elimination_groups.push_back(0);
1342 }
1343
Keir Mierle8ebb0732012-04-30 23:09:08 -07001344 return LinearSolver::Create(linear_solver_options);
1345}
1346
Keir Mierle8ebb0732012-04-30 23:09:08 -07001347
1348// Find the minimum index of any parameter block to the given residual.
1349// Parameter blocks that have indices greater than num_eliminate_blocks are
1350// considered to have an index equal to num_eliminate_blocks.
Sergey Sharybinb53c9662013-02-25 01:14:26 +06001351static int MinParameterBlock(const ResidualBlock* residual_block,
1352 int num_eliminate_blocks) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001353 int min_parameter_block_position = num_eliminate_blocks;
1354 for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
1355 ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
Keir Mierle32de18d2012-05-13 16:45:05 -07001356 if (!parameter_block->IsConstant()) {
1357 CHECK_NE(parameter_block->index(), -1)
1358 << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
1359 << "This is a Ceres bug; please contact the developers!";
1360 min_parameter_block_position = std::min(parameter_block->index(),
1361 min_parameter_block_position);
1362 }
Keir Mierle8ebb0732012-04-30 23:09:08 -07001363 }
1364 return min_parameter_block_position;
1365}
1366
1367// Reorder the residuals for program, if necessary, so that the residuals
1368// involving each E block occur together. This is a necessary condition for the
1369// Schur eliminator, which works on these "row blocks" in the jacobian.
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001370bool SolverImpl::LexicographicallyOrderResidualBlocks(
1371 const int num_eliminate_blocks,
1372 Program* program,
1373 string* error) {
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001374 CHECK_GE(num_eliminate_blocks, 1)
1375 << "Congratulations, you found a Ceres bug! Please report this error "
1376 << "to the developers.";
Keir Mierle8ebb0732012-04-30 23:09:08 -07001377
1378 // Create a histogram of the number of residuals for each E block. There is an
1379 // extra bucket at the end to catch all non-eliminated F blocks.
Sameer Agarwal65625f72012-09-17 12:06:57 -07001380 vector<int> residual_blocks_per_e_block(num_eliminate_blocks + 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001381 vector<ResidualBlock*>* residual_blocks = program->mutable_residual_blocks();
1382 vector<int> min_position_per_residual(residual_blocks->size());
1383 for (int i = 0; i < residual_blocks->size(); ++i) {
1384 ResidualBlock* residual_block = (*residual_blocks)[i];
Sameer Agarwal65625f72012-09-17 12:06:57 -07001385 int position = MinParameterBlock(residual_block, num_eliminate_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001386 min_position_per_residual[i] = position;
Sameer Agarwal65625f72012-09-17 12:06:57 -07001387 DCHECK_LE(position, num_eliminate_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001388 residual_blocks_per_e_block[position]++;
1389 }
1390
1391 // Run a cumulative sum on the histogram, to obtain offsets to the start of
1392 // each histogram bucket (where each bucket is for the residuals for that
1393 // E-block).
Sameer Agarwal65625f72012-09-17 12:06:57 -07001394 vector<int> offsets(num_eliminate_blocks + 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -07001395 std::partial_sum(residual_blocks_per_e_block.begin(),
1396 residual_blocks_per_e_block.end(),
1397 offsets.begin());
1398 CHECK_EQ(offsets.back(), residual_blocks->size())
1399 << "Congratulations, you found a Ceres bug! Please report this error "
1400 << "to the developers.";
1401
1402 CHECK(find(residual_blocks_per_e_block.begin(),
1403 residual_blocks_per_e_block.end() - 1, 0) !=
1404 residual_blocks_per_e_block.end())
1405 << "Congratulations, you found a Ceres bug! Please report this error "
1406 << "to the developers.";
1407
1408 // Fill in each bucket with the residual blocks for its corresponding E block.
1409 // Each bucket is individually filled from the back of the bucket to the front
1410 // of the bucket. The filling order among the buckets is dictated by the
1411 // residual blocks. This loop uses the offsets as counters; subtracting one
1412 // from each offset as a residual block is placed in the bucket. When the
1413 // filling is finished, the offset pointerts should have shifted down one
1414 // entry (this is verified below).
1415 vector<ResidualBlock*> reordered_residual_blocks(
1416 (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL));
1417 for (int i = 0; i < residual_blocks->size(); ++i) {
1418 int bucket = min_position_per_residual[i];
1419
1420 // Decrement the cursor, which should now point at the next empty position.
1421 offsets[bucket]--;
1422
1423 // Sanity.
1424 CHECK(reordered_residual_blocks[offsets[bucket]] == NULL)
1425 << "Congratulations, you found a Ceres bug! Please report this error "
1426 << "to the developers.";
1427
1428 reordered_residual_blocks[offsets[bucket]] = (*residual_blocks)[i];
1429 }
1430
1431 // Sanity check #1: The difference in bucket offsets should match the
1432 // histogram sizes.
Sameer Agarwal65625f72012-09-17 12:06:57 -07001433 for (int i = 0; i < num_eliminate_blocks; ++i) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001434 CHECK_EQ(residual_blocks_per_e_block[i], offsets[i + 1] - offsets[i])
1435 << "Congratulations, you found a Ceres bug! Please report this error "
1436 << "to the developers.";
1437 }
1438 // Sanity check #2: No NULL's left behind.
1439 for (int i = 0; i < reordered_residual_blocks.size(); ++i) {
1440 CHECK(reordered_residual_blocks[i] != NULL)
1441 << "Congratulations, you found a Ceres bug! Please report this error "
1442 << "to the developers.";
1443 }
1444
1445 // Now that the residuals are collected by E block, swap them in place.
1446 swap(*program->mutable_residual_blocks(), reordered_residual_blocks);
1447 return true;
1448}
1449
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001450Evaluator* SolverImpl::CreateEvaluator(
1451 const Solver::Options& options,
1452 const ProblemImpl::ParameterMap& parameter_map,
1453 Program* program,
1454 string* error) {
Keir Mierle8ebb0732012-04-30 23:09:08 -07001455 Evaluator::Options evaluator_options;
1456 evaluator_options.linear_solver_type = options.linear_solver_type;
Sameer Agarwal65625f72012-09-17 12:06:57 -07001457 evaluator_options.num_eliminate_blocks =
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001458 (options.linear_solver_ordering->NumGroups() > 0 &&
Sameer Agarwal65625f72012-09-17 12:06:57 -07001459 IsSchurType(options.linear_solver_type))
Sameer Agarwal68b32a92012-10-06 23:10:51 -07001460 ? (options.linear_solver_ordering
1461 ->group_to_elements().begin()
1462 ->second.size())
Sameer Agarwal9123e2f2012-09-18 21:49:06 -07001463 : 0;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001464 evaluator_options.num_threads = options.num_threads;
Richard Stebbing32530782014-04-26 07:42:23 +01001465 evaluator_options.dynamic_sparsity = options.dynamic_sparsity;
Keir Mierle8ebb0732012-04-30 23:09:08 -07001466 return Evaluator::Create(evaluator_options, program, error);
1467}
1468
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001469CoordinateDescentMinimizer* SolverImpl::CreateInnerIterationMinimizer(
1470 const Solver::Options& options,
1471 const Program& program,
1472 const ProblemImpl::ParameterMap& parameter_map,
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001473 Solver::Summary* summary) {
Sameer Agarwal9f9488b2013-05-23 09:40:40 -07001474 summary->inner_iterations_given = true;
1475
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001476 scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer(
1477 new CoordinateDescentMinimizer);
1478 scoped_ptr<ParameterBlockOrdering> inner_iteration_ordering;
1479 ParameterBlockOrdering* ordering_ptr = NULL;
1480
Sameer Agarwalbb05be32014-04-13 14:22:19 -07001481 if (options.inner_iteration_ordering.get() == NULL) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001482 // Find a recursive decomposition of the Hessian matrix as a set
1483 // of independent sets of decreasing size and invert it. This
1484 // seems to work better in practice, i.e., Cameras before
1485 // points.
1486 inner_iteration_ordering.reset(new ParameterBlockOrdering);
1487 ComputeRecursiveIndependentSetOrdering(program,
1488 inner_iteration_ordering.get());
1489 inner_iteration_ordering->Reverse();
1490 ordering_ptr = inner_iteration_ordering.get();
1491 } else {
1492 const map<int, set<double*> >& group_to_elements =
1493 options.inner_iteration_ordering->group_to_elements();
1494
1495 // Iterate over each group and verify that it is an independent
1496 // set.
1497 map<int, set<double*> >::const_iterator it = group_to_elements.begin();
Sameer Agarwal509f68c2013-02-20 01:39:03 -08001498 for ( ; it != group_to_elements.end(); ++it) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001499 if (!IsParameterBlockSetIndependent(it->second,
1500 program.residual_blocks())) {
Sameer Agarwaldcee1202013-12-07 21:48:56 -08001501 summary->message =
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001502 StringPrintf("The user-provided "
1503 "parameter_blocks_for_inner_iterations does not "
1504 "form an independent set. Group Id: %d", it->first);
1505 return NULL;
1506 }
1507 }
Sameer Agarwalbb05be32014-04-13 14:22:19 -07001508 ordering_ptr = options.inner_iteration_ordering.get();
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001509 }
1510
1511 if (!inner_iteration_minimizer->Init(program,
1512 parameter_map,
1513 *ordering_ptr,
Sameer Agarwaldcee1202013-12-07 21:48:56 -08001514 &summary->message)) {
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001515 return NULL;
1516 }
1517
Sameer Agarwal9f9488b2013-05-23 09:40:40 -07001518 summary->inner_iterations_used = true;
1519 summary->inner_iteration_time_in_seconds = 0.0;
Sameer Agarwal977be7c2013-01-26 16:01:54 -08001520 SummarizeOrdering(ordering_ptr, &(summary->inner_iteration_ordering_used));
Sameer Agarwal67a107b2012-10-08 14:35:41 -07001521 return inner_iteration_minimizer.release();
1522}
1523
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001524void SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(
1525 Solver::Options* options) {
1526 if (!IsSchurType(options->linear_solver_type)) {
1527 return;
1528 }
1529
1530 string msg = "No e_blocks remaining. Switching from ";
1531 if (options->linear_solver_type == SPARSE_SCHUR) {
1532 options->linear_solver_type = SPARSE_NORMAL_CHOLESKY;
1533 msg += "SPARSE_SCHUR to SPARSE_NORMAL_CHOLESKY.";
1534 } else if (options->linear_solver_type == DENSE_SCHUR) {
1535 // TODO(sameeragarwal): This is probably not a great choice.
1536 // Ideally, we should have a DENSE_NORMAL_CHOLESKY, that can
1537 // take a BlockSparseMatrix as input.
1538 options->linear_solver_type = DENSE_QR;
1539 msg += "DENSE_SCHUR to DENSE_QR.";
1540 } else if (options->linear_solver_type == ITERATIVE_SCHUR) {
1541 options->linear_solver_type = CGNR;
1542 if (options->preconditioner_type != IDENTITY) {
1543 msg += StringPrintf("ITERATIVE_SCHUR with %s preconditioner "
1544 "to CGNR with JACOBI preconditioner.",
1545 PreconditionerTypeToString(
1546 options->preconditioner_type));
1547 // CGNR currently only supports the JACOBI preconditioner.
1548 options->preconditioner_type = JACOBI;
1549 } else {
Sameer Agarwalcbdeb792013-04-22 10:18:18 -07001550 msg += "ITERATIVE_SCHUR with IDENTITY preconditioner"
1551 "to CGNR with IDENTITY preconditioner.";
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001552 }
1553 }
1554 LOG(WARNING) << msg;
1555}
1556
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001557bool SolverImpl::ApplyUserOrdering(
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001558 const ProblemImpl::ParameterMap& parameter_map,
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001559 const ParameterBlockOrdering* parameter_block_ordering,
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001560 Program* program,
1561 string* error) {
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001562 const int num_parameter_blocks = program->NumParameterBlocks();
1563 if (parameter_block_ordering->NumElements() != num_parameter_blocks) {
1564 *error = StringPrintf("User specified ordering does not have the same "
1565 "number of parameters as the problem. The problem"
1566 "has %d blocks while the ordering has %d blocks.",
1567 num_parameter_blocks,
1568 parameter_block_ordering->NumElements());
1569 return false;
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001570 }
1571
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001572 vector<ParameterBlock*>* parameter_blocks =
1573 program->mutable_parameter_blocks();
1574 parameter_blocks->clear();
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001575
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001576 const map<int, set<double*> >& groups =
1577 parameter_block_ordering->group_to_elements();
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001578
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001579 for (map<int, set<double*> >::const_iterator group_it = groups.begin();
1580 group_it != groups.end();
1581 ++group_it) {
1582 const set<double*>& group = group_it->second;
1583 for (set<double*>::const_iterator parameter_block_ptr_it = group.begin();
1584 parameter_block_ptr_it != group.end();
1585 ++parameter_block_ptr_it) {
1586 ProblemImpl::ParameterMap::const_iterator parameter_block_it =
1587 parameter_map.find(*parameter_block_ptr_it);
1588 if (parameter_block_it == parameter_map.end()) {
1589 *error = StringPrintf("User specified ordering contains a pointer "
1590 "to a double that is not a parameter block in "
1591 "the problem. The invalid double is in group: %d",
1592 group_it->first);
1593 return false;
1594 }
1595 parameter_blocks->push_back(parameter_block_it->second);
1596 }
1597 }
1598 return true;
Sameer Agarwalc8f07902013-04-19 08:01:04 -07001599}
1600
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001601
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001602TripletSparseMatrix* SolverImpl::CreateJacobianBlockSparsityTranspose(
1603 const Program* program) {
1604
Sameer Agarwalcbdeb792013-04-22 10:18:18 -07001605 // Matrix to store the block sparsity structure of the Jacobian.
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001606 TripletSparseMatrix* tsm =
1607 new TripletSparseMatrix(program->NumParameterBlocks(),
1608 program->NumResidualBlocks(),
1609 10 * program->NumResidualBlocks());
1610 int num_nonzeros = 0;
1611 int* rows = tsm->mutable_rows();
1612 int* cols = tsm->mutable_cols();
1613 double* values = tsm->mutable_values();
1614
1615 const vector<ResidualBlock*>& residual_blocks = program->residual_blocks();
1616 for (int c = 0; c < residual_blocks.size(); ++c) {
1617 const ResidualBlock* residual_block = residual_blocks[c];
1618 const int num_parameter_blocks = residual_block->NumParameterBlocks();
1619 ParameterBlock* const* parameter_blocks =
1620 residual_block->parameter_blocks();
1621
1622 for (int j = 0; j < num_parameter_blocks; ++j) {
1623 if (parameter_blocks[j]->IsConstant()) {
1624 continue;
1625 }
1626
1627 // Re-size the matrix if needed.
1628 if (num_nonzeros >= tsm->max_num_nonzeros()) {
Sameer Agarwal5f433c82013-06-13 06:57:58 -07001629 tsm->set_num_nonzeros(num_nonzeros);
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001630 tsm->Reserve(2 * num_nonzeros);
1631 rows = tsm->mutable_rows();
1632 cols = tsm->mutable_cols();
1633 values = tsm->mutable_values();
1634 }
1635 CHECK_LT(num_nonzeros, tsm->max_num_nonzeros());
1636
1637 const int r = parameter_blocks[j]->index();
1638 rows[num_nonzeros] = r;
1639 cols[num_nonzeros] = c;
1640 values[num_nonzeros] = 1.0;
1641 ++num_nonzeros;
1642 }
1643 }
1644
1645 tsm->set_num_nonzeros(num_nonzeros);
1646 return tsm;
1647}
1648
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001649bool SolverImpl::ReorderProgramForSchurTypeLinearSolver(
1650 const LinearSolverType linear_solver_type,
1651 const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
1652 const ProblemImpl::ParameterMap& parameter_map,
1653 ParameterBlockOrdering* parameter_block_ordering,
1654 Program* program,
1655 string* error) {
1656 if (parameter_block_ordering->NumGroups() == 1) {
1657 // If the user supplied an parameter_block_ordering with just one
1658 // group, it is equivalent to the user supplying NULL as an
1659 // parameter_block_ordering. Ceres is completely free to choose the
1660 // parameter block ordering as it sees fit. For Schur type solvers,
1661 // this means that the user wishes for Ceres to identify the
1662 // e_blocks, which we do by computing a maximal independent set.
1663 vector<ParameterBlock*> schur_ordering;
Sameer Agarwala427c872013-06-24 17:50:56 -07001664 const int num_eliminate_blocks =
1665 ComputeStableSchurOrdering(*program, &schur_ordering);
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001666
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001667 CHECK_EQ(schur_ordering.size(), program->NumParameterBlocks())
1668 << "Congratulations, you found a Ceres bug! Please report this error "
1669 << "to the developers.";
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001670
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001671 // Update the parameter_block_ordering object.
1672 for (int i = 0; i < schur_ordering.size(); ++i) {
1673 double* parameter_block = schur_ordering[i]->mutable_user_state();
1674 const int group_id = (i < num_eliminate_blocks) ? 0 : 1;
1675 parameter_block_ordering->AddElementToGroup(parameter_block, group_id);
1676 }
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001677
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001678 // We could call ApplyUserOrdering but this is cheaper and
1679 // simpler.
1680 swap(*program->mutable_parameter_blocks(), schur_ordering);
1681 } else {
1682 // The user provided an ordering with more than one elimination
1683 // group. Trust the user and apply the ordering.
1684 if (!ApplyUserOrdering(parameter_map,
1685 parameter_block_ordering,
1686 program,
1687 error)) {
1688 return false;
1689 }
1690 }
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001691
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001692 // Pre-order the columns corresponding to the schur complement if
1693 // possible.
1694#if !defined(CERES_NO_SUITESPARSE) && !defined(CERES_NO_CAMD)
1695 if (linear_solver_type == SPARSE_SCHUR &&
1696 sparse_linear_algebra_library_type == SUITE_SPARSE) {
1697 vector<int> constraints;
1698 vector<ParameterBlock*>& parameter_blocks =
1699 *(program->mutable_parameter_blocks());
1700
1701 for (int i = 0; i < parameter_blocks.size(); ++i) {
1702 constraints.push_back(
1703 parameter_block_ordering->GroupId(
1704 parameter_blocks[i]->mutable_user_state()));
1705 }
1706
Sameer Agarwal126dfbe2013-08-20 22:34:34 -07001707 // Renumber the entries of constraints to be contiguous integers
1708 // as camd requires that the group ids be in the range [0,
1709 // parameter_blocks.size() - 1].
1710 SolverImpl::CompactifyArray(&constraints);
1711
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001712 // Set the offsets and index for CreateJacobianSparsityTranspose.
1713 program->SetParameterOffsetsAndIndex();
1714 // Compute a block sparse presentation of J'.
1715 scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
1716 SolverImpl::CreateJacobianBlockSparsityTranspose(program));
1717
1718 SuiteSparse ss;
1719 cholmod_sparse* block_jacobian_transpose =
1720 ss.CreateSparseMatrix(tsm_block_jacobian_transpose.get());
1721
1722 vector<int> ordering(parameter_blocks.size(), 0);
1723 ss.ConstrainedApproximateMinimumDegreeOrdering(block_jacobian_transpose,
1724 &constraints[0],
1725 &ordering[0]);
1726 ss.Free(block_jacobian_transpose);
1727
1728 const vector<ParameterBlock*> parameter_blocks_copy(parameter_blocks);
1729 for (int i = 0; i < program->NumParameterBlocks(); ++i) {
1730 parameter_blocks[i] = parameter_blocks_copy[ordering[i]];
1731 }
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001732 }
1733#endif
1734
1735 program->SetParameterOffsetsAndIndex();
Sameer Agarwald5b93bf2013-04-26 21:17:49 -07001736 // Schur type solvers also require that their residual blocks be
1737 // lexicographically ordered.
1738 const int num_eliminate_blocks =
1739 parameter_block_ordering->group_to_elements().begin()->second.size();
1740 return LexicographicallyOrderResidualBlocks(num_eliminate_blocks,
1741 program,
1742 error);
1743}
1744
1745bool SolverImpl::ReorderProgramForSparseNormalCholesky(
1746 const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
1747 const ParameterBlockOrdering* parameter_block_ordering,
1748 Program* program,
1749 string* error) {
1750 // Set the offsets and index for CreateJacobianSparsityTranspose.
1751 program->SetParameterOffsetsAndIndex();
1752 // Compute a block sparse presentation of J'.
1753 scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose(
1754 SolverImpl::CreateJacobianBlockSparsityTranspose(program));
1755
1756 vector<int> ordering(program->NumParameterBlocks(), 0);
1757 vector<ParameterBlock*>& parameter_blocks =
1758 *(program->mutable_parameter_blocks());
1759
1760 if (sparse_linear_algebra_library_type == SUITE_SPARSE) {
1761#ifdef CERES_NO_SUITESPARSE
1762 *error = "Can't use SPARSE_NORMAL_CHOLESKY with SUITE_SPARSE because "
1763 "SuiteSparse was not enabled when Ceres was built.";
1764 return false;
1765#else
1766 SuiteSparse ss;
1767 cholmod_sparse* block_jacobian_transpose =
1768 ss.CreateSparseMatrix(tsm_block_jacobian_transpose.get());
1769
1770# ifdef CERES_NO_CAMD
1771 // No cholmod_camd, so ignore user's parameter_block_ordering and
1772 // use plain old AMD.
1773 ss.ApproximateMinimumDegreeOrdering(block_jacobian_transpose, &ordering[0]);
1774# else
1775 if (parameter_block_ordering->NumGroups() > 1) {
1776 // If the user specified more than one elimination groups use them
1777 // to constrain the ordering.
1778 vector<int> constraints;
1779 for (int i = 0; i < parameter_blocks.size(); ++i) {
1780 constraints.push_back(
1781 parameter_block_ordering->GroupId(
1782 parameter_blocks[i]->mutable_user_state()));
1783 }
1784 ss.ConstrainedApproximateMinimumDegreeOrdering(
1785 block_jacobian_transpose,
1786 &constraints[0],
1787 &ordering[0]);
1788 } else {
1789 ss.ApproximateMinimumDegreeOrdering(block_jacobian_transpose,
1790 &ordering[0]);
1791 }
1792# endif // CERES_NO_CAMD
1793
1794 ss.Free(block_jacobian_transpose);
1795#endif // CERES_NO_SUITESPARSE
1796
1797 } else if (sparse_linear_algebra_library_type == CX_SPARSE) {
1798#ifndef CERES_NO_CXSPARSE
1799
1800 // CXSparse works with J'J instead of J'. So compute the block
1801 // sparsity for J'J and compute an approximate minimum degree
1802 // ordering.
1803 CXSparse cxsparse;
1804 cs_di* block_jacobian_transpose;
1805 block_jacobian_transpose =
1806 cxsparse.CreateSparseMatrix(tsm_block_jacobian_transpose.get());
1807 cs_di* block_jacobian = cxsparse.TransposeMatrix(block_jacobian_transpose);
1808 cs_di* block_hessian =
1809 cxsparse.MatrixMatrixMultiply(block_jacobian_transpose, block_jacobian);
1810 cxsparse.Free(block_jacobian);
1811 cxsparse.Free(block_jacobian_transpose);
1812
1813 cxsparse.ApproximateMinimumDegreeOrdering(block_hessian, &ordering[0]);
1814 cxsparse.Free(block_hessian);
1815#else // CERES_NO_CXSPARSE
1816 *error = "Can't use SPARSE_NORMAL_CHOLESKY with CX_SPARSE because "
1817 "CXSparse was not enabled when Ceres was built.";
1818 return false;
1819#endif // CERES_NO_CXSPARSE
1820 } else {
1821 *error = "Unknown sparse linear algebra library.";
1822 return false;
1823 }
1824
1825 // Apply ordering.
1826 const vector<ParameterBlock*> parameter_blocks_copy(parameter_blocks);
1827 for (int i = 0; i < program->NumParameterBlocks(); ++i) {
1828 parameter_blocks[i] = parameter_blocks_copy[ordering[i]];
1829 }
1830
1831 program->SetParameterOffsetsAndIndex();
1832 return true;
Sameer Agarwalf7ed22e2013-04-19 14:24:48 -07001833}
1834
Sameer Agarwal126dfbe2013-08-20 22:34:34 -07001835void SolverImpl::CompactifyArray(vector<int>* array_ptr) {
1836 vector<int>& array = *array_ptr;
1837 const set<int> unique_group_ids(array.begin(), array.end());
1838 map<int, int> group_id_map;
1839 for (set<int>::const_iterator it = unique_group_ids.begin();
1840 it != unique_group_ids.end();
1841 ++it) {
1842 InsertOrDie(&group_id_map, *it, group_id_map.size());
1843 }
1844
1845 for (int i = 0; i < array.size(); ++i) {
1846 array[i] = group_id_map[array[i]];
1847 }
1848}
1849
Keir Mierle8ebb0732012-04-30 23:09:08 -07001850} // namespace internal
1851} // namespace ceres