Add BlockRandomAccessSparseMatrix::SymmetricRightMultiply.

Change-Id: Ib06a22a209b4c985ba218162dfb6bf46bd93169e
diff --git a/internal/ceres/block_random_access_sparse_matrix.cc b/internal/ceres/block_random_access_sparse_matrix.cc
index f789436..9da16a4 100644
--- a/internal/ceres/block_random_access_sparse_matrix.cc
+++ b/internal/ceres/block_random_access_sparse_matrix.cc
@@ -54,9 +54,9 @@
   // Build the row/column layout vector and count the number of scalar
   // rows/columns.
   int num_cols = 0;
-  vector<int> col_layout;
+  block_positions_.reserve(blocks_.size());
   for (int i = 0; i < blocks_.size(); ++i) {
-    col_layout.push_back(num_cols);
+    block_positions_.push_back(num_cols);
     num_cols += blocks_[i];
   }
 
@@ -105,8 +105,8 @@
         layout_[IntPairToLong(row_block_id, col_block_id)]->values - values;
     for (int r = 0; r < row_block_size; ++r) {
       for (int c = 0; c < col_block_size; ++c, ++pos) {
-          rows[pos] = col_layout[row_block_id] + r;
-          cols[pos] = col_layout[col_block_id] + c;
+          rows[pos] = block_positions_[row_block_id] + r;
+          cols[pos] = block_positions_[col_block_id] + c;
           values[pos] = 1.0;
           DCHECK_LT(rows[pos], tsm_->num_rows());
           DCHECK_LT(cols[pos], tsm_->num_rows());
@@ -154,5 +154,37 @@
   }
 }
 
+void BlockRandomAccessSparseMatrix::SymmetricRightMultiply(const double* x,
+                                                           double* y) const {
+  for (LayoutType::const_iterator it = layout_.begin();
+       it != layout_.end();
+       ++it) {
+    int row;
+    int col;
+    LongToIntPair(it->first, &row, &col);
+
+    const int row_block_size = blocks_[row];
+    const int row_block_pos = block_positions_[row];
+    const int col_block_size = blocks_[col];
+    const int col_block_pos = block_positions_[col];
+
+    MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
+        it->second->values, row_block_size, col_block_size,
+        x + col_block_pos,
+        y + row_block_pos);
+
+    // Since the matrix is symmetric, but only the upper triangular
+    // part is stored, if the block being accessed is not a diagonal
+    // block, then use the same block to do the corresponding lower
+    // triangular multiply also.
+    if (row != col) {
+      MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
+          it->second->values, row_block_size, col_block_size,
+          x + row_block_pos,
+          y + col_block_pos);
+    }
+  }
+}
+
 }  // namespace internal
 }  // namespace ceres
diff --git a/internal/ceres/block_random_access_sparse_matrix.h b/internal/ceres/block_random_access_sparse_matrix.h
index 27b1029..a4f89d8 100644
--- a/internal/ceres/block_random_access_sparse_matrix.h
+++ b/internal/ceres/block_random_access_sparse_matrix.h
@@ -43,6 +43,7 @@
 #include "ceres/internal/port.h"
 #include "ceres/internal/scoped_ptr.h"
 #include "ceres/types.h"
+#include "ceres/small_blas.h"
 
 namespace ceres {
 namespace internal {
@@ -75,6 +76,12 @@
   // locked.
   virtual void SetZero();
 
+  // Assume that the matrix is symmetric and only one half of the
+  // matrix is stored.
+  //
+  // y += S * x
+  void SymmetricRightMultiply(const double* x, double* y) const;
+
   // Since the matrix is square, num_rows() == num_cols().
   virtual int num_rows() const { return tsm_->num_rows(); }
   virtual int num_cols() const { return tsm_->num_cols(); }
@@ -84,13 +91,20 @@
   TripletSparseMatrix* mutable_matrix() { return tsm_.get(); }
 
  private:
-  int64 IntPairToLong(int a, int b) {
-    return a * kMaxRowBlocks + b;
+  int64 IntPairToLong(int row, int col) const {
+    return row * kMaxRowBlocks + col;
+  }
+
+  void LongToIntPair(int64 index, int* row, int* col) const {
+    *row = index / kMaxRowBlocks;
+    *col = index % kMaxRowBlocks;
   }
 
   const int64 kMaxRowBlocks;
+
   // row/column block sizes.
   const vector<int> blocks_;
+  vector<int> block_positions_;
 
   // A mapping from <row_block_id, col_block_id> to the position in
   // the values array of tsm_ where the block is stored.
diff --git a/internal/ceres/block_random_access_sparse_matrix_test.cc b/internal/ceres/block_random_access_sparse_matrix_test.cc
index b2396a1..10a4190 100644
--- a/internal/ceres/block_random_access_sparse_matrix_test.cc
+++ b/internal/ceres/block_random_access_sparse_matrix_test.cc
@@ -56,7 +56,7 @@
   block_pairs.insert(make_pair(1, 2));
   num_nonzeros += blocks[1] * blocks[2];
 
-  block_pairs.insert(make_pair(2, 0));
+  block_pairs.insert(make_pair(0, 2));
   num_nonzeros += blocks[2] * blocks[0];
 
   BlockRandomAccessSparseMatrix m(blocks, block_pairs);
@@ -97,26 +97,37 @@
 
   double kTolerance = 1e-14;
 
-  // (0,0)
+  // (0, 0)
   EXPECT_NEAR((dense.block(0, 0, 3, 3) - Matrix::Ones(3, 3)).norm(),
               0.0,
               kTolerance);
-  // (1,1)
+  // (1, 1)
   EXPECT_NEAR((dense.block(3, 3, 4, 4) - 2 * 2 * Matrix::Ones(4, 4)).norm(),
               0.0,
               kTolerance);
-  // (1,2)
+  // (1, 2)
   EXPECT_NEAR((dense.block(3, 3 + 4, 4, 5) - 2 * 3 * Matrix::Ones(4, 5)).norm(),
               0.0,
               kTolerance);
-  // (2,0)
-  EXPECT_NEAR((dense.block(3 + 4, 0, 5, 3) - 3 * 1 * Matrix::Ones(5, 3)).norm(),
+  // (0, 2)
+  EXPECT_NEAR((dense.block(0, 3 + 4, 3, 5) - 3 * 1 * Matrix::Ones(3, 5)).norm(),
               0.0,
               kTolerance);
 
   // There is nothing else in the matrix besides these four blocks.
   EXPECT_NEAR(dense.norm(), sqrt(9. + 16. * 16. + 36. * 20. + 9. * 15.),
               kTolerance);
+
+  Vector x = Vector::Ones(dense.rows());
+  Vector actual_y = Vector::Zero(dense.rows());
+  Vector expected_y = Vector::Zero(dense.rows());
+
+  expected_y += dense.selfadjointView<Eigen::Upper>() * x;
+  m.SymmetricRightMultiply(x.data(), actual_y.data());
+  EXPECT_NEAR((expected_y - actual_y).norm(), 0.0, kTolerance)
+      << "actual: " << actual_y.transpose() << "\n"
+      << "expected: " << expected_y.transpose()
+      << "matrix: \n " << dense;
 }
 
 // IntPairToLong is private, thus this fixture is needed to access and
@@ -131,19 +142,38 @@
     m_.reset(new BlockRandomAccessSparseMatrix(blocks, block_pairs));
   }
 
-  void CheckIntPair(int a, int b) {
+  void CheckIntPairToLong(int a, int b) {
     int64 value = m_->IntPairToLong(a, b);
     EXPECT_GT(value, 0) << "Overflow a = " << a << " b = " << b;
     EXPECT_GT(value, a) << "Overflow a = " << a << " b = " << b;
     EXPECT_GT(value, b) << "Overflow a = " << a << " b = " << b;
   }
 
+  void CheckLongToIntPair() {
+    uint64 max_rows =  m_->kMaxRowBlocks;
+    for (int row = max_rows - 10; row < max_rows; ++row) {
+      for (int col = 0; col < 10; ++col) {
+        int row_computed;
+        int col_computed;
+        m_->LongToIntPair(m_->IntPairToLong(row, col),
+                          &row_computed,
+                          &col_computed);
+        EXPECT_EQ(row, row_computed);
+        EXPECT_EQ(col, col_computed);
+      }
+    }
+  }
+
  private:
   scoped_ptr<BlockRandomAccessSparseMatrix> m_;
 };
 
 TEST_F(BlockRandomAccessSparseMatrixTest, IntPairToLongOverflow) {
-  CheckIntPair(numeric_limits<int>::max(), numeric_limits<int>::max());
+  CheckIntPairToLong(numeric_limits<int>::max(), numeric_limits<int>::max());
+}
+
+TEST_F(BlockRandomAccessSparseMatrixTest, LongToIntPair) {
+  CheckLongToIntPair();
 }
 
 }  // namespace internal