blob: adeaf41319edeb8173fdc0ee878f6d03e734430c [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
Sergiu Deitschc8658c82022-02-20 02:22:17 +01002// Copyright 2022 Google Inc. All rights reserved.
Keir Mierle7492b0d2015-03-17 22:30:16 -07003// http://ceres-solver.org/
Keir Mierle8ebb0732012-04-30 23:09:08 -07004//
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/block_jacobian_writer.h"
32
Sameer Agarwalae652192022-02-02 13:17:29 -080033#include <algorithm>
Sameer Agarwal446487c2022-02-12 08:11:33 -080034#include <memory>
Sameer Agarwal749a4422023-01-16 07:38:05 -080035#include <vector>
Sameer Agarwalae652192022-02-02 13:17:29 -080036
Keir Mierle8ebb0732012-04-30 23:09:08 -070037#include "ceres/block_evaluate_preparer.h"
38#include "ceres/block_sparse_matrix.h"
Nikolaus Demmel7b8f6752020-09-20 21:45:24 +020039#include "ceres/internal/eigen.h"
Sergiu Deitschf90833f2022-02-07 23:43:19 +010040#include "ceres/internal/export.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070041#include "ceres/parameter_block.h"
42#include "ceres/program.h"
43#include "ceres/residual_block.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070044
Sameer Agarwalcaf614a2022-04-21 17:41:10 -070045namespace ceres::internal {
Sameer Agarwalbcc865f2014-12-17 07:35:09 -080046
Keir Mierle8ebb0732012-04-30 23:09:08 -070047namespace {
48
49// Given the residual block ordering, build a lookup table to determine which
50// per-parameter jacobian goes where in the overall program jacobian.
51//
52// Since we expect to use a Schur type linear solver to solve the LM step, take
53// extra care to place the E blocks and the F blocks contiguously. E blocks are
54// the first num_eliminate_blocks parameter blocks as indicated by the parameter
55// block ordering. The remaining parameter blocks are the F blocks.
56//
Dmitriy Korchemkin75baced2023-06-19 18:00:28 +000057// In order to simplify handling block-sparse to CRS conversion, cells within
58// the row-block of non-partitioned matrix are stored in memory sequentially in
59// the order of increasing column-block id. In case of partitioned matrices,
60// cells corresponding to F sub-matrix are stored sequentially in the order of
61// increasing column-block id (with cells corresponding to E sub-matrix stored
62// separately).
63//
Keir Mierle8ebb0732012-04-30 23:09:08 -070064// TODO(keir): Consider if we should use a boolean for each parameter block
65// instead of num_eliminate_blocks.
Sameer Agarwalee90f6c2023-08-10 20:56:59 -070066bool BuildJacobianLayout(const Program& program,
Keir Mierle8ebb0732012-04-30 23:09:08 -070067 int num_eliminate_blocks,
Alexander Ivanov53df5dd2023-01-11 16:51:38 +000068 std::vector<int*>* jacobian_layout,
69 std::vector<int>* jacobian_layout_storage) {
Sameer Agarwal9602ed72023-01-14 05:54:24 -080070 const std::vector<ResidualBlock*>& residual_blocks =
71 program.residual_blocks();
Keir Mierle8ebb0732012-04-30 23:09:08 -070072
73 // Iterate over all the active residual blocks and determine how many E blocks
74 // are there. This will determine where the F blocks start in the jacobian
75 // matrix. Also compute the number of jacobian blocks.
Sameer Agarwalee90f6c2023-08-10 20:56:59 -070076 unsigned int f_block_pos = 0;
77 unsigned int num_jacobian_blocks = 0;
Sergiu Deitschc8658c82022-02-20 02:22:17 +010078 for (auto* residual_block : residual_blocks) {
Keir Mierle8ebb0732012-04-30 23:09:08 -070079 const int num_residuals = residual_block->NumResiduals();
80 const int num_parameter_blocks = residual_block->NumParameterBlocks();
81
82 // Advance f_block_pos over each E block for this residual.
83 for (int j = 0; j < num_parameter_blocks; ++j) {
84 ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
85 if (!parameter_block->IsConstant()) {
86 // Only count blocks for active parameters.
87 num_jacobian_blocks++;
88 if (parameter_block->index() < num_eliminate_blocks) {
Sameer Agarwal125a0e92021-12-28 07:05:03 -080089 f_block_pos += num_residuals * parameter_block->TangentSize();
Keir Mierle8ebb0732012-04-30 23:09:08 -070090 }
91 }
92 }
Sameer Agarwalee90f6c2023-08-10 20:56:59 -070093 if (num_jacobian_blocks > std::numeric_limits<int>::max()) {
94 LOG(ERROR) << "Overlow error. Too many blocks in the jacobian matrix : "
95 << num_jacobian_blocks;
96 return false;
97 }
Keir Mierle8ebb0732012-04-30 23:09:08 -070098 }
99
100 // We now know that the E blocks are laid out starting at zero, and the F
101 // blocks are laid out starting at f_block_pos. Iterate over the residual
102 // blocks again, and this time fill the jacobian_layout array with the
103 // position information.
104
105 jacobian_layout->resize(program.NumResidualBlocks());
106 jacobian_layout_storage->resize(num_jacobian_blocks);
107
108 int e_block_pos = 0;
Sameer Agarwalf802a092022-08-25 10:00:12 +0530109 int* jacobian_pos = jacobian_layout_storage->data();
Dmitriy Korchemkin75baced2023-06-19 18:00:28 +0000110 std::vector<std::pair<int, int>> active_parameter_blocks;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700111 for (int i = 0; i < residual_blocks.size(); ++i) {
112 const ResidualBlock* residual_block = residual_blocks[i];
113 const int num_residuals = residual_block->NumResiduals();
114 const int num_parameter_blocks = residual_block->NumParameterBlocks();
115
116 (*jacobian_layout)[i] = jacobian_pos;
Dmitriy Korchemkin75baced2023-06-19 18:00:28 +0000117 // Cells from F sub-matrix are to be stored sequentially with increasing
118 // column block id. For each non-constant parameter block, a pair of indices
119 // (index in the list of active parameter blocks and index in the list of
120 // all parameter blocks) is computed, and index pairs are sorted by the
121 // index of corresponding column block id.
122 active_parameter_blocks.clear();
123 active_parameter_blocks.reserve(num_parameter_blocks);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700124 for (int j = 0; j < num_parameter_blocks; ++j) {
125 ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
Keir Mierle8ebb0732012-04-30 23:09:08 -0700126 if (parameter_block->IsConstant()) {
127 continue;
128 }
Dmitriy Korchemkin75baced2023-06-19 18:00:28 +0000129 const int k = active_parameter_blocks.size();
130 active_parameter_blocks.emplace_back(k, j);
131 }
132 std::sort(active_parameter_blocks.begin(),
133 active_parameter_blocks.end(),
134 [&residual_block](const std::pair<int, int>& a,
135 const std::pair<int, int>& b) {
136 return residual_block->parameter_blocks()[a.second]->index() <
137 residual_block->parameter_blocks()[b.second]->index();
138 });
139 // Cell positions for each active parameter block are filled in the order of
140 // active parameter block indices sorted by columnd block index. This
141 // guarantees that cells are laid out sequentially with increasing column
142 // block indices.
143 for (const auto& indices : active_parameter_blocks) {
144 const auto [k, j] = indices;
145 ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
146 const int parameter_block_index = parameter_block->index();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700147 const int jacobian_block_size =
Sameer Agarwal125a0e92021-12-28 07:05:03 -0800148 num_residuals * parameter_block->TangentSize();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700149 if (parameter_block_index < num_eliminate_blocks) {
Dmitriy Korchemkin75baced2023-06-19 18:00:28 +0000150 jacobian_pos[k] = e_block_pos;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700151 e_block_pos += jacobian_block_size;
152 } else {
Sameer Agarwalee90f6c2023-08-10 20:56:59 -0700153 jacobian_pos[k] = static_cast<int>(f_block_pos);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700154 f_block_pos += jacobian_block_size;
Sameer Agarwalee90f6c2023-08-10 20:56:59 -0700155 if (f_block_pos > std::numeric_limits<int>::max()) {
156 LOG(ERROR)
157 << "Overlow error. Too many entries in the Jacobian matrix.";
158 return false;
159 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700160 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700161 }
Dmitriy Korchemkin75baced2023-06-19 18:00:28 +0000162 jacobian_pos += active_parameter_blocks.size();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700163 }
Sameer Agarwalee90f6c2023-08-10 20:56:59 -0700164 return true;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700165}
166
167} // namespace
168
169BlockJacobianWriter::BlockJacobianWriter(const Evaluator::Options& options,
170 Program* program)
Sameer Agarwal0f9de3d2023-05-15 09:13:34 -0700171 : options_(options), program_(program) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700172 CHECK_GE(options.num_eliminate_blocks, 0)
173 << "num_eliminate_blocks must be greater than 0.";
174
Sameer Agarwalee90f6c2023-08-10 20:56:59 -0700175 jacobian_layout_is_valid_ = BuildJacobianLayout(*program,
176 options.num_eliminate_blocks,
177 &jacobian_layout_,
178 &jacobian_layout_storage_);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700179}
180
181// Create evaluate prepareres that point directly into the final jacobian. This
182// makes the final Write() a nop.
Sameer Agarwal44039af2022-02-10 16:16:37 -0800183std::unique_ptr<BlockEvaluatePreparer[]>
Sergiu Deitsch388c1422022-08-14 12:49:45 +0200184BlockJacobianWriter::CreateEvaluatePreparers(unsigned num_threads) {
Alexander Ivanov5e787ab2023-01-28 03:42:03 +0000185 const int max_derivatives_per_residual_block =
Keir Mierlef44907f2012-07-06 13:52:32 -0700186 program_->MaxDerivativesPerResidualBlock();
187
Sameer Agarwal44039af2022-02-10 16:16:37 -0800188 auto preparers = std::make_unique<BlockEvaluatePreparer[]>(num_threads);
Sergiu Deitsch388c1422022-08-14 12:49:45 +0200189 for (unsigned i = 0; i < num_threads; i++) {
Sameer Agarwalf802a092022-08-25 10:00:12 +0530190 preparers[i].Init(jacobian_layout_.data(),
191 max_derivatives_per_residual_block);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700192 }
193 return preparers;
194}
195
Sameer Agarwalae652192022-02-02 13:17:29 -0800196std::unique_ptr<SparseMatrix> BlockJacobianWriter::CreateJacobian() const {
Sameer Agarwalee90f6c2023-08-10 20:56:59 -0700197 if (!jacobian_layout_is_valid_) {
198 LOG(ERROR) << "Unable to create Jacobian matrix. Too many entries in the "
199 "Jacobian matrix.";
200 return nullptr;
201 }
202
Sergiu Deitschc8658c82022-02-20 02:22:17 +0100203 auto* bs = new CompressedRowBlockStructure;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700204
Alexander Ivanov53df5dd2023-01-11 16:51:38 +0000205 const std::vector<ParameterBlock*>& parameter_blocks =
Keir Mierle8ebb0732012-04-30 23:09:08 -0700206 program_->parameter_blocks();
207
208 // Construct the column blocks.
209 bs->cols.resize(parameter_blocks.size());
210 for (int i = 0, cursor = 0; i < parameter_blocks.size(); ++i) {
211 CHECK_NE(parameter_blocks[i]->index(), -1);
212 CHECK(!parameter_blocks[i]->IsConstant());
Sameer Agarwal125a0e92021-12-28 07:05:03 -0800213 bs->cols[i].size = parameter_blocks[i]->TangentSize();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700214 bs->cols[i].position = cursor;
215 cursor += bs->cols[i].size;
216 }
217
218 // Construct the cells in each row.
Sameer Agarwal9602ed72023-01-14 05:54:24 -0800219 const std::vector<ResidualBlock*>& residual_blocks =
220 program_->residual_blocks();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700221 int row_block_position = 0;
222 bs->rows.resize(residual_blocks.size());
223 for (int i = 0; i < residual_blocks.size(); ++i) {
224 const ResidualBlock* residual_block = residual_blocks[i];
225 CompressedRow* row = &bs->rows[i];
226
227 row->block.size = residual_block->NumResiduals();
228 row->block.position = row_block_position;
229 row_block_position += row->block.size;
230
231 // Size the row by the number of active parameters in this residual.
232 const int num_parameter_blocks = residual_block->NumParameterBlocks();
233 int num_active_parameter_blocks = 0;
234 for (int j = 0; j < num_parameter_blocks; ++j) {
235 if (residual_block->parameter_blocks()[j]->index() != -1) {
236 num_active_parameter_blocks++;
237 }
238 }
239 row->cells.resize(num_active_parameter_blocks);
240
241 // Add layout information for the active parameters in this row.
242 for (int j = 0, k = 0; j < num_parameter_blocks; ++j) {
243 const ParameterBlock* parameter_block =
244 residual_block->parameter_blocks()[j];
245 if (!parameter_block->IsConstant()) {
246 Cell& cell = row->cells[k];
247 cell.block_id = parameter_block->index();
248 cell.position = jacobian_layout_[i][k];
249
250 // Only increment k for active parameters, since there is only layout
251 // information for active parameters.
252 k++;
253 }
254 }
255
Sameer Agarwalae652192022-02-02 13:17:29 -0800256 std::sort(row->cells.begin(), row->cells.end(), CellLessThan);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700257 }
258
Sameer Agarwal0f9de3d2023-05-15 09:13:34 -0700259 return std::make_unique<BlockSparseMatrix>(
260 bs, options_.sparse_linear_algebra_library_type == CUDA_SPARSE);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700261}
262
Sameer Agarwalcaf614a2022-04-21 17:41:10 -0700263} // namespace ceres::internal