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@google.com (Sameer Agarwal)
30
31#include "ceres/compressed_row_sparse_matrix.h"
32
33#include <algorithm>
34#include <numeric>
35#include <vector>
36#include "ceres/crs_matrix.h"
37#include "ceres/internal/port.h"
38#include "ceres/triplet_sparse_matrix.h"
39#include "glog/logging.h"
40
41namespace ceres {
42namespace internal {
43namespace {
44
45// Helper functor used by the constructor for reordering the contents
46// of a TripletSparseMatrix. This comparator assumes thay there are no
47// duplicates in the pair of arrays rows and cols, i.e., there is no
48// indices i and j (not equal to each other) s.t.
49//
50//  rows[i] == rows[j] && cols[i] == cols[j]
51//
52// If this is the case, this functor will not be a StrictWeakOrdering.
53struct RowColLessThan {
54  RowColLessThan(const int* rows, const int* cols)
55      : rows(rows), cols(cols) {
56  }
57
58  bool operator()(const int x, const int y) const {
59    if (rows[x] == rows[y]) {
60      return (cols[x] < cols[y]);
61    }
62    return (rows[x] < rows[y]);
63  }
64
65  const int* rows;
66  const int* cols;
67};
68
69}  // namespace
70
71// This constructor gives you a semi-initialized CompressedRowSparseMatrix.
72CompressedRowSparseMatrix::CompressedRowSparseMatrix(int num_rows,
73                                                     int num_cols,
74                                                     int max_num_nonzeros) {
75  num_rows_ = num_rows;
76  num_cols_ = num_cols;
77  rows_.resize(num_rows + 1, 0);
78  cols_.resize(max_num_nonzeros, 0);
79  values_.resize(max_num_nonzeros, 0.0);
80
81
82  VLOG(1) << "# of rows: " << num_rows_
83          << " # of columns: " << num_cols_
84          << " max_num_nonzeros: " << cols_.size()
85          << ". Allocating " << (num_rows_ + 1) * sizeof(int) +  // NOLINT
86      cols_.size() * sizeof(int) +  // NOLINT
87      cols_.size() * sizeof(double);  // NOLINT
88}
89
90CompressedRowSparseMatrix::CompressedRowSparseMatrix(
91    const TripletSparseMatrix& m) {
92  num_rows_ = m.num_rows();
93  num_cols_ = m.num_cols();
94
95  rows_.resize(num_rows_ + 1, 0);
96  cols_.resize(m.num_nonzeros(), 0);
97  values_.resize(m.max_num_nonzeros(), 0.0);
98
99  // index is the list of indices into the TripletSparseMatrix m.
100  vector<int> index(m.num_nonzeros(), 0);
101  for (int i = 0; i < m.num_nonzeros(); ++i) {
102    index[i] = i;
103  }
104
105  // Sort index such that the entries of m are ordered by row and ties
106  // are broken by column.
107  sort(index.begin(), index.end(), RowColLessThan(m.rows(), m.cols()));
108
109  VLOG(1) << "# of rows: " << num_rows_
110          << " # of columns: " << num_cols_
111          << " max_num_nonzeros: " << cols_.size()
112          << ". Allocating "
113          << ((num_rows_ + 1) * sizeof(int) +  // NOLINT
114              cols_.size() * sizeof(int) +     // NOLINT
115              cols_.size() * sizeof(double));  // NOLINT
116
117  // Copy the contents of the cols and values array in the order given
118  // by index and count the number of entries in each row.
119  for (int i = 0; i < m.num_nonzeros(); ++i) {
120    const int idx = index[i];
121    ++rows_[m.rows()[idx] + 1];
122    cols_[i] = m.cols()[idx];
123    values_[i] = m.values()[idx];
124  }
125
126  // Find the cumulative sum of the row counts.
127  for (int i = 1; i < num_rows_ + 1; ++i) {
128    rows_[i] += rows_[i - 1];
129  }
130
131  CHECK_EQ(num_nonzeros(), m.num_nonzeros());
132}
133
134CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal,
135                                                     int num_rows) {
136  CHECK_NOTNULL(diagonal);
137
138  num_rows_ = num_rows;
139  num_cols_ = num_rows;
140  rows_.resize(num_rows + 1);
141  cols_.resize(num_rows);
142  values_.resize(num_rows);
143
144  rows_[0] = 0;
145  for (int i = 0; i < num_rows_; ++i) {
146    cols_[i] = i;
147    values_[i] = diagonal[i];
148    rows_[i + 1] = i + 1;
149  }
150
151  CHECK_EQ(num_nonzeros(), num_rows);
152}
153
154CompressedRowSparseMatrix::~CompressedRowSparseMatrix() {
155}
156
157void CompressedRowSparseMatrix::SetZero() {
158  fill(values_.begin(), values_.end(), 0);
159}
160
161void CompressedRowSparseMatrix::RightMultiply(const double* x,
162                                              double* y) const {
163  CHECK_NOTNULL(x);
164  CHECK_NOTNULL(y);
165
166  for (int r = 0; r < num_rows_; ++r) {
167    for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
168      y[r] += values_[idx] * x[cols_[idx]];
169    }
170  }
171}
172
173void CompressedRowSparseMatrix::LeftMultiply(const double* x, double* y) const {
174  CHECK_NOTNULL(x);
175  CHECK_NOTNULL(y);
176
177  for (int r = 0; r < num_rows_; ++r) {
178    for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
179      y[cols_[idx]] += values_[idx] * x[r];
180    }
181  }
182}
183
184void CompressedRowSparseMatrix::SquaredColumnNorm(double* x) const {
185  CHECK_NOTNULL(x);
186
187  fill(x, x + num_cols_, 0.0);
188  for (int idx = 0; idx < rows_[num_rows_]; ++idx) {
189    x[cols_[idx]] += values_[idx] * values_[idx];
190  }
191}
192
193void CompressedRowSparseMatrix::ScaleColumns(const double* scale) {
194  CHECK_NOTNULL(scale);
195
196  for (int idx = 0; idx < rows_[num_rows_]; ++idx) {
197    values_[idx] *= scale[cols_[idx]];
198  }
199}
200
201void CompressedRowSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const {
202  CHECK_NOTNULL(dense_matrix);
203  dense_matrix->resize(num_rows_, num_cols_);
204  dense_matrix->setZero();
205
206  for (int r = 0; r < num_rows_; ++r) {
207    for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
208      (*dense_matrix)(r, cols_[idx]) = values_[idx];
209    }
210  }
211}
212
213void CompressedRowSparseMatrix::DeleteRows(int delta_rows) {
214  CHECK_GE(delta_rows, 0);
215  CHECK_LE(delta_rows, num_rows_);
216
217  num_rows_ -= delta_rows;
218  rows_.resize(num_rows_ + 1);
219
220  // Walk the list of row blocks until we reach the new number of rows
221  // and the drop the rest of the row blocks.
222  int num_row_blocks = 0;
223  int num_rows = 0;
224  while (num_row_blocks < row_blocks_.size() && num_rows < num_rows_) {
225    num_rows += row_blocks_[num_row_blocks];
226    ++num_row_blocks;
227  }
228
229  row_blocks_.resize(num_row_blocks);
230}
231
232void CompressedRowSparseMatrix::AppendRows(const CompressedRowSparseMatrix& m) {
233  CHECK_EQ(m.num_cols(), num_cols_);
234
235  CHECK(row_blocks_.size() == 0 || m.row_blocks().size() !=0)
236      << "Cannot append a matrix with row blocks to one without and vice versa."
237      << "This matrix has : " << row_blocks_.size() << " row blocks."
238      << "The matrix being appended has: " << m.row_blocks().size()
239      << " row blocks.";
240
241  if (cols_.size() < num_nonzeros() + m.num_nonzeros()) {
242    cols_.resize(num_nonzeros() + m.num_nonzeros());
243    values_.resize(num_nonzeros() + m.num_nonzeros());
244  }
245
246  // Copy the contents of m into this matrix.
247  copy(m.cols(), m.cols() + m.num_nonzeros(), &cols_[num_nonzeros()]);
248  copy(m.values(), m.values() + m.num_nonzeros(), &values_[num_nonzeros()]);
249  rows_.resize(num_rows_ + m.num_rows() + 1);
250  // new_rows = [rows_, m.row() + rows_[num_rows_]]
251  fill(rows_.begin() + num_rows_,
252       rows_.begin() + num_rows_ + m.num_rows() + 1,
253       rows_[num_rows_]);
254
255  for (int r = 0; r < m.num_rows() + 1; ++r) {
256    rows_[num_rows_ + r] += m.rows()[r];
257  }
258
259  num_rows_ += m.num_rows();
260  row_blocks_.insert(row_blocks_.end(), m.row_blocks().begin(), m.row_blocks().end());
261}
262
263void CompressedRowSparseMatrix::ToTextFile(FILE* file) const {
264  CHECK_NOTNULL(file);
265  for (int r = 0; r < num_rows_; ++r) {
266    for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
267      fprintf(file,
268              "% 10d % 10d %17f\n",
269              r,
270              cols_[idx],
271              values_[idx]);
272    }
273  }
274}
275
276void CompressedRowSparseMatrix::ToCRSMatrix(CRSMatrix* matrix) const {
277  matrix->num_rows = num_rows_;
278  matrix->num_cols = num_cols_;
279  matrix->rows = rows_;
280  matrix->cols = cols_;
281  matrix->values = values_;
282
283  // Trim.
284  matrix->rows.resize(matrix->num_rows + 1);
285  matrix->cols.resize(matrix->rows[matrix->num_rows]);
286  matrix->values.resize(matrix->rows[matrix->num_rows]);
287}
288
289void CompressedRowSparseMatrix::SetMaxNumNonZeros(int num_nonzeros) {
290  CHECK_GE(num_nonzeros, 0);
291
292  cols_.resize(num_nonzeros);
293  values_.resize(num_nonzeros);
294}
295
296void CompressedRowSparseMatrix::SolveLowerTriangularInPlace(
297    double* solution) const {
298  for (int r = 0; r < num_rows_; ++r) {
299    for (int idx = rows_[r]; idx < rows_[r + 1] - 1; ++idx) {
300      solution[r] -= values_[idx] * solution[cols_[idx]];
301    }
302    solution[r] /=  values_[rows_[r + 1] - 1];
303  }
304}
305
306void CompressedRowSparseMatrix::SolveLowerTriangularTransposeInPlace(
307    double* solution) const {
308  for (int r = num_rows_ - 1; r >= 0; --r) {
309    solution[r] /= values_[rows_[r + 1] - 1];
310    for (int idx = rows_[r + 1] - 2; idx >= rows_[r]; --idx) {
311      solution[cols_[idx]] -= values_[idx] * solution[r];
312    }
313  }
314}
315
316CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
317    const double* diagonal,
318    const vector<int>& blocks) {
319  int num_rows = 0;
320  int num_nonzeros = 0;
321  for (int i = 0; i < blocks.size(); ++i) {
322    num_rows += blocks[i];
323    num_nonzeros += blocks[i] * blocks[i];
324  }
325
326  CompressedRowSparseMatrix* matrix =
327      new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros);
328
329  int* rows = matrix->mutable_rows();
330  int* cols = matrix->mutable_cols();
331  double* values = matrix->mutable_values();
332  fill(values, values + num_nonzeros, 0.0);
333
334  int idx_cursor = 0;
335  int col_cursor = 0;
336  for (int i = 0; i < blocks.size(); ++i) {
337    const int block_size = blocks[i];
338    for (int r = 0; r < block_size; ++r) {
339      *(rows++) = idx_cursor;
340      values[idx_cursor + r] = diagonal[col_cursor + r];
341      for (int c = 0; c < block_size; ++c, ++idx_cursor) {
342        *(cols++) = col_cursor + c;
343      }
344    }
345    col_cursor += block_size;
346  }
347  *rows = idx_cursor;
348
349  *matrix->mutable_row_blocks() = blocks;
350  *matrix->mutable_col_blocks() = blocks;
351
352  CHECK_EQ(idx_cursor, num_nonzeros);
353  CHECK_EQ(col_cursor, num_rows);
354  return matrix;
355}
356
357CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const {
358  CompressedRowSparseMatrix* transpose =
359      new CompressedRowSparseMatrix(num_cols_, num_rows_, num_nonzeros());
360
361  int* transpose_rows = transpose->mutable_rows();
362  int* transpose_cols = transpose->mutable_cols();
363  double* transpose_values = transpose->mutable_values();
364
365  for (int idx = 0; idx < num_nonzeros(); ++idx) {
366    ++transpose_rows[cols_[idx] + 1];
367  }
368
369  for (int i = 1; i < transpose->num_rows() + 1; ++i) {
370    transpose_rows[i] += transpose_rows[i - 1];
371  }
372
373  for (int r = 0; r < num_rows(); ++r) {
374    for (int idx = rows_[r]; idx < rows_[r + 1]; ++idx) {
375      const int c = cols_[idx];
376      const int transpose_idx = transpose_rows[c]++;
377      transpose_cols[transpose_idx] = r;
378      transpose_values[transpose_idx] = values_[idx];
379    }
380  }
381
382  for (int i = transpose->num_rows() - 1; i > 0 ; --i) {
383    transpose_rows[i] = transpose_rows[i - 1];
384  }
385  transpose_rows[0] = 0;
386
387  *(transpose->mutable_row_blocks()) = col_blocks_;
388  *(transpose->mutable_col_blocks()) = row_blocks_;
389
390  return transpose;
391}
392
393namespace {
394// A ProductTerm is a term in the outer product of a matrix with
395// itself.
396struct ProductTerm {
397  ProductTerm(const int row, const int col, const int index)
398      : row(row), col(col), index(index) {
399  }
400
401  bool operator<(const ProductTerm& right) const {
402    if (row == right.row) {
403      if (col == right.col) {
404        return index < right.index;
405      }
406      return col < right.col;
407    }
408    return row < right.row;
409  }
410
411  int row;
412  int col;
413  int index;
414};
415
416CompressedRowSparseMatrix*
417CompressAndFillProgram(const int num_rows,
418                       const int num_cols,
419                       const vector<ProductTerm>& product,
420                       vector<int>* program) {
421  CHECK_GT(product.size(), 0);
422
423  // Count the number of unique product term, which in turn is the
424  // number of non-zeros in the outer product.
425  int num_nonzeros = 1;
426  for (int i = 1; i < product.size(); ++i) {
427    if (product[i].row != product[i - 1].row ||
428        product[i].col != product[i - 1].col) {
429      ++num_nonzeros;
430    }
431  }
432
433  CompressedRowSparseMatrix* matrix =
434      new CompressedRowSparseMatrix(num_rows, num_cols, num_nonzeros);
435
436  int* crsm_rows = matrix->mutable_rows();
437  std::fill(crsm_rows, crsm_rows + num_rows + 1, 0);
438  int* crsm_cols = matrix->mutable_cols();
439  std::fill(crsm_cols, crsm_cols + num_nonzeros, 0);
440
441  CHECK_NOTNULL(program)->clear();
442  program->resize(product.size());
443
444  // Iterate over the sorted product terms. This means each row is
445  // filled one at a time, and we are able to assign a position in the
446  // values array to each term.
447  //
448  // If terms repeat, i.e., they contribute to the same entry in the
449  // result matrix), then they do not affect the sparsity structure of
450  // the result matrix.
451  int nnz = 0;
452  crsm_cols[0] = product[0].col;
453  crsm_rows[product[0].row + 1]++;
454  (*program)[product[0].index] = nnz;
455  for (int i = 1; i < product.size(); ++i) {
456    const ProductTerm& previous = product[i - 1];
457    const ProductTerm& current = product[i];
458
459    // Sparsity structure is updated only if the term is not a repeat.
460    if (previous.row != current.row || previous.col != current.col) {
461      crsm_cols[++nnz] = current.col;
462      crsm_rows[current.row + 1]++;
463    }
464
465    // All terms get assigned the position in the values array where
466    // their value is accumulated.
467    (*program)[current.index] = nnz;
468  }
469
470  for (int i = 1; i < num_rows + 1; ++i) {
471    crsm_rows[i] += crsm_rows[i - 1];
472  }
473
474  return matrix;
475}
476
477}  // namespace
478
479CompressedRowSparseMatrix*
480CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
481      const CompressedRowSparseMatrix& m,
482      vector<int>* program) {
483  CHECK_NOTNULL(program)->clear();
484  CHECK_GT(m.num_nonzeros(), 0) << "Congratulations, "
485                                << "you found a bug in Ceres. Please report it.";
486
487  vector<ProductTerm> product;
488  const vector<int>& row_blocks = m.row_blocks();
489  int row_block_begin = 0;
490  // Iterate over row blocks
491  for (int row_block = 0; row_block < row_blocks.size(); ++row_block) {
492    const int row_block_end = row_block_begin + row_blocks[row_block];
493    // Compute the outer product terms for just one row per row block.
494    const int r = row_block_begin;
495    // Compute the lower triangular part of the product.
496    for (int idx1 = m.rows()[r]; idx1 < m.rows()[r + 1]; ++idx1) {
497      for (int idx2 = m.rows()[r]; idx2 <= idx1; ++idx2) {
498        product.push_back(ProductTerm(m.cols()[idx1], m.cols()[idx2], product.size()));
499      }
500    }
501    row_block_begin = row_block_end;
502  }
503  CHECK_EQ(row_block_begin, m.num_rows());
504  sort(product.begin(), product.end());
505  return CompressAndFillProgram(m.num_cols(), m.num_cols(), product, program);
506}
507
508void CompressedRowSparseMatrix::ComputeOuterProduct(
509    const CompressedRowSparseMatrix& m,
510    const vector<int>& program,
511    CompressedRowSparseMatrix* result) {
512  result->SetZero();
513  double* values = result->mutable_values();
514  const vector<int>& row_blocks = m.row_blocks();
515
516  int cursor = 0;
517  int row_block_begin = 0;
518  const double* m_values = m.values();
519  const int* m_rows = m.rows();
520  // Iterate over row blocks.
521  for (int row_block = 0; row_block < row_blocks.size(); ++row_block) {
522    const int row_block_end = row_block_begin + row_blocks[row_block];
523    const int saved_cursor = cursor;
524    for (int r = row_block_begin; r < row_block_end; ++r) {
525      // Reuse the program segment for each row in this row block.
526      cursor = saved_cursor;
527      const int row_begin = m_rows[r];
528      const int row_end = m_rows[r + 1];
529      for (int idx1 = row_begin; idx1 < row_end; ++idx1) {
530        const double v1 =  m_values[idx1];
531        for (int idx2 = row_begin; idx2 <= idx1; ++idx2, ++cursor) {
532          values[program[cursor]] += v1 * m_values[idx2];
533        }
534      }
535    }
536    row_block_begin = row_block_end;
537  }
538
539  CHECK_EQ(row_block_begin, m.num_rows());
540  CHECK_EQ(cursor, program.size());
541}
542
543}  // namespace internal
544}  // namespace ceres
545