1 // 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: sameeragarwal (at) google.com (Sameer Agarwal) 30 31 #include "ceres/compressed_row_sparse_matrix.h" 32 33 #include <algorithm> 34 #include <vector> 35 #include "ceres/crs_matrix.h" 36 #include "ceres/internal/port.h" 37 #include "ceres/triplet_sparse_matrix.h" 38 #include "glog/logging.h" 39 40 namespace ceres { 41 namespace internal { 42 namespace { 43 44 // Helper functor used by the constructor for reordering the contents 45 // of a TripletSparseMatrix. This comparator assumes thay there are no 46 // duplicates in the pair of arrays rows and cols, i.e., there is no 47 // indices i and j (not equal to each other) s.t. 48 // 49 // rows[i] == rows[j] && cols[i] == cols[j] 50 // 51 // If this is the case, this functor will not be a StrictWeakOrdering. 52 struct RowColLessThan { 53 RowColLessThan(const int* rows, const int* cols) 54 : rows(rows), cols(cols) { 55 } 56 57 bool operator()(const int x, const int y) const { 58 if (rows[x] == rows[y]) { 59 return (cols[x] < cols[y]); 60 } 61 return (rows[x] < rows[y]); 62 } 63 64 const int* rows; 65 const int* cols; 66 }; 67 68 } // namespace 69 70 // This constructor gives you a semi-initialized CompressedRowSparseMatrix. 71 CompressedRowSparseMatrix::CompressedRowSparseMatrix(int num_rows, 72 int num_cols, 73 int max_num_nonzeros) { 74 num_rows_ = num_rows; 75 num_cols_ = num_cols; 76 rows_.resize(num_rows + 1, 0); 77 cols_.resize(max_num_nonzeros, 0); 78 values_.resize(max_num_nonzeros, 0.0); 79 80 81 VLOG(1) << "# of rows: " << num_rows_ 82 << " # of columns: " << num_cols_ 83 << " max_num_nonzeros: " << cols_.size() 84 << ". Allocating " << (num_rows_ + 1) * sizeof(int) + // NOLINT 85 cols_.size() * sizeof(int) + // NOLINT 86 cols_.size() * sizeof(double); // NOLINT 87 } 88 89 CompressedRowSparseMatrix::CompressedRowSparseMatrix( 90 const TripletSparseMatrix& m) { 91 num_rows_ = m.num_rows(); 92 num_cols_ = m.num_cols(); 93 94 rows_.resize(num_rows_ + 1, 0); 95 cols_.resize(m.num_nonzeros(), 0); 96 values_.resize(m.max_num_nonzeros(), 0.0); 97 98 // index is the list of indices into the TripletSparseMatrix m. 99 vector<int> index(m.num_nonzeros(), 0); 100 for (int i = 0; i < m.num_nonzeros(); ++i) { 101 index[i] = i; 102 } 103 104 // Sort index such that the entries of m are ordered by row and ties 105 // are broken by column. 106 sort(index.begin(), index.end(), RowColLessThan(m.rows(), m.cols())); 107 108 VLOG(1) << "# of rows: " << num_rows_ 109 << " # of columns: " << num_cols_ 110 << " max_num_nonzeros: " << cols_.size() 111 << ". Allocating " 112 << ((num_rows_ + 1) * sizeof(int) + // NOLINT 113 cols_.size() * sizeof(int) + // NOLINT 114 cols_.size() * sizeof(double)); // NOLINT 115 116 // Copy the contents of the cols and values array in the order given 117 // by index and count the number of entries in each row. 118 for (int i = 0; i < m.num_nonzeros(); ++i) { 119 const int idx = index[i]; 120 ++rows_[m.rows()[idx] + 1]; 121 cols_[i] = m.cols()[idx]; 122 values_[i] = m.values()[idx]; 123 } 124 125 // Find the cumulative sum of the row counts. 126 for (int i = 1; i < num_rows_ + 1; ++i) { 127 rows_[i] += rows_[i-1]; 128 } 129 130 CHECK_EQ(num_nonzeros(), m.num_nonzeros()); 131 } 132 133 CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal, 134 int num_rows) { 135 CHECK_NOTNULL(diagonal); 136 137 num_rows_ = num_rows; 138 num_cols_ = num_rows; 139 rows_.resize(num_rows + 1); 140 cols_.resize(num_rows); 141 values_.resize(num_rows); 142 143 rows_[0] = 0; 144 for (int i = 0; i < num_rows_; ++i) { 145 cols_[i] = i; 146 values_[i] = diagonal[i]; 147 rows_[i + 1] = i + 1; 148 } 149 150 CHECK_EQ(num_nonzeros(), num_rows); 151 } 152 153 CompressedRowSparseMatrix::~CompressedRowSparseMatrix() { 154 } 155 156 void CompressedRowSparseMatrix::SetZero() { 157 fill(values_.begin(), values_.end(), 0); 158 } 159 160 void CompressedRowSparseMatrix::RightMultiply(const double* x, 161 double* y) const { 162 CHECK_NOTNULL(x); 163 CHECK_NOTNULL(y); 164 165 for (int r = 0; r < num_rows_; ++r) { 166 for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) { 167 y[r] += values_[idx] * x[cols_[idx]]; 168 } 169 } 170 } 171 172 void CompressedRowSparseMatrix::LeftMultiply(const double* x, double* y) const { 173 CHECK_NOTNULL(x); 174 CHECK_NOTNULL(y); 175 176 for (int r = 0; r < num_rows_; ++r) { 177 for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) { 178 y[cols_[idx]] += values_[idx] * x[r]; 179 } 180 } 181 } 182 183 void CompressedRowSparseMatrix::SquaredColumnNorm(double* x) const { 184 CHECK_NOTNULL(x); 185 186 fill(x, x + num_cols_, 0.0); 187 for (int idx = 0; idx < rows_[num_rows_]; ++idx) { 188 x[cols_[idx]] += values_[idx] * values_[idx]; 189 } 190 } 191 192 void CompressedRowSparseMatrix::ScaleColumns(const double* scale) { 193 CHECK_NOTNULL(scale); 194 195 for (int idx = 0; idx < rows_[num_rows_]; ++idx) { 196 values_[idx] *= scale[cols_[idx]]; 197 } 198 } 199 200 void CompressedRowSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const { 201 CHECK_NOTNULL(dense_matrix); 202 dense_matrix->resize(num_rows_, num_cols_); 203 dense_matrix->setZero(); 204 205 for (int r = 0; r < num_rows_; ++r) { 206 for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) { 207 (*dense_matrix)(r, cols_[idx]) = values_[idx]; 208 } 209 } 210 } 211 212 void CompressedRowSparseMatrix::DeleteRows(int delta_rows) { 213 CHECK_GE(delta_rows, 0); 214 CHECK_LE(delta_rows, num_rows_); 215 216 num_rows_ -= delta_rows; 217 rows_.resize(num_rows_ + 1); 218 } 219 220 void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) { 221 CHECK_EQ(m.num_cols(), num_cols_); 222 223 if (cols_.size() < num_nonzeros() + m.num_nonzeros()) { 224 cols_.resize(num_nonzeros() + m.num_nonzeros()); 225 values_.resize(num_nonzeros() + m.num_nonzeros()); 226 } 227 228 // Copy the contents of m into this matrix. 229 copy(m.cols(), m.cols() + m.num_nonzeros(), &cols_[num_nonzeros()]); 230 copy(m.values(), m.values() + m.num_nonzeros(), &values_[num_nonzeros()]); 231 rows_.resize(num_rows_ + m.num_rows() + 1); 232 // new_rows = [rows_, m.row() + rows_[num_rows_]] 233 fill(rows_.begin() + num_rows_, 234 rows_.begin() + num_rows_ + m.num_rows() + 1, 235 rows_[num_rows_]); 236 237 for (int r = 0; r < m.num_rows() + 1; ++r) { 238 rows_[num_rows_ + r] += m.rows()[r]; 239 } 240 241 num_rows_ += m.num_rows(); 242 } 243 244 void CompressedRowSparseMatrix::ToTextFile(FILE* file) const { 245 CHECK_NOTNULL(file); 246 for (int r = 0; r < num_rows_; ++r) { 247 for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) { 248 fprintf(file, 249 "% 10d % 10d %17f\n", 250 r, 251 cols_[idx], 252 values_[idx]); 253 } 254 } 255 } 256 257 void CompressedRowSparseMatrix::ToCRSMatrix(CRSMatrix* matrix) const { 258 matrix->num_rows = num_rows_; 259 matrix->num_cols = num_cols_; 260 matrix->rows = rows_; 261 matrix->cols = cols_; 262 matrix->values = values_; 263 264 // Trim. 265 matrix->rows.resize(matrix->num_rows + 1); 266 matrix->cols.resize(matrix->rows[matrix->num_rows]); 267 matrix->values.resize(matrix->rows[matrix->num_rows]); 268 } 269 270 void CompressedRowSparseMatrix::SolveLowerTriangularInPlace( 271 double* solution) const { 272 for (int r = 0; r < num_rows_; ++r) { 273 for (int idx = rows_[r]; idx < rows_[r + 1] - 1; ++idx) { 274 solution[r] -= values_[idx] * solution[cols_[idx]]; 275 } 276 solution[r] /= values_[rows_[r + 1] - 1]; 277 } 278 } 279 280 void CompressedRowSparseMatrix::SolveLowerTriangularTransposeInPlace( 281 double* solution) const { 282 for (int r = num_rows_ - 1; r >= 0; --r) { 283 solution[r] /= values_[rows_[r + 1] - 1]; 284 for (int idx = rows_[r + 1] - 2; idx >= rows_[r]; --idx) { 285 solution[cols_[idx]] -= values_[idx] * solution[r]; 286 } 287 } 288 } 289 290 CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( 291 const double* diagonal, 292 const vector<int>& blocks) { 293 int num_rows = 0; 294 int num_nonzeros = 0; 295 for (int i = 0; i < blocks.size(); ++i) { 296 num_rows += blocks[i]; 297 num_nonzeros += blocks[i] * blocks[i]; 298 } 299 300 CompressedRowSparseMatrix* matrix = 301 new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros); 302 303 int* rows = matrix->mutable_rows(); 304 int* cols = matrix->mutable_cols(); 305 double* values = matrix->mutable_values(); 306 fill(values, values + num_nonzeros, 0.0); 307 308 int idx_cursor = 0; 309 int col_cursor = 0; 310 for (int i = 0; i < blocks.size(); ++i) { 311 const int block_size = blocks[i]; 312 for (int r = 0; r < block_size; ++r) { 313 *(rows++) = idx_cursor; 314 values[idx_cursor + r] = diagonal[col_cursor + r]; 315 for (int c = 0; c < block_size; ++c, ++idx_cursor) { 316 *(cols++) = col_cursor + c; 317 } 318 } 319 col_cursor += block_size; 320 } 321 *rows = idx_cursor; 322 323 *matrix->mutable_row_blocks() = blocks; 324 *matrix->mutable_col_blocks() = blocks; 325 326 CHECK_EQ(idx_cursor, num_nonzeros); 327 CHECK_EQ(col_cursor, num_rows); 328 return matrix; 329 } 330 331 CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const { 332 CompressedRowSparseMatrix* transpose = 333 new CompressedRowSparseMatrix(num_cols_, num_rows_, num_nonzeros()); 334 335 int* transpose_rows = transpose->mutable_rows(); 336 int* transpose_cols = transpose->mutable_cols(); 337 double* transpose_values = transpose->mutable_values(); 338 339 for (int idx = 0; idx < num_nonzeros(); ++idx) { 340 ++transpose_rows[cols_[idx] + 1]; 341 } 342 343 for (int i = 1; i < transpose->num_rows() + 1; ++i) { 344 transpose_rows[i] += transpose_rows[i - 1]; 345 } 346 347 for (int r = 0; r < num_rows(); ++r) { 348 for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) { 349 const int c = cols_[idx]; 350 const int transpose_idx = transpose_rows[c]++; 351 transpose_cols[transpose_idx] = r; 352 transpose_values[transpose_idx] = values_[idx]; 353 } 354 } 355 356 for (int i = transpose->num_rows() - 1; i > 0 ; --i) { 357 transpose_rows[i] = transpose_rows[i - 1]; 358 } 359 transpose_rows[0] = 0; 360 361 return transpose; 362 } 363 364 365 } // namespace internal 366 } // namespace ceres 367