Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 1 | // Ceres Solver - A fast non-linear least squares minimizer |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 2 | // Copyright 2023 Google Inc. All rights reserved. |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 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 | // |
| 31 | // Block structure objects are used to carry information about the |
| 32 | // dense block structure of sparse matrices. The BlockSparseMatrix |
| 33 | // object uses the BlockStructure objects to keep track of the matrix |
| 34 | // structure and operate upon it. This allows us to use more cache |
| 35 | // friendly block oriented linear algebra operations on the matrix |
| 36 | // instead of accessing it one scalar entry at a time. |
| 37 | |
| 38 | #ifndef CERES_INTERNAL_BLOCK_STRUCTURE_H_ |
| 39 | #define CERES_INTERNAL_BLOCK_STRUCTURE_H_ |
| 40 | |
| 41 | #include <cstdint> |
| 42 | #include <vector> |
Austin Schuh | 1d1e6ea | 2020-12-23 21:56:30 -0800 | [diff] [blame] | 43 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 44 | #include "ceres/internal/export.h" |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 45 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 46 | // This file is being included into source files that are compiled with nvcc. |
| 47 | // nvcc shipped with ubuntu 20.04 does not support some features of c++17, |
| 48 | // including nested namespace definitions |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 49 | namespace ceres { |
| 50 | namespace internal { |
| 51 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 52 | using BlockSize = int32_t; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 53 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 54 | struct CERES_NO_EXPORT Block { |
| 55 | Block() = default; |
| 56 | Block(int size_, int position_) noexcept : size(size_), position(position_) {} |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 57 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 58 | BlockSize size{-1}; |
| 59 | int position{-1}; // Position along the row/column. |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 60 | }; |
| 61 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 62 | inline bool operator==(const Block& left, const Block& right) noexcept { |
| 63 | return (left.size == right.size) && (left.position == right.position); |
| 64 | } |
| 65 | |
| 66 | struct CERES_NO_EXPORT Cell { |
| 67 | Cell() = default; |
| 68 | Cell(int block_id_, int position_) noexcept |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 69 | : block_id(block_id_), position(position_) {} |
| 70 | |
| 71 | // Column or row block id as the case maybe. |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 72 | int block_id{-1}; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 73 | // Where in the values array of the jacobian is this cell located. |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 74 | int position{-1}; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 75 | }; |
| 76 | |
| 77 | // Order cell by their block_id; |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 78 | CERES_NO_EXPORT bool CellLessThan(const Cell& lhs, const Cell& rhs); |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 79 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 80 | struct CERES_NO_EXPORT CompressedList { |
| 81 | CompressedList() = default; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 82 | |
| 83 | // Construct a CompressedList with the cells containing num_cells |
| 84 | // entries. |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 85 | explicit CompressedList(int num_cells) noexcept : cells(num_cells) {} |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 86 | Block block; |
| 87 | std::vector<Cell> cells; |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 88 | // Number of non-zeros in cells of this row block |
| 89 | int nnz{-1}; |
| 90 | // Number of non-zeros in cells of this and every preceeding row block in |
| 91 | // block-sparse matrix |
| 92 | int cumulative_nnz{-1}; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 93 | }; |
| 94 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 95 | using CompressedRow = CompressedList; |
| 96 | using CompressedColumn = CompressedList; |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 97 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 98 | // CompressedRowBlockStructure specifies the storage structure of a row block |
| 99 | // sparse matrix. |
| 100 | // |
| 101 | // Consider the following matrix A: |
| 102 | // A = [A_11 A_12 ... |
| 103 | // A_21 A_22 ... |
| 104 | // ... |
| 105 | // A_m1 A_m2 ... ] |
| 106 | // |
| 107 | // A row block sparse matrix is a matrix where the following properties hold: |
| 108 | // 1. The number of rows in every block A_ij and A_ik are the same. |
| 109 | // 2. The number of columns in every block A_ij and A_kj are the same. |
| 110 | // 3. The number of rows in A_ij and A_kj may be different (i != k). |
| 111 | // 4. The number of columns in A_ij and A_ik may be different (j != k). |
| 112 | // 5. Any block A_ij may be all 0s, in which case the block is not stored. |
| 113 | // |
| 114 | // The structure of the matrix is stored as follows: |
| 115 | // |
| 116 | // The `rows' array contains the following information for each row block: |
| 117 | // - rows[i].block.size: The number of rows in each block A_ij in the row block. |
| 118 | // - rows[i].block.position: The starting row in the full matrix A of the |
| 119 | // row block i. |
| 120 | // - rows[i].cells[j].block_id: The index into the `cols' array corresponding to |
| 121 | // the non-zero blocks A_ij. |
| 122 | // - rows[i].cells[j].position: The index in the `values' array for the contents |
| 123 | // of block A_ij. |
| 124 | // |
| 125 | // The `cols' array contains the following information for block: |
| 126 | // - cols[.].size: The number of columns spanned by the block. |
| 127 | // - cols[.].position: The starting column in the full matrix A of the block. |
| 128 | // |
| 129 | // |
| 130 | // Example of a row block sparse matrix: |
| 131 | // block_id: | 0 |1|2 |3 | |
| 132 | // rows[0]: [ 1 2 0 3 4 0 ] |
| 133 | // [ 5 6 0 7 8 0 ] |
| 134 | // rows[1]: [ 0 0 9 0 0 0 ] |
| 135 | // |
| 136 | // This matrix is stored as follows: |
| 137 | // |
| 138 | // There are four column blocks: |
| 139 | // cols[0].size = 2 |
| 140 | // cols[0].position = 0 |
| 141 | // cols[1].size = 1 |
| 142 | // cols[1].position = 2 |
| 143 | // cols[2].size = 2 |
| 144 | // cols[2].position = 3 |
| 145 | // cols[3].size = 1 |
| 146 | // cols[3].position = 5 |
| 147 | |
| 148 | // The first row block spans two rows, starting at row 0: |
| 149 | // rows[0].block.size = 2 // This row block spans two rows. |
| 150 | // rows[0].block.position = 0 // It starts at row 0. |
| 151 | // rows[0] has two cells, at column blocks 0 and 2: |
| 152 | // rows[0].cells[0].block_id = 0 // This cell is in column block 0. |
| 153 | // rows[0].cells[0].position = 0 // See below for an explanation of this. |
| 154 | // rows[0].cells[1].block_id = 2 // This cell is in column block 2. |
| 155 | // rows[0].cells[1].position = 4 // See below for an explanation of this. |
| 156 | // |
| 157 | // The second row block spans two rows, starting at row 2: |
| 158 | // rows[1].block.size = 1 // This row block spans one row. |
| 159 | // rows[1].block.position = 2 // It starts at row 2. |
| 160 | // rows[1] has one cell at column block 1: |
| 161 | // rows[1].cells[0].block_id = 1 // This cell is in column block 1. |
| 162 | // rows[1].cells[0].position = 8 // See below for an explanation of this. |
| 163 | // |
| 164 | // The values in each blocks are stored contiguously in row major order. |
| 165 | // However, there is no unique way to order the blocks -- it is usually |
| 166 | // optimized to promote cache coherent access, e.g. ordering it so that |
| 167 | // Jacobian blocks of parameters of the same type are stored nearby. |
| 168 | // This is one possible way to store the values of the blocks in a values array: |
| 169 | // values = { 1, 2, 5, 6, 3, 4, 7, 8, 9 } |
| 170 | // | | | | // The three blocks. |
| 171 | // ^ rows[0].cells[0].position = 0 |
| 172 | // ^ rows[0].cells[1].position = 4 |
| 173 | // ^ rows[1].cells[0].position = 8 |
| 174 | struct CERES_NO_EXPORT CompressedRowBlockStructure { |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 175 | std::vector<Block> cols; |
| 176 | std::vector<CompressedRow> rows; |
| 177 | }; |
| 178 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 179 | struct CERES_NO_EXPORT CompressedColumnBlockStructure { |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 180 | std::vector<Block> rows; |
| 181 | std::vector<CompressedColumn> cols; |
| 182 | }; |
| 183 | |
Austin Schuh | 3de38b0 | 2024-06-25 18:25:10 -0700 | [diff] [blame^] | 184 | inline int NumScalarEntries(const std::vector<Block>& blocks) { |
| 185 | if (blocks.empty()) { |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | auto& block = blocks.back(); |
| 190 | return block.position + block.size; |
| 191 | } |
| 192 | |
| 193 | std::vector<Block> Tail(const std::vector<Block>& blocks, int n); |
| 194 | int SumSquaredSizes(const std::vector<Block>& blocks); |
| 195 | |
Austin Schuh | 70cc955 | 2019-01-21 19:46:48 -0800 | [diff] [blame] | 196 | } // namespace internal |
| 197 | } // namespace ceres |
| 198 | |
| 199 | #endif // CERES_INTERNAL_BLOCK_STRUCTURE_H_ |