coordinate_descent_minimizer.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Ceres Solver - A fast non-linear least squares minimizer
  2. // Copyright 2022 Google Inc. All rights reserved.
  3. // http://ceres-solver.org/
  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: sameeragarwal@google.com (Sameer Agarwal)
  30. #include "ceres/coordinate_descent_minimizer.h"
  31. #include <algorithm>
  32. #include <iterator>
  33. #include <map>
  34. #include <memory>
  35. #include <numeric>
  36. #include <set>
  37. #include <string>
  38. #include <vector>
  39. #include "ceres/evaluator.h"
  40. #include "ceres/linear_solver.h"
  41. #include "ceres/minimizer.h"
  42. #include "ceres/parallel_for.h"
  43. #include "ceres/parameter_block.h"
  44. #include "ceres/parameter_block_ordering.h"
  45. #include "ceres/problem_impl.h"
  46. #include "ceres/program.h"
  47. #include "ceres/residual_block.h"
  48. #include "ceres/solver.h"
  49. #include "ceres/trust_region_minimizer.h"
  50. #include "ceres/trust_region_strategy.h"
  51. namespace ceres::internal {
  52. CoordinateDescentMinimizer::CoordinateDescentMinimizer(ContextImpl* context)
  53. : context_(context) {
  54. CHECK(context_ != nullptr);
  55. }
  56. CoordinateDescentMinimizer::~CoordinateDescentMinimizer() = default;
  57. bool CoordinateDescentMinimizer::Init(
  58. const Program& program,
  59. const ProblemImpl::ParameterMap& parameter_map,
  60. const ParameterBlockOrdering& ordering,
  61. std::string* /*error*/) {
  62. parameter_blocks_.clear();
  63. independent_set_offsets_.clear();
  64. independent_set_offsets_.push_back(0);
  65. // Serialize the OrderedGroups into a vector of parameter block
  66. // offsets for parallel access.
  67. // TODO(sameeragarwal): Investigate if parameter_block_index should be an
  68. // ordered or an unordered container.
  69. std::map<ParameterBlock*, int> parameter_block_index;
  70. std::map<int, std::set<double*>> group_to_elements =
  71. ordering.group_to_elements();
  72. for (const auto& g_t_e : group_to_elements) {
  73. const auto& elements = g_t_e.second;
  74. for (double* parameter_block : elements) {
  75. parameter_blocks_.push_back(parameter_map.find(parameter_block)->second);
  76. parameter_block_index[parameter_blocks_.back()] =
  77. parameter_blocks_.size() - 1;
  78. }
  79. independent_set_offsets_.push_back(independent_set_offsets_.back() +
  80. elements.size());
  81. }
  82. // The ordering does not have to contain all parameter blocks, so
  83. // assign zero offsets/empty independent sets to these parameter
  84. // blocks.
  85. const std::vector<ParameterBlock*>& parameter_blocks =
  86. program.parameter_blocks();
  87. for (auto* parameter_block : parameter_blocks) {
  88. if (!ordering.IsMember(parameter_block->mutable_user_state())) {
  89. parameter_blocks_.push_back(parameter_block);
  90. independent_set_offsets_.push_back(independent_set_offsets_.back());
  91. }
  92. }
  93. // Compute the set of residual blocks that depend on each parameter
  94. // block.
  95. residual_blocks_.resize(parameter_block_index.size());
  96. const std::vector<ResidualBlock*>& residual_blocks =
  97. program.residual_blocks();
  98. for (auto* residual_block : residual_blocks) {
  99. const int num_parameter_blocks = residual_block->NumParameterBlocks();
  100. for (int j = 0; j < num_parameter_blocks; ++j) {
  101. ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
  102. const auto it = parameter_block_index.find(parameter_block);
  103. if (it != parameter_block_index.end()) {
  104. residual_blocks_[it->second].push_back(residual_block);
  105. }
  106. }
  107. }
  108. evaluator_options_.linear_solver_type = DENSE_QR;
  109. evaluator_options_.num_eliminate_blocks = 0;
  110. evaluator_options_.num_threads = 1;
  111. evaluator_options_.context = context_;
  112. return true;
  113. }
  114. void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options,
  115. double* parameters,
  116. Solver::Summary* /*summary*/) {
  117. // Set the state and mark all parameter blocks constant.
  118. for (auto* parameter_block : parameter_blocks_) {
  119. parameter_block->SetState(parameters + parameter_block->state_offset());
  120. parameter_block->SetConstant();
  121. }
  122. std::vector<std::unique_ptr<LinearSolver>> linear_solvers(
  123. options.num_threads);
  124. LinearSolver::Options linear_solver_options;
  125. linear_solver_options.type = DENSE_QR;
  126. linear_solver_options.context = context_;
  127. for (int i = 0; i < options.num_threads; ++i) {
  128. linear_solvers[i] = LinearSolver::Create(linear_solver_options);
  129. }
  130. for (int i = 0; i < independent_set_offsets_.size() - 1; ++i) {
  131. const int num_problems =
  132. independent_set_offsets_[i + 1] - independent_set_offsets_[i];
  133. // Avoid parallelization overhead call if the set is empty.
  134. if (num_problems == 0) {
  135. continue;
  136. }
  137. const int num_inner_iteration_threads =
  138. std::min(options.num_threads, num_problems);
  139. evaluator_options_.num_threads =
  140. std::max(1, options.num_threads / num_inner_iteration_threads);
  141. // The parameter blocks in each independent set can be optimized
  142. // in parallel, since they do not co-occur in any residual block.
  143. ParallelFor(
  144. context_,
  145. independent_set_offsets_[i],
  146. independent_set_offsets_[i + 1],
  147. num_inner_iteration_threads,
  148. [&](int thread_id, int j) {
  149. ParameterBlock* parameter_block = parameter_blocks_[j];
  150. const int old_index = parameter_block->index();
  151. const int old_delta_offset = parameter_block->delta_offset();
  152. const int old_state_offset = parameter_block->state_offset();
  153. parameter_block->SetVarying();
  154. parameter_block->set_index(0);
  155. parameter_block->set_delta_offset(0);
  156. parameter_block->set_state_offset(0);
  157. Program inner_program;
  158. inner_program.mutable_parameter_blocks()->push_back(parameter_block);
  159. *inner_program.mutable_residual_blocks() = residual_blocks_[j];
  160. // TODO(sameeragarwal): Better error handling. Right now we
  161. // assume that this is not going to lead to problems of any
  162. // sort. Basically we should be checking for numerical failure
  163. // of some sort.
  164. //
  165. // On the other hand, if the optimization is a failure, that in
  166. // some ways is fine, since it won't change the parameters and
  167. // we are fine.
  168. Solver::Summary inner_summary;
  169. Solve(&inner_program,
  170. linear_solvers[thread_id].get(),
  171. parameters + old_state_offset,
  172. &inner_summary);
  173. parameter_block->set_index(old_index);
  174. parameter_block->set_delta_offset(old_delta_offset);
  175. parameter_block->set_state_offset(old_state_offset);
  176. parameter_block->SetState(parameters +
  177. parameter_block->state_offset());
  178. parameter_block->SetConstant();
  179. });
  180. }
  181. for (auto* parameter_block : parameter_blocks_) {
  182. parameter_block->SetVarying();
  183. }
  184. }
  185. // Solve the optimization problem for one parameter block.
  186. void CoordinateDescentMinimizer::Solve(Program* program,
  187. LinearSolver* linear_solver,
  188. double* parameter,
  189. Solver::Summary* summary) {
  190. *summary = Solver::Summary();
  191. summary->initial_cost = 0.0;
  192. summary->fixed_cost = 0.0;
  193. summary->final_cost = 0.0;
  194. std::string error;
  195. Minimizer::Options minimizer_options;
  196. minimizer_options.evaluator =
  197. Evaluator::Create(evaluator_options_, program, &error);
  198. CHECK(minimizer_options.evaluator != nullptr);
  199. minimizer_options.jacobian = minimizer_options.evaluator->CreateJacobian();
  200. CHECK(minimizer_options.jacobian != nullptr);
  201. TrustRegionStrategy::Options trs_options;
  202. trs_options.linear_solver = linear_solver;
  203. minimizer_options.trust_region_strategy =
  204. TrustRegionStrategy::Create(trs_options);
  205. CHECK(minimizer_options.trust_region_strategy != nullptr);
  206. minimizer_options.is_silent = true;
  207. TrustRegionMinimizer minimizer;
  208. minimizer.Minimize(minimizer_options, parameter, summary);
  209. }
  210. bool CoordinateDescentMinimizer::IsOrderingValid(
  211. const Program& program,
  212. const ParameterBlockOrdering& ordering,
  213. std::string* message) {
  214. // TODO(sameeragarwal): Investigate if this should be an ordered or an
  215. // unordered group.
  216. const std::map<int, std::set<double*>>& group_to_elements =
  217. ordering.group_to_elements();
  218. // Verify that each group is an independent set
  219. for (const auto& g_t_e : group_to_elements) {
  220. if (!program.IsParameterBlockSetIndependent(g_t_e.second)) {
  221. *message = StringPrintf(
  222. "The user-provided parameter_blocks_for_inner_iterations does not "
  223. "form an independent set. Group Id: %d",
  224. g_t_e.first);
  225. return false;
  226. }
  227. }
  228. return true;
  229. }
  230. // Find a recursive decomposition of the Hessian matrix as a set
  231. // of independent sets of decreasing size and invert it. This
  232. // seems to work better in practice, i.e., Cameras before
  233. // points.
  234. std::shared_ptr<ParameterBlockOrdering>
  235. CoordinateDescentMinimizer::CreateOrdering(const Program& program) {
  236. auto ordering = std::make_shared<ParameterBlockOrdering>();
  237. ComputeRecursiveIndependentSetOrdering(program, ordering.get());
  238. ordering->Reverse();
  239. return ordering;
  240. }
  241. } // namespace ceres::internal